Skip to content

Commit 5003626

Browse files
authored
feat: Add kms key options to secrets manager instance commands (#1347)
* feat(secrets-manager): add KMS flags to create and update instance commands * feat(secrets-manager): implement request building with KMS key * feat(secrets-manager): enforce mutual exclusivity between ACL and KMS key flags * refactor(secrets-manager): refactor flag requirements to use cobra built-in validtion * feat(secrets-manager): add KMS key options examples for create and update commands * refactor(secrets-manager): change test data * feat(secrets-manager): add KMS key options to create and update instance tests * feat(secrets-manager): add KMS key details to instance output * feat(secrets-manager): add docs * fix(secrets-manager): include instance name in payload, as it is required * feat(secrets-manager): Support multiple API calls for instance update and ACL updates to align with create command * feat(secrets-manager): test new instance update features
1 parent a1729da commit 5003626

File tree

8 files changed

+401
-100
lines changed

8 files changed

+401
-100
lines changed

docs/stackit_secrets-manager_instance_create.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,21 @@ stackit secrets-manager instance create [flags]
1818
1919
Create a Secrets Manager instance with name "my-instance" and specify IP range which is allowed to access it
2020
$ stackit secrets-manager instance create --name my-instance --acl 1.2.3.0/24
21+
22+
Create a Secrets Manager instance with name "my-instance" and configure KMS key options
23+
$ 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
2124
```
2225

2326
### Options
2427

2528
```
26-
--acl strings List of IP networks in CIDR notation which are allowed to access this instance (default [])
27-
-h, --help Help for "stackit secrets-manager instance create"
28-
-n, --name string Instance name
29+
--acl strings List of IP networks in CIDR notation which are allowed to access this instance (default [])
30+
-h, --help Help for "stackit secrets-manager instance create"
31+
--kms-key-id string ID of the KMS key to use for encryption
32+
--kms-key-version int Version of the KMS key
33+
--kms-keyring-id string ID of the KMS key ring
34+
--kms-service-account-email string Service account email for KMS access
35+
-n, --name string Instance name
2936
```
3037

3138
### Options inherited from parent commands

docs/stackit_secrets-manager_instance_update.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,29 @@ stackit secrets-manager instance update INSTANCE_ID [flags]
1313
### Examples
1414

1515
```
16+
Update the name of a Secrets Manager instance with ID "xxx"
17+
$ stackit secrets-manager instance update xxx --name my-new-name
18+
1619
Update the range of IPs allowed to access a Secrets Manager instance with ID "xxx"
1720
$ stackit secrets-manager instance update xxx --acl 1.2.3.0/24
21+
22+
Update the name and ACLs of a Secrets Manager instance with ID "xxx"
23+
$ stackit secrets-manager instance update xxx --name my-new-name --acl 1.2.3.0/24
24+
25+
Update the KMS key settings of a Secrets Manager instance with ID "xxx"
26+
$ stackit secrets-manager instance update xxx --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
1827
```
1928

2029
### Options
2130

2231
```
23-
--acl strings List of IP networks in CIDR notation which are allowed to access this instance (default [])
24-
-h, --help Help for "stackit secrets-manager instance update"
32+
--acl strings List of IP networks in CIDR notation which are allowed to access this instance (default [])
33+
-h, --help Help for "stackit secrets-manager instance update"
34+
--kms-key-id string ID of the KMS key to use for encryption
35+
--kms-key-version int Version of the KMS key
36+
--kms-keyring-id string ID of the KMS key ring
37+
--kms-service-account-email string Service account email for KMS access
38+
-n, --name string Instance name
2539
```
2640

2741
### Options inherited from parent commands

internal/cmd/secrets-manager/instance/create/create.go

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,23 @@ import (
2323
const (
2424
instanceNameFlag = "name"
2525
aclFlag = "acl"
26+
27+
kmsKeyIdFlag = "kms-key-id"
28+
kmsKeyringIdFlag = "kms-keyring-id"
29+
kmsKeyVersionFlag = "kms-key-version"
30+
kmsServiceAccountEmailFlag = "kms-service-account-email"
2631
)
2732

2833
type inputModel struct {
2934
*globalflags.GlobalFlagModel
3035

3136
InstanceName *string
3237
Acls *[]string
38+
39+
KmsKeyId *string
40+
KmsKeyringId *string
41+
KmsKeyVersion *int64
42+
KmsServiceAccountEmail *string
3343
}
3444

3545
func NewCmd(params *types.CmdParams) *cobra.Command {
@@ -45,6 +55,9 @@ func NewCmd(params *types.CmdParams) *cobra.Command {
4555
examples.NewExample(
4656
`Create a Secrets Manager instance with name "my-instance" and specify IP range which is allowed to access it`,
4757
`$ stackit secrets-manager instance create --name my-instance --acl 1.2.3.0/24`),
58+
examples.NewExample(
59+
`Create a Secrets Manager instance with name "my-instance" and configure KMS key options`,
60+
`$ 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`),
4861
),
4962
RunE: func(cmd *cobra.Command, args []string) error {
5063
ctx := context.Background()
@@ -103,8 +116,15 @@ func configureFlags(cmd *cobra.Command) {
103116
cmd.Flags().StringP(instanceNameFlag, "n", "", "Instance name")
104117
cmd.Flags().Var(flags.CIDRSliceFlag(), aclFlag, "List of IP networks in CIDR notation which are allowed to access this instance")
105118

119+
cmd.Flags().String(kmsKeyIdFlag, "", "ID of the KMS key to use for encryption")
120+
cmd.Flags().String(kmsKeyringIdFlag, "", "ID of the KMS key ring")
121+
cmd.Flags().Int64(kmsKeyVersionFlag, 0, "Version of the KMS key")
122+
cmd.Flags().String(kmsServiceAccountEmailFlag, "", "Service account email for KMS access")
123+
106124
err := flags.MarkFlagsRequired(cmd, instanceNameFlag)
107125
cobra.CheckErr(err)
126+
127+
cmd.MarkFlagsRequiredTogether(kmsKeyIdFlag, kmsKeyringIdFlag, kmsKeyVersionFlag, kmsServiceAccountEmailFlag)
108128
}
109129

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

116136
model := inputModel{
117-
GlobalFlagModel: globalFlags,
118-
InstanceName: flags.FlagToStringPointer(p, cmd, instanceNameFlag),
119-
Acls: flags.FlagToStringSlicePointer(p, cmd, aclFlag),
137+
GlobalFlagModel: globalFlags,
138+
InstanceName: flags.FlagToStringPointer(p, cmd, instanceNameFlag),
139+
Acls: flags.FlagToStringSlicePointer(p, cmd, aclFlag),
140+
KmsKeyId: flags.FlagToStringPointer(p, cmd, kmsKeyIdFlag),
141+
KmsKeyringId: flags.FlagToStringPointer(p, cmd, kmsKeyringIdFlag),
142+
KmsKeyVersion: flags.FlagToInt64Pointer(p, cmd, kmsKeyVersionFlag),
143+
KmsServiceAccountEmail: flags.FlagToStringPointer(p, cmd, kmsServiceAccountEmailFlag),
120144
}
121145

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

129-
req = req.CreateInstancePayload(secretsmanager.CreateInstancePayload{
153+
payload := secretsmanager.CreateInstancePayload{
130154
Name: model.InstanceName,
131-
})
155+
}
156+
157+
if model.KmsKeyId != nil {
158+
payload.KmsKey = &secretsmanager.KmsKeyPayload{
159+
KeyId: model.KmsKeyId,
160+
KeyRingId: model.KmsKeyringId,
161+
KeyVersion: model.KmsKeyVersion,
162+
ServiceAccountEmail: model.KmsServiceAccountEmail,
163+
}
164+
}
165+
166+
req = req.CreateInstancePayload(payload)
132167

133168
return req
134169
}

internal/cmd/secrets-manager/instance/create/create_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ var testClient = &secretsmanager.APIClient{}
2525
var testProjectId = uuid.NewString()
2626
var testInstanceId = uuid.NewString()
2727

28+
const (
29+
testKmsKeyId = "key-id"
30+
testKmsKeyringId = "keyring-id"
31+
testKmsKeyVersion = int64(1)
32+
testKmsServiceAccountEmail = "my-service-account-1234567@sa.stackit.cloud"
33+
)
34+
2835
func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string {
2936
flagValues := map[string]string{
3037
projectIdFlag: testProjectId,
@@ -162,6 +169,24 @@ func TestParseInput(t *testing.T) {
162169
*model.Acls = append(*model.Acls, "1.2.3.4/32")
163170
}),
164171
},
172+
{
173+
description: "kms flags",
174+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
175+
delete(flagValues, aclFlag)
176+
flagValues[kmsKeyIdFlag] = testKmsKeyId
177+
flagValues[kmsKeyringIdFlag] = testKmsKeyringId
178+
flagValues[kmsKeyVersionFlag] = "1"
179+
flagValues[kmsServiceAccountEmailFlag] = testKmsServiceAccountEmail
180+
}),
181+
isValid: true,
182+
expectedModel: fixtureInputModel(func(model *inputModel) {
183+
model.Acls = nil
184+
model.KmsKeyId = utils.Ptr(testKmsKeyId)
185+
model.KmsKeyringId = utils.Ptr(testKmsKeyringId)
186+
model.KmsKeyVersion = utils.Ptr(testKmsKeyVersion)
187+
model.KmsServiceAccountEmail = utils.Ptr(testKmsServiceAccountEmail)
188+
}),
189+
},
165190
{
166191
description: "project id missing",
167192
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
@@ -205,6 +230,28 @@ func TestBuildCreateInstanceRequest(t *testing.T) {
205230
model: fixtureInputModel(),
206231
expectedRequest: fixtureRequest(),
207232
},
233+
{
234+
description: "with kms",
235+
model: fixtureInputModel(func(model *inputModel) {
236+
model.Acls = nil
237+
model.KmsKeyId = utils.Ptr(testKmsKeyId)
238+
model.KmsKeyringId = utils.Ptr(testKmsKeyringId)
239+
model.KmsKeyVersion = utils.Ptr(testKmsKeyVersion)
240+
model.KmsServiceAccountEmail = utils.Ptr(testKmsServiceAccountEmail)
241+
}),
242+
expectedRequest: fixtureRequest(func(request *secretsmanager.ApiCreateInstanceRequest) {
243+
payload := secretsmanager.CreateInstancePayload{
244+
Name: utils.Ptr("example"),
245+
KmsKey: &secretsmanager.KmsKeyPayload{
246+
KeyId: utils.Ptr(testKmsKeyId),
247+
KeyRingId: utils.Ptr(testKmsKeyringId),
248+
KeyVersion: utils.Ptr(testKmsKeyVersion),
249+
ServiceAccountEmail: utils.Ptr(testKmsServiceAccountEmail),
250+
},
251+
}
252+
*request = (*request).CreateInstancePayload(payload)
253+
}),
254+
},
208255
}
209256

210257
for _, tt := range tests {

internal/cmd/secrets-manager/instance/describe/describe.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,17 @@ func outputResult(p *print.Printer, outputFormat string, instance *secretsmanage
128128
table.AddSeparator()
129129
table.AddRow("CREATION DATE", utils.PtrString(instance.CreationStartDate))
130130
table.AddSeparator()
131+
kmsKey := instance.KmsKey
132+
showKms := kmsKey != nil && (kmsKey.KeyId != nil || kmsKey.KeyRingId != nil || kmsKey.KeyVersion != nil || kmsKey.ServiceAccountEmail != nil)
133+
if showKms {
134+
table.AddRow("KMS KEY ID", utils.PtrString(kmsKey.KeyId))
135+
table.AddSeparator()
136+
table.AddRow("KMS KEYRING ID", utils.PtrString(kmsKey.KeyRingId))
137+
table.AddSeparator()
138+
table.AddRow("KMS KEY VERSION", utils.PtrString(kmsKey.KeyVersion))
139+
table.AddSeparator()
140+
table.AddRow("KMS SERVICE ACCOUNT EMAIL", utils.PtrString(kmsKey.ServiceAccountEmail))
141+
}
131142
// Only show ACL if it's present and not empty
132143
if aclList.Acls != nil && len(*aclList.Acls) > 0 {
133144
var cidrs []string
@@ -136,6 +147,9 @@ func outputResult(p *print.Printer, outputFormat string, instance *secretsmanage
136147
cidrs = append(cidrs, *acl.Cidr)
137148
}
138149

150+
if showKms {
151+
table.AddSeparator()
152+
}
139153
table.AddRow("ACL", strings.Join(cidrs, ","))
140154
}
141155
err := table.Display(p)

internal/cmd/secrets-manager/instance/describe/describe_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
1010
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
1111
"github.com/stackitcloud/stackit-cli/internal/pkg/testutils"
12+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
1213

1314
"github.com/google/go-cmp/cmp"
1415
"github.com/google/go-cmp/cmp/cmpopts"
@@ -247,6 +248,21 @@ func TestOutputResult(t *testing.T) {
247248
},
248249
wantErr: false,
249250
},
251+
{
252+
name: "instance with kms key",
253+
args: args{
254+
instance: &secretsmanager.Instance{
255+
KmsKey: &secretsmanager.KmsKeyPayload{
256+
KeyId: utils.Ptr("key-id"),
257+
KeyRingId: utils.Ptr("keyring-id"),
258+
KeyVersion: utils.Ptr(int64(1)),
259+
ServiceAccountEmail: utils.Ptr("my-service-account-1234567@sa.stackit.cloud"),
260+
},
261+
},
262+
aclList: &secretsmanager.ListACLsResponse{},
263+
},
264+
wantErr: false,
265+
},
250266
}
251267
p := print.NewPrinter()
252268
p.Cmd = NewCmd(&types.CmdParams{Printer: p})

0 commit comments

Comments
 (0)