Skip to content

Commit

Permalink
store credentials in file for gateway create (#135)
Browse files Browse the repository at this point in the history
* store credentials in file for gateway create

* CL

* fix linter issue

* feedback
  • Loading branch information
hghaf099 authored Jul 25, 2024
1 parent 99d6502 commit 40546ea
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 23 deletions.
3 changes: 3 additions & 0 deletions .changelog/135.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
vault-secrets: storing credentials and config files for gateway create
```
138 changes: 133 additions & 5 deletions internal/commands/vaultsecrets/gatewaypools/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,17 @@ package gatewaypools

import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"

"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/hcl/v2/hclwrite"
"github.com/hashicorp/hcp-sdk-go/auth"
preview_secret_service "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/preview/2023-11-28/client/secret_service"
"github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/preview/2023-11-28/models"
"github.com/hashicorp/hcp-sdk-go/config/files"
"github.com/hashicorp/hcp/internal/pkg/cmd"
"github.com/hashicorp/hcp/internal/pkg/flagvalue"
"github.com/hashicorp/hcp/internal/pkg/format"
Expand All @@ -17,15 +24,22 @@ import (
"github.com/hashicorp/hcp/internal/pkg/profile"
)

const (
CredsFilePath = "./creds.json"
ConfigFilePath = "config.hcl"
)

type CreateOpts struct {
Ctx context.Context
Profile *profile.Profile
Output *format.Outputter
IO iostreams.IOStreams

GatewayPoolName string
Description string
PreviewClient preview_secret_service.ClientService
GatewayPoolName string
Description string
OutDirPath string
ShowClientSecret bool
PreviewClient preview_secret_service.ClientService
}

func NewCmdCreate(ctx *cmd.Context, runF func(*CreateOpts) error) *cmd.Command {
Expand Down Expand Up @@ -69,6 +83,23 @@ func NewCmdCreate(ctx *cmd.Context, runF func(*CreateOpts) error) *cmd.Command {
Value: flagvalue.Simple("", &opts.Description),
Required: false,
},
{
Name: "output-dir",
DisplayValue: "OUTPUT_DIR_PATH",
Shorthand: "o",
Description: "Directory path where the gateway credentials file and config file should be written.",
Value: flagvalue.Simple("", &opts.OutDirPath),
Required: false,
},
{
Name: "show-client-secret",
DisplayValue: "SHOW_CLIENT_SECRET",
IsBooleanFlag: true,
Description: "Show the client secret in the output. If this is not set, OUTPUT_DIR_PATH should be set.",
Shorthand: "s",
Value: flagvalue.Simple(false, &opts.ShowClientSecret),
Required: false,
},
},
},
RunF: func(c *cmd.Command, args []string) error {
Expand All @@ -84,7 +115,38 @@ func NewCmdCreate(ctx *cmd.Context, runF func(*CreateOpts) error) *cmd.Command {
return cmd
}

func extraFields(showOauth bool) []format.Field {
extraFields := []format.Field{
{
Name: "Resource Name",
ValueFormat: "{{ .GatewayPool.ResourceName }}",
},
}

if showOauth {
extraFields = append(extraFields, []format.Field{
{
Name: "Client ID",
ValueFormat: "{{ .Oauth.ClientID }}",
},
{
Name: "Client Secret",
ValueFormat: "{{ .Oauth.ClientSecret }}",
},
}...)
}
return extraFields
}

func createRun(opts *CreateOpts) error {
if !opts.ShowClientSecret && opts.OutDirPath == "" {
return fmt.Errorf("either show-client-secret or output-dir should be set")
}
if opts.OutDirPath != "" {
if err := os.Mkdir(opts.OutDirPath, 0o700); err != nil {
return fmt.Errorf("failed to create the output directory: %w", err)
}
}
resp, err := opts.PreviewClient.CreateGatewayPool(&preview_secret_service.CreateGatewayPoolParams{
Context: opts.Ctx,
OrganizationID: opts.Profile.OrganizationID,
Expand All @@ -99,7 +161,73 @@ func createRun(opts *CreateOpts) error {
return fmt.Errorf("failed to create gateway pool: %w", err)
}

return opts.Output.Display(newDisplayer(true, false, &gatewayPoolWithIntegrations{
oauth := &auth.OauthConfig{
ClientID: resp.Payload.ClientID,
ClientSecret: resp.Payload.ClientSecret,
}
if opts.OutDirPath != "" {
creds := &gatewayCreds{
ProjectID: resp.Payload.GatewayPool.ProjectID,
Scheme: auth.CredentialFileSchemeServicePrincipal,
Oauth: oauth,
}
if err := writeGatewayCredentialFile(filepath.Join(opts.OutDirPath, CredsFilePath), creds); err != nil {
return fmt.Errorf("failed to write the gateway credential file: %w", err)
}

c := &cloud{
CredFile: CredsFilePath,
ResourceName: resp.Payload.GatewayPool.ResourceName,
}
if err := WriteConfig(filepath.Join(opts.OutDirPath, ConfigFilePath), &config{Cloud: c}); err != nil {
return fmt.Errorf("failed to write the gateway config file: %w", err)
}
}

// Display Oauth in the output if explicitly asked for it
displayerOpts := &gatewayPoolWithIntegrations{
GatewayPool: resp.Payload.GatewayPool,
}))
}
if opts.ShowClientSecret {
displayerOpts.Oauth = oauth
}

return opts.Output.Display(newDisplayer(true, displayerOpts).SetDefaultFormat(format.Pretty).AddExtraFields(extraFields(opts.ShowClientSecret)...))
}

type gatewayCreds struct {
ProjectID string `json:"project_id,omitempty"`
ResourceName string `json:"resource_name,omitempty"`
// Scheme is the authentication scheme which is service_principal_creds
Scheme string `json:"scheme,omitempty"`

Oauth *auth.OauthConfig `json:"oauth,omitempty"`
}

// writeGatewayCredentialFile writes the given credential file to the path.
func writeGatewayCredentialFile(path string, cf *gatewayCreds) error {
data, err := json.MarshalIndent(cf, "", " ")
if err != nil {
return err
}

return os.WriteFile(path, data, files.FileMode)
}

type cloud struct {
// CredFile is the path to the credential json file.
CredFile string `hcl:"cred_file"`
// ResourceName is the resource name of the gateway pool.
ResourceName string `hcl:"resource_name"`
}

type config struct {
Cloud *cloud `hcl:"cloud,block"`
}

// WriteConfig writes the config to disk.
func WriteConfig(path string, c *config) error {
f := hclwrite.NewEmptyFile()
gohcl.EncodeIntoBody(c, f.Body())
return os.WriteFile(path, f.Bytes(), 0o700)
}
38 changes: 22 additions & 16 deletions internal/commands/vaultsecrets/gatewaypools/displayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,45 @@
package gatewaypools

import (
"github.com/hashicorp/hcp-sdk-go/auth"
preview_models "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/preview/2023-11-28/models"
"github.com/hashicorp/hcp/internal/pkg/format"
)

type gatewayPoolWithIntegrations struct {
GatewayPool *preview_models.Secrets20231128GatewayPool
Oauth *auth.OauthConfig
Integrations []string
}

type displayer struct {
gatewayPools []*gatewayPoolWithIntegrations

// showIntegrations is used to determine if the integrations should be shown
// This is used only for the read command where the integrations associated with
// the gateway pool is also displayed
showIntegrations bool

single bool
extraFields []format.Field
format format.Format
single bool
}

func newDisplayer(single, showIntegrations bool, gatewayPools ...*gatewayPoolWithIntegrations) *displayer {
func newDisplayer(single bool, gatewayPools ...*gatewayPoolWithIntegrations) *displayer {
return &displayer{
gatewayPools: gatewayPools,
single: single,
showIntegrations: showIntegrations,
gatewayPools: gatewayPools,
single: single,
format: format.Table,
}
}

func (d *displayer) AddExtraFields(fields ...format.Field) *displayer {
d.extraFields = append(d.extraFields, fields...)
return d
}

func (d *displayer) SetDefaultFormat(f format.Format) *displayer {
d.format = f
return d
}

func (d *displayer) DefaultFormat() format.Format {
return format.Table
return d.format
}

func (d *displayer) Payload() any {
Expand Down Expand Up @@ -72,11 +81,8 @@ func (d *displayer) FieldTemplates() []format.Field {
},
}

if d.showIntegrations {
fields = append(fields, format.Field{
Name: "Integrations",
ValueFormat: "{{ .Integrations }}",
})
if d.extraFields != nil {
fields = append(fields, d.extraFields...)
}

return fields
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/vaultsecrets/gatewaypools/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,5 @@ func listRun(opts *ListOpts) error {
GatewayPool: gp,
})
}
return opts.Output.Display(newDisplayer(false, false, gws...))
return opts.Output.Display(newDisplayer(false, gws...))
}
9 changes: 8 additions & 1 deletion internal/commands/vaultsecrets/gatewaypools/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,12 @@ func readRun(opts *ReadOpts) error {
Integrations: integList.Payload.Integrations,
}

return opts.Output.Display(newDisplayer(true, true, gwIntegrations))
return opts.Output.Display(
newDisplayer(true, gwIntegrations).AddExtraFields(
format.Field{
Name: "Integrations",
ValueFormat: "{{ .Integrations }}",
},
),
)
}

0 comments on commit 40546ea

Please sign in to comment.