diff --git a/.changelog/133.txt b/.changelog/133.txt new file mode 100644 index 00000000..b9cc546b --- /dev/null +++ b/.changelog/133.txt @@ -0,0 +1,3 @@ +```release-note:improvement +vault-secrets: adding list gateway pools gateways command to vault-secrets gateway-pools +``` diff --git a/internal/commands/vaultsecrets/gatewaypools/create.go b/internal/commands/vaultsecrets/gatewaypools/create.go index ead1043c..71597b68 100644 --- a/internal/commands/vaultsecrets/gatewaypools/create.go +++ b/internal/commands/vaultsecrets/gatewaypools/create.go @@ -115,8 +115,16 @@ func NewCmdCreate(ctx *cmd.Context, runF func(*CreateOpts) error) *cmd.Command { return cmd } -func extraFields(showOauth bool) []format.Field { - extraFields := []format.Field{ +func createFields(showOauth bool) []format.Field { + fields := []format.Field{ + { + Name: "GatewayPool Name", + ValueFormat: "{{ .GatewayPool.Name }}", + }, + { + Name: "Description", + ValueFormat: "{{ .GatewayPool.Description }}", + }, { Name: "Resource Name", ValueFormat: "{{ .GatewayPool.ResourceName }}", @@ -124,7 +132,7 @@ func extraFields(showOauth bool) []format.Field { } if showOauth { - extraFields = append(extraFields, []format.Field{ + fields = append(fields, []format.Field{ { Name: "Client ID", ValueFormat: "{{ .Oauth.ClientID }}", @@ -135,7 +143,7 @@ func extraFields(showOauth bool) []format.Field { }, }...) } - return extraFields + return fields } func createRun(opts *CreateOpts) error { @@ -185,14 +193,15 @@ func createRun(opts *CreateOpts) error { } // Display Oauth in the output if explicitly asked for it - displayerOpts := &gatewayPoolWithIntegrations{ + displayerOpts := &gatewayPoolWithOauth{ GatewayPool: resp.Payload.GatewayPool, } if opts.ShowClientSecret { displayerOpts.Oauth = oauth } - return opts.Output.Display(newDisplayer(true, displayerOpts).SetDefaultFormat(format.Pretty).AddExtraFields(extraFields(opts.ShowClientSecret)...)) + d := newDisplayer(true).GatewayPoolsWithOauth(displayerOpts) + return opts.Output.Display(d.SetDefaultFormat(format.Pretty).AddFields(createFields(opts.ShowClientSecret)...)) } type gatewayCreds struct { diff --git a/internal/commands/vaultsecrets/gatewaypools/create_test.go b/internal/commands/vaultsecrets/gatewaypools/create_test.go index c0831224..93d546bc 100644 --- a/internal/commands/vaultsecrets/gatewaypools/create_test.go +++ b/internal/commands/vaultsecrets/gatewaypools/create_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package gatewaypools import ( diff --git a/internal/commands/vaultsecrets/gatewaypools/displayer.go b/internal/commands/vaultsecrets/gatewaypools/displayer.go index 1f786148..4e9e8936 100644 --- a/internal/commands/vaultsecrets/gatewaypools/displayer.go +++ b/internal/commands/vaultsecrets/gatewaypools/displayer.go @@ -11,28 +11,58 @@ import ( type gatewayPoolWithIntegrations struct { GatewayPool *preview_models.Secrets20231128GatewayPool - Oauth *auth.OauthConfig Integrations []string } -type displayer struct { - gatewayPools []*gatewayPoolWithIntegrations +type gatewayPoolWithOauth struct { + GatewayPool *preview_models.Secrets20231128GatewayPool + Oauth *auth.OauthConfig +} - extraFields []format.Field - format format.Format - single bool +type displayer struct { + gatewayPools []*preview_models.Secrets20231128GatewayPool + gatewayPoolsWithIntegrations *gatewayPoolWithIntegrations + gatewayPoolWithOauth *gatewayPoolWithOauth + gateways []*preview_models.Secrets20231128Gateway + + fields []format.Field + format format.Format + single bool } -func newDisplayer(single bool, gatewayPools ...*gatewayPoolWithIntegrations) *displayer { +func newDisplayer(single bool) *displayer { return &displayer{ - gatewayPools: gatewayPools, - single: single, - format: format.Table, + single: single, + format: format.Table, + } +} + +func (d *displayer) GatewayPools(gatewayPools ...*preview_models.Secrets20231128GatewayPool) *displayer { + d.gatewayPools = gatewayPools + return d +} + +func (d *displayer) GatewayPoolWithIntegrations(gatewayPool *preview_models.Secrets20231128GatewayPool, integrations ...string) *displayer { + d.gatewayPoolsWithIntegrations = &gatewayPoolWithIntegrations{ + GatewayPool: gatewayPool, + Integrations: integrations, } + + return d +} + +func (d *displayer) Gateways(gateways ...*preview_models.Secrets20231128Gateway) *displayer { + d.gateways = gateways + return d +} + +func (d *displayer) GatewayPoolsWithOauth(gpo *gatewayPoolWithOauth) *displayer { + d.gatewayPoolWithOauth = gpo + return d } -func (d *displayer) AddExtraFields(fields ...format.Field) *displayer { - d.extraFields = append(d.extraFields, fields...) +func (d *displayer) AddFields(fields ...format.Field) *displayer { + d.fields = append(d.fields, fields...) return d } @@ -45,45 +75,45 @@ func (d *displayer) DefaultFormat() format.Format { return d.format } -func (d *displayer) Payload() any { - if d.gatewayPools != nil { - if d.single { - if len(d.gatewayPools) != 1 { - return nil - } - - return d.gatewayPools[0] - } - - return d.gatewayPools - } - +func (d *displayer) previewGatewayPools() any { if d.single { if len(d.gatewayPools) != 1 { return nil } - return d.gatewayPools[0] } return d.gatewayPools } -func (d *displayer) FieldTemplates() []format.Field { - fields := []format.Field{ - { - Name: "GatewayPool Name", - ValueFormat: "{{ .GatewayPool.Name }}", - }, - { - Name: "Description", - ValueFormat: "{{ .GatewayPool.Description }}", - }, +func (d *displayer) previewGateways() any { + if d.single { + if len(d.gateways) != 1 { + return nil + } + return d.gateways[0] } - if d.extraFields != nil { - fields = append(fields, d.extraFields...) + return d.gateways +} + +func (d *displayer) Payload() any { + if d.gatewayPools != nil { + return d.previewGatewayPools() + } + if d.gatewayPoolsWithIntegrations != nil { + return d.gatewayPoolsWithIntegrations + } + if d.gateways != nil { + return d.previewGateways() } + if d.gatewayPoolWithOauth != nil { + return d.gatewayPoolWithOauth + } + + return nil +} - return fields +func (d *displayer) FieldTemplates() []format.Field { + return d.fields } diff --git a/internal/commands/vaultsecrets/gatewaypools/gateway_pools.go b/internal/commands/vaultsecrets/gatewaypools/gateway_pools.go index 94665f6f..e94cde6f 100644 --- a/internal/commands/vaultsecrets/gatewaypools/gateway_pools.go +++ b/internal/commands/vaultsecrets/gatewaypools/gateway_pools.go @@ -26,5 +26,6 @@ func NewCmdGatewayPools(ctx *cmd.Context) *cmd.Command { command.AddChild(NewCmdUpdate(ctx, nil)) command.AddChild(NewCmdDelete(ctx, nil)) command.AddChild(NewCmdRead(ctx, nil)) + command.AddChild(NewCmdListGatewayPoolsGateway(ctx, nil)) return command } diff --git a/internal/commands/vaultsecrets/gatewaypools/list.go b/internal/commands/vaultsecrets/gatewaypools/list.go index 39646de9..65af7f8d 100644 --- a/internal/commands/vaultsecrets/gatewaypools/list.go +++ b/internal/commands/vaultsecrets/gatewaypools/list.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package gatewaypools import ( @@ -54,6 +57,19 @@ func NewCmdList(ctx *cmd.Context, runF func(*ListOpts) error) *cmd.Command { return cmd } +func listFields() []format.Field { + return []format.Field{ + { + Name: "GatewayPool Name", + ValueFormat: "{{ .Name }}", + }, + { + Name: "Description", + ValueFormat: "{{ .Description }}", + }, + } +} + func listRun(opts *ListOpts) error { params := &preview_secret_service.ListGatewayPoolsParams{ Context: opts.Ctx, @@ -65,15 +81,6 @@ func listRun(opts *ListOpts) error { if err != nil { return fmt.Errorf("failed to list gateway pools: %w", err) } - if resp.Payload == nil || resp.Payload.GatewayPools == nil { - return fmt.Errorf("failed to list gateway pools: empty response") - } - gws := make([]*gatewayPoolWithIntegrations, 0, len(resp.Payload.GatewayPools)) - for _, gp := range resp.Payload.GatewayPools { - gws = append(gws, &gatewayPoolWithIntegrations{ - GatewayPool: gp, - }) - } - return opts.Output.Display(newDisplayer(false, gws...)) + return opts.Output.Display(newDisplayer(false).GatewayPools(resp.Payload.GatewayPools...).AddFields(listFields()...)) } diff --git a/internal/commands/vaultsecrets/gatewaypools/list_gateway_pools_gateways.go b/internal/commands/vaultsecrets/gatewaypools/list_gateway_pools_gateways.go new file mode 100644 index 00000000..faf46b81 --- /dev/null +++ b/internal/commands/vaultsecrets/gatewaypools/list_gateway_pools_gateways.go @@ -0,0 +1,141 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package gatewaypools + +import ( + "context" + "fmt" + + preview_secret_service "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/preview/2023-11-28/client/secret_service" + "github.com/hashicorp/hcp/internal/pkg/cmd" + "github.com/hashicorp/hcp/internal/pkg/flagvalue" + "github.com/hashicorp/hcp/internal/pkg/format" + "github.com/hashicorp/hcp/internal/pkg/heredoc" + "github.com/hashicorp/hcp/internal/pkg/iostreams" + "github.com/hashicorp/hcp/internal/pkg/profile" +) + +type ListGatewaysOpts struct { + Ctx context.Context + Profile *profile.Profile + Output *format.Outputter + IO iostreams.IOStreams + + PreviewClient preview_secret_service.ClientService + GatewayPoolName string + ShowAll bool +} + +func NewCmdListGatewayPoolsGateway(ctx *cmd.Context, runF func(*ListGatewaysOpts) error) *cmd.Command { + opts := &ListGatewaysOpts{ + Ctx: ctx.ShutdownCtx, + Profile: ctx.Profile, + IO: ctx.IO, + Output: ctx.Output, + PreviewClient: preview_secret_service.New(ctx.HCP, nil), + } + + cmd := &cmd.Command{ + Name: "list-gateways", + ShortHelp: "List Vault Secrets gateway pools gateways.", + LongHelp: heredoc.New(ctx.IO).Must(` + The {{ template "mdCodeOrBold" "hcp vault-secrets gateway-pools list-gateways" }} command lists all Vault Secrets gateway pools gateways. + `), + Examples: []cmd.Example{ + { + Preamble: `List gateway-pools gateways:`, + Command: heredoc.New(ctx.IO, heredoc.WithPreserveNewlines()).Must(` + $ hcp vault-secrets gateway-pools list-gateways company-tunnel + `), + }, + }, + Flags: cmd.Flags{ + Local: []*cmd.Flag{ + { + Name: "show-all", + Shorthand: "a", + Description: "Show all fields.", + IsBooleanFlag: true, + Value: flagvalue.Simple(false, &opts.ShowAll), + Required: false, + }, + }, + }, + Args: cmd.PositionalArguments{ + Args: []cmd.PositionalArgument{ + { + Name: "NAME", + Documentation: "The name of the gateway pool to list its gateways.", + }, + }, + }, + RunF: func(c *cmd.Command, args []string) error { + opts.GatewayPoolName = args[0] + + if runF != nil { + return runF(opts) + } + return listGatewaysRun(opts) + }, + } + return cmd +} + +func listGatewaysRun(opts *ListGatewaysOpts) error { + params := &preview_secret_service.ListGatewayPoolGatewaysParams{ + Context: opts.Ctx, + ProjectID: opts.Profile.ProjectID, + OrganizationID: opts.Profile.OrganizationID, + GatewayPoolName: opts.GatewayPoolName, + } + + resp, err := opts.PreviewClient.ListGatewayPoolGateways(params, nil) + if err != nil { + return fmt.Errorf("failed to list gateway pools: %w", err) + } + + d := newDisplayer(false).Gateways(resp.Payload.Gateways...) + + return opts.Output.Display(d.AddFields(listGatewaysFields(opts.ShowAll)...)) +} + +func listGatewaysFields(showAll bool) []format.Field { + fields := []format.Field{ + { + Name: "Gateway ID", + ValueFormat: "{{ .ID }}", + }, + { + Name: "Gateway Status", + ValueFormat: "{{ .Status }}", + }, + { + Name: "Gateway Version", + ValueFormat: "{{ .Version }}", + }, + } + if showAll { + ComplementaryFields := []format.Field{ + { + Name: "Gateway Hostname", + ValueFormat: "{{ .Hostname }}", + }, + { + Name: "Gateway Os", + ValueFormat: "{{ .Os }}", + }, + { + Name: "Gateway Metadata", + ValueFormat: "{{ .Metadata }}", + }, + { + Name: "Gateway Status Message", + ValueFormat: "{{ .StatusMessage }}", + }, + } + fields = append(fields, ComplementaryFields...) + } + + return fields +} diff --git a/internal/commands/vaultsecrets/gatewaypools/read.go b/internal/commands/vaultsecrets/gatewaypools/read.go index 48b9e138..951ea3fd 100644 --- a/internal/commands/vaultsecrets/gatewaypools/read.go +++ b/internal/commands/vaultsecrets/gatewaypools/read.go @@ -70,6 +70,23 @@ func NewCmdRead(ctx *cmd.Context, runF func(*ReadOpts) error) *cmd.Command { return cmd } +func readFields() []format.Field { + return []format.Field{ + { + Name: "GatewayPool Name", + ValueFormat: "{{ .GatewayPool.Name }}", + }, + { + Name: "Description", + ValueFormat: "{{ .GatewayPool.Description }}", + }, + { + Name: "Integrations", + ValueFormat: "{{ .Integrations }}", + }, + } +} + func readRun(opts *ReadOpts) error { resp, err := opts.PreviewClient.GetGatewayPool(&preview_secret_service.GetGatewayPoolParams{ Context: opts.Ctx, @@ -96,17 +113,6 @@ func readRun(opts *ReadOpts) error { return fmt.Errorf("failed to list gateway pool integrations: %w", err) } - gwIntegrations := &gatewayPoolWithIntegrations{ - GatewayPool: resp.Payload.GatewayPool, - Integrations: integList.Payload.Integrations, - } - - return opts.Output.Display( - newDisplayer(true, gwIntegrations).AddExtraFields( - format.Field{ - Name: "Integrations", - ValueFormat: "{{ .Integrations }}", - }, - ), - ) + d := newDisplayer(true).GatewayPoolWithIntegrations(resp.Payload.GatewayPool, integList.Payload.Integrations...) + return opts.Output.Display(d.AddFields(readFields()...)) }