diff --git a/internal/commands/vaultsecrets/secrets/import.go b/internal/commands/vaultsecrets/secrets/import.go new file mode 100644 index 00000000..d8796fc2 --- /dev/null +++ b/internal/commands/vaultsecrets/secrets/import.go @@ -0,0 +1,81 @@ +package secrets + +import ( + "context" + "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/stable/2023-11-28/client/secret_service" + "github.com/hashicorp/hcp/internal/commands/vaultsecrets/secrets/appname" + "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" + "github.com/posener/complete" +) + +type ImportOpts struct { + Ctx context.Context + Profile *profile.Profile + IO iostreams.IOStreams + Output *format.Outputter + + AppName string + ConfigFilePath string + Client secret_service.ClientService +} + +func NewCmdImport(ctx *cmd.Context, runF func(*ImportOpts) error) *cmd.Command { + opts := &ImportOpts{ + Ctx: ctx.ShutdownCtx, + Profile: ctx.Profile, + IO: ctx.IO, + Output: ctx.Output, + Client: secret_service.New(ctx.HCP, nil), + } + + cmd := &cmd.Command{ + Name: "import", + ShortHelp: "Import new static secret.", + LongHelp: heredoc.New(ctx.IO).Must(` + The {{ template "mdCodeOrBold" "hcp vault-secrets secrets import" }} command imports static secrets and creates them under a Vault Secrets application. + The configuration for importing your static secrets will be read from the provided HCL config file. + `), + Examples: []cmd.Example{ + { + Preamble: `Import static secrets:`, + Command: heredoc.New(ctx.IO, heredoc.WithPreserveNewlines()).Must(` + $ hcp vault-secrets secrets import --config-file=--config-file=path/to/file/config.hcl + `), + }, + }, + Flags: cmd.Flags{ + Local: []*cmd.Flag{ + { + Name: "config-file", + DisplayValue: "CONFIG_FILE", + Description: "File path to read import config data.", + Value: flagvalue.Simple("", &opts.ConfigFilePath), + Required: true, + Autocomplete: complete.PredictOr( + complete.PredictFiles("*"), + complete.PredictSet("-"), + ), + }, + }, + }, + RunF: func(c *cmd.Command, args []string) error { + opts.AppName = appname.Get() + + if runF != nil { + return runF(opts) + } + return importRun(opts) + }, + } + + return cmd +} + +func importRun(opts *ImportOpts) error { + return nil +} diff --git a/internal/commands/vaultsecrets/secrets/import_test.go b/internal/commands/vaultsecrets/secrets/import_test.go new file mode 100644 index 00000000..9ede2c61 --- /dev/null +++ b/internal/commands/vaultsecrets/secrets/import_test.go @@ -0,0 +1,82 @@ +package secrets + +import ( + "context" + "github.com/go-openapi/runtime/client" + "github.com/hashicorp/hcp/internal/pkg/cmd" + "github.com/hashicorp/hcp/internal/pkg/format" + "github.com/hashicorp/hcp/internal/pkg/iostreams" + "github.com/hashicorp/hcp/internal/pkg/profile" + "github.com/stretchr/testify/require" + "testing" +) + +func TestNewCmdImport(t *testing.T) { + t.Parallel() + + testProfile := func(t *testing.T) *profile.Profile { + tp := profile.TestProfile(t).SetOrgID("123").SetProjectID("456") + return tp + } + + cases := []struct { + Name string + Args []string + Profile func(t *testing.T) *profile.Profile + Error string + Expect *ImportOpts + }{ + { + Name: "Good", + Profile: testProfile, + Args: []string{"--config-file", "path/to/file"}, + Expect: &ImportOpts{ + ConfigFilePath: "path/to/file", + }, + }, + { + Name: "Failed: No config file specified", + Profile: testProfile, + Expect: &ImportOpts{ + ConfigFilePath: "path/to/file", + }, + Error: "ERROR: missing required flag: --config-file=CONFIG_FILE", + }, + } + + for _, c := range cases { + c := c + t.Run(c.Name, func(t *testing.T) { + t.Parallel() + r := require.New(t) + + // Create a context. + io := iostreams.Test() + ctx := &cmd.Context{ + IO: io, + Profile: c.Profile(t), + Output: format.New(io), + HCP: &client.Runtime{}, + ShutdownCtx: context.Background(), + } + + var gotOpts *ImportOpts + importCmd := NewCmdImport(ctx, func(o *ImportOpts) error { + gotOpts = o + return nil + }) + importCmd.SetIO(io) + + code := importCmd.Run(c.Args) + if c.Error != "" { + r.NotZero(code) + r.Contains(io.Error.String(), c.Error) + return + } + + r.Zero(code, io.Error.String()) + r.NotNil(gotOpts) + r.Equal(c.Expect.ConfigFilePath, gotOpts.ConfigFilePath) + }) + } +} diff --git a/internal/commands/vaultsecrets/secrets/secrets.go b/internal/commands/vaultsecrets/secrets/secrets.go index e2d390e5..1f989735 100644 --- a/internal/commands/vaultsecrets/secrets/secrets.go +++ b/internal/commands/vaultsecrets/secrets/secrets.go @@ -50,6 +50,7 @@ func NewCmdSecrets(ctx *cmd.Context) *cmd.Command { cmd.AddChild(NewCmdOpen(ctx, nil)) cmd.AddChild(NewCmdRotate(ctx, nil)) cmd.AddChild(NewCmdUpdate(ctx, nil)) + cmd.AddChild(NewCmdImport(ctx, nil)) cmd.AddChild(versions.NewCmdVersions(ctx)) return cmd