Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions docs/stackit_secrets-manager_instance_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,21 @@ stackit secrets-manager instance create [flags]

Create a Secrets Manager instance with name "my-instance" and specify IP range which is allowed to access it
$ stackit secrets-manager instance create --name my-instance --acl 1.2.3.0/24

Create a Secrets Manager instance with name "my-instance" and configure KMS key options
$ stackit secrets-manager instance create --name my-instance --kms-key-id key-id --kms-keyring-id keyring-id --kms-key-version 1 --kms-service-account-email my-service-account-1234567@sa.stackit.cloud
```

### Options

```
--acl strings List of IP networks in CIDR notation which are allowed to access this instance (default [])
-h, --help Help for "stackit secrets-manager instance create"
-n, --name string Instance name
--acl strings List of IP networks in CIDR notation which are allowed to access this instance (default [])
-h, --help Help for "stackit secrets-manager instance create"
--kms-key-id string ID of the KMS key to use for encryption
--kms-key-version int Version of the KMS key
--kms-keyring-id string ID of the KMS key ring
--kms-service-account-email string Service account email for KMS access
-n, --name string Instance name
```

### Options inherited from parent commands
Expand Down
11 changes: 9 additions & 2 deletions docs/stackit_secrets-manager_instance_update.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,20 @@ stackit secrets-manager instance update INSTANCE_ID [flags]
```
Update the range of IPs allowed to access a Secrets Manager instance with ID "xxx"
$ stackit secrets-manager instance update xxx --acl 1.2.3.0/24

Update the KMS key settings of a Secrets Manager instance with ID "xxx"
$ stackit secrets-manager instance update xxx --kms-key-id key-id --kms-keyring-id keyring-id --kms-key-version 1 --kms-service-account-email my-service-account-1234567@sa.stackit.cloud
```

### Options

```
--acl strings List of IP networks in CIDR notation which are allowed to access this instance (default [])
-h, --help Help for "stackit secrets-manager instance update"
--acl strings List of IP networks in CIDR notation which are allowed to access this instance (default [])
-h, --help Help for "stackit secrets-manager instance update"
--kms-key-id string ID of the KMS key to use for encryption
--kms-key-version int Version of the KMS key
--kms-keyring-id string ID of the KMS key ring
--kms-service-account-email string Service account email for KMS access
```

### Options inherited from parent commands
Expand Down
45 changes: 40 additions & 5 deletions internal/cmd/secrets-manager/instance/create/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,23 @@ import (
const (
instanceNameFlag = "name"
aclFlag = "acl"

kmsKeyIdFlag = "kms-key-id"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please also adjust the tests of the create command 😅

kmsKeyringIdFlag = "kms-keyring-id"
kmsKeyVersionFlag = "kms-key-version"
kmsServiceAccountEmailFlag = "kms-service-account-email"
)

type inputModel struct {
*globalflags.GlobalFlagModel

InstanceName *string
Acls *[]string

KmsKeyId *string
KmsKeyringId *string
KmsKeyVersion *int64
KmsServiceAccountEmail *string
}

func NewCmd(params *types.CmdParams) *cobra.Command {
Expand All @@ -45,6 +55,9 @@ func NewCmd(params *types.CmdParams) *cobra.Command {
examples.NewExample(
`Create a Secrets Manager instance with name "my-instance" and specify IP range which is allowed to access it`,
`$ stackit secrets-manager instance create --name my-instance --acl 1.2.3.0/24`),
examples.NewExample(
`Create a Secrets Manager instance with name "my-instance" and configure KMS key options`,
`$ stackit secrets-manager instance create --name my-instance --kms-key-id key-id --kms-keyring-id keyring-id --kms-key-version 1 --kms-service-account-email my-service-account-1234567@sa.stackit.cloud`),
),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
Expand Down Expand Up @@ -103,8 +116,15 @@ func configureFlags(cmd *cobra.Command) {
cmd.Flags().StringP(instanceNameFlag, "n", "", "Instance name")
cmd.Flags().Var(flags.CIDRSliceFlag(), aclFlag, "List of IP networks in CIDR notation which are allowed to access this instance")

cmd.Flags().String(kmsKeyIdFlag, "", "ID of the KMS key to use for encryption")
cmd.Flags().String(kmsKeyringIdFlag, "", "ID of the KMS key ring")
cmd.Flags().Int64(kmsKeyVersionFlag, 0, "Version of the KMS key")
cmd.Flags().String(kmsServiceAccountEmailFlag, "", "Service account email for KMS access")

err := flags.MarkFlagsRequired(cmd, instanceNameFlag)
cobra.CheckErr(err)

cmd.MarkFlagsRequiredTogether(kmsKeyIdFlag, kmsKeyringIdFlag, kmsKeyVersionFlag, kmsServiceAccountEmailFlag)
}

func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, error) {
Expand All @@ -114,9 +134,13 @@ func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel,
}

model := inputModel{
GlobalFlagModel: globalFlags,
InstanceName: flags.FlagToStringPointer(p, cmd, instanceNameFlag),
Acls: flags.FlagToStringSlicePointer(p, cmd, aclFlag),
GlobalFlagModel: globalFlags,
InstanceName: flags.FlagToStringPointer(p, cmd, instanceNameFlag),
Acls: flags.FlagToStringSlicePointer(p, cmd, aclFlag),
KmsKeyId: flags.FlagToStringPointer(p, cmd, kmsKeyIdFlag),
KmsKeyringId: flags.FlagToStringPointer(p, cmd, kmsKeyringIdFlag),
KmsKeyVersion: flags.FlagToInt64Pointer(p, cmd, kmsKeyVersionFlag),
KmsServiceAccountEmail: flags.FlagToStringPointer(p, cmd, kmsServiceAccountEmailFlag),
}

p.DebugInputModel(model)
Expand All @@ -126,9 +150,20 @@ func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel,
func buildCreateInstanceRequest(ctx context.Context, model *inputModel, apiClient *secretsmanager.APIClient) secretsmanager.ApiCreateInstanceRequest {
req := apiClient.CreateInstance(ctx, model.ProjectId)

req = req.CreateInstancePayload(secretsmanager.CreateInstancePayload{
payload := secretsmanager.CreateInstancePayload{
Name: model.InstanceName,
})
}

if model.KmsKeyId != nil {
payload.KmsKey = &secretsmanager.KmsKeyPayload{
KeyId: model.KmsKeyId,
KeyRingId: model.KmsKeyringId,
KeyVersion: model.KmsKeyVersion,
ServiceAccountEmail: model.KmsServiceAccountEmail,
}
}

req = req.CreateInstancePayload(payload)

return req
}
Expand Down
47 changes: 47 additions & 0 deletions internal/cmd/secrets-manager/instance/create/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ var testClient = &secretsmanager.APIClient{}
var testProjectId = uuid.NewString()
var testInstanceId = uuid.NewString()

const (
testKmsKeyId = "key-id"
testKmsKeyringId = "keyring-id"
testKmsKeyVersion = int64(1)
testKmsServiceAccountEmail = "my-service-account-1234567@sa.stackit.cloud"
)

func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string {
flagValues := map[string]string{
projectIdFlag: testProjectId,
Expand Down Expand Up @@ -162,6 +169,24 @@ func TestParseInput(t *testing.T) {
*model.Acls = append(*model.Acls, "1.2.3.4/32")
}),
},
{
description: "kms flags",
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
delete(flagValues, aclFlag)
flagValues[kmsKeyIdFlag] = testKmsKeyId
flagValues[kmsKeyringIdFlag] = testKmsKeyringId
flagValues[kmsKeyVersionFlag] = "1"
flagValues[kmsServiceAccountEmailFlag] = testKmsServiceAccountEmail
}),
isValid: true,
expectedModel: fixtureInputModel(func(model *inputModel) {
model.Acls = nil
model.KmsKeyId = utils.Ptr(testKmsKeyId)
model.KmsKeyringId = utils.Ptr(testKmsKeyringId)
model.KmsKeyVersion = utils.Ptr(testKmsKeyVersion)
model.KmsServiceAccountEmail = utils.Ptr(testKmsServiceAccountEmail)
}),
},
{
description: "project id missing",
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
Expand Down Expand Up @@ -205,6 +230,28 @@ func TestBuildCreateInstanceRequest(t *testing.T) {
model: fixtureInputModel(),
expectedRequest: fixtureRequest(),
},
{
description: "with kms",
model: fixtureInputModel(func(model *inputModel) {
model.Acls = nil
model.KmsKeyId = utils.Ptr(testKmsKeyId)
model.KmsKeyringId = utils.Ptr(testKmsKeyringId)
model.KmsKeyVersion = utils.Ptr(testKmsKeyVersion)
model.KmsServiceAccountEmail = utils.Ptr(testKmsServiceAccountEmail)
}),
expectedRequest: fixtureRequest(func(request *secretsmanager.ApiCreateInstanceRequest) {
payload := secretsmanager.CreateInstancePayload{
Name: utils.Ptr("example"),
KmsKey: &secretsmanager.KmsKeyPayload{
KeyId: utils.Ptr(testKmsKeyId),
KeyRingId: utils.Ptr(testKmsKeyringId),
KeyVersion: utils.Ptr(testKmsKeyVersion),
ServiceAccountEmail: utils.Ptr(testKmsServiceAccountEmail),
},
}
*request = (*request).CreateInstancePayload(payload)
}),
},
}

for _, tt := range tests {
Expand Down
14 changes: 14 additions & 0 deletions internal/cmd/secrets-manager/instance/describe/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,17 @@ func outputResult(p *print.Printer, outputFormat string, instance *secretsmanage
table.AddSeparator()
table.AddRow("CREATION DATE", utils.PtrString(instance.CreationStartDate))
table.AddSeparator()
kmsKey := instance.KmsKey
showKms := kmsKey != nil && (kmsKey.KeyId != nil || kmsKey.KeyRingId != nil || kmsKey.KeyVersion != nil || kmsKey.ServiceAccountEmail != nil)
if showKms {
table.AddRow("KMS KEY ID", utils.PtrString(kmsKey.KeyId))
table.AddSeparator()
table.AddRow("KMS KEYRING ID", utils.PtrString(kmsKey.KeyRingId))
table.AddSeparator()
table.AddRow("KMS KEY VERSION", utils.PtrString(kmsKey.KeyVersion))
table.AddSeparator()
table.AddRow("KMS SERVICE ACCOUNT EMAIL", utils.PtrString(kmsKey.ServiceAccountEmail))
}
// Only show ACL if it's present and not empty
if aclList.Acls != nil && len(*aclList.Acls) > 0 {
var cidrs []string
Expand All @@ -136,6 +147,9 @@ func outputResult(p *print.Printer, outputFormat string, instance *secretsmanage
cidrs = append(cidrs, *acl.Cidr)
}

if showKms {
table.AddSeparator()
}
table.AddRow("ACL", strings.Join(cidrs, ","))
}
err := table.Display(p)
Expand Down
16 changes: 16 additions & 0 deletions internal/cmd/secrets-manager/instance/describe/describe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
"github.com/stackitcloud/stackit-cli/internal/pkg/testutils"
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
Expand Down Expand Up @@ -247,6 +248,21 @@ func TestOutputResult(t *testing.T) {
},
wantErr: false,
},
{
name: "instance with kms key",
args: args{
instance: &secretsmanager.Instance{
KmsKey: &secretsmanager.KmsKeyPayload{
KeyId: utils.Ptr("key-id"),
KeyRingId: utils.Ptr("keyring-id"),
KeyVersion: utils.Ptr(int64(1)),
ServiceAccountEmail: utils.Ptr("my-service-account-1234567@sa.stackit.cloud"),
},
},
aclList: &secretsmanager.ListACLsResponse{},
},
wantErr: false,
},
}
p := print.NewPrinter()
p.Cmd = NewCmd(&types.CmdParams{Printer: p})
Expand Down
72 changes: 63 additions & 9 deletions internal/cmd/secrets-manager/instance/update/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,23 @@ const (
instanceIdArg = "INSTANCE_ID"

aclFlag = "acl"

kmsKeyIdFlag = "kms-key-id"
kmsKeyringIdFlag = "kms-keyring-id"
kmsKeyVersionFlag = "kms-key-version"
kmsServiceAccountEmailFlag = "kms-service-account-email"
)

type inputModel struct {
*globalflags.GlobalFlagModel
InstanceId string

Acls *[]string

KmsKeyId *string
KmsKeyringId *string
KmsKeyVersion *int64
KmsServiceAccountEmail *string
}

func NewCmd(params *types.CmdParams) *cobra.Command {
Expand All @@ -44,6 +54,9 @@ func NewCmd(params *types.CmdParams) *cobra.Command {
examples.NewExample(
`Update the range of IPs allowed to access a Secrets Manager instance with ID "xxx"`,
"$ stackit secrets-manager instance update xxx --acl 1.2.3.0/24"),
examples.NewExample(
`Update the KMS key settings of a Secrets Manager instance with ID "xxx"`,
"$ stackit secrets-manager instance update xxx --kms-key-id key-id --kms-keyring-id keyring-id --kms-key-version 1 --kms-service-account-email my-service-account-1234567@sa.stackit.cloud"),
),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
Expand Down Expand Up @@ -72,7 +85,14 @@ func NewCmd(params *types.CmdParams) *cobra.Command {

// Call API
req := buildRequest(ctx, model, apiClient)
err = req.Execute()
switch request := req.(type) {
case secretsmanager.ApiUpdateInstanceRequest:
err = request.Execute()
case secretsmanager.ApiUpdateACLsRequest:
err = request.Execute()
default:
err = fmt.Errorf("unknown request type")
}
if err != nil {
return fmt.Errorf("update Secrets Manager instance: %w", err)
}
Expand All @@ -87,6 +107,15 @@ func NewCmd(params *types.CmdParams) *cobra.Command {

func configureFlags(cmd *cobra.Command) {
cmd.Flags().Var(flags.CIDRSliceFlag(), aclFlag, "List of IP networks in CIDR notation which are allowed to access this instance")

cmd.Flags().String(kmsKeyIdFlag, "", "ID of the KMS key to use for encryption")
cmd.Flags().String(kmsKeyringIdFlag, "", "ID of the KMS key ring")
cmd.Flags().Int64(kmsKeyVersionFlag, 0, "Version of the KMS key")
cmd.Flags().String(kmsServiceAccountEmailFlag, "", "Service account email for KMS access")

cmd.MarkFlagsRequiredTogether(kmsKeyIdFlag, kmsKeyringIdFlag, kmsKeyVersionFlag, kmsServiceAccountEmailFlag)
cmd.MarkFlagsMutuallyExclusive(aclFlag, kmsKeyIdFlag)
cmd.MarkFlagsOneRequired(aclFlag, kmsKeyIdFlag)
}

func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) {
Expand All @@ -99,21 +128,46 @@ func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inpu

acls := flags.FlagToStringSlicePointer(p, cmd, aclFlag)

if acls == nil {
return nil, &cliErr.EmptyUpdateError{}
}

model := inputModel{
GlobalFlagModel: globalFlags,
InstanceId: instanceId,
Acls: acls,
GlobalFlagModel: globalFlags,
InstanceId: instanceId,
Acls: acls,
KmsKeyId: flags.FlagToStringPointer(p, cmd, kmsKeyIdFlag),
KmsKeyringId: flags.FlagToStringPointer(p, cmd, kmsKeyringIdFlag),
KmsKeyVersion: flags.FlagToInt64Pointer(p, cmd, kmsKeyVersionFlag),
KmsServiceAccountEmail: flags.FlagToStringPointer(p, cmd, kmsServiceAccountEmailFlag),
}

p.DebugInputModel(model)
return &model, nil
}

func buildRequest(ctx context.Context, model *inputModel, apiClient *secretsmanager.APIClient) secretsmanager.ApiUpdateACLsRequest {
func buildRequest(ctx context.Context, model *inputModel, apiClient *secretsmanager.APIClient) interface{ Execute() error } {
if model.KmsKeyId != nil {
return buildUpdateInstanceRequest(ctx, model, apiClient)
}

return buildUpdateACLsRequest(ctx, model, apiClient)
}

func buildUpdateInstanceRequest(ctx context.Context, model *inputModel, apiClient *secretsmanager.APIClient) secretsmanager.ApiUpdateInstanceRequest {
req := apiClient.UpdateInstance(ctx, model.ProjectId, model.InstanceId)

payload := secretsmanager.UpdateInstancePayload{
KmsKey: &secretsmanager.KmsKeyPayload{
KeyId: model.KmsKeyId,
KeyRingId: model.KmsKeyringId,
KeyVersion: model.KmsKeyVersion,
ServiceAccountEmail: model.KmsServiceAccountEmail,
},
}

req = req.UpdateInstancePayload(payload)

return req
}

func buildUpdateACLsRequest(ctx context.Context, model *inputModel, apiClient *secretsmanager.APIClient) secretsmanager.ApiUpdateACLsRequest {
req := apiClient.UpdateACLs(ctx, model.ProjectId, model.InstanceId)

cidrs := []secretsmanager.UpdateACLPayload{}
Expand Down
Loading
Loading