Skip to content

Commit 7ee923b

Browse files
author
Jan Sternagel
committed
pkg setup for kms
1 parent eb7c321 commit 7ee923b

File tree

4 files changed

+352
-0
lines changed

4 files changed

+352
-0
lines changed

internal/pkg/config/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const (
3636
RedisCustomEndpointKey = "redis_custom_endpoint"
3737
ResourceManagerEndpointKey = "resource_manager_custom_endpoint"
3838
SecretsManagerCustomEndpointKey = "secrets_manager_custom_endpoint"
39+
KMSCustomEndpointKey = "kms_custom_endpoint"
3940
ServiceAccountCustomEndpointKey = "service_account_custom_endpoint"
4041
ServiceEnablementCustomEndpointKey = "service_enablement_custom_endpoint"
4142
ServerBackupCustomEndpointKey = "serverbackup_custom_endpoint"
@@ -95,6 +96,7 @@ var ConfigKeys = []string{
9596
RedisCustomEndpointKey,
9697
ResourceManagerEndpointKey,
9798
SecretsManagerCustomEndpointKey,
99+
KMSCustomEndpointKey,
98100
ServiceAccountCustomEndpointKey,
99101
ServiceEnablementCustomEndpointKey,
100102
ServerBackupCustomEndpointKey,
@@ -180,6 +182,7 @@ func setConfigDefaults() {
180182
viper.SetDefault(PostgresFlexCustomEndpointKey, "")
181183
viper.SetDefault(ResourceManagerEndpointKey, "")
182184
viper.SetDefault(SecretsManagerCustomEndpointKey, "")
185+
viper.SetDefault(KMSCustomEndpointKey, "")
183186
viper.SetDefault(ServiceAccountCustomEndpointKey, "")
184187
viper.SetDefault(ServiceEnablementCustomEndpointKey, "")
185188
viper.SetDefault(ServerBackupCustomEndpointKey, "")
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package client
2+
3+
import (
4+
"github.com/stackitcloud/stackit-cli/internal/pkg/auth"
5+
"github.com/stackitcloud/stackit-cli/internal/pkg/config"
6+
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
7+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
8+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
9+
10+
"github.com/spf13/viper"
11+
sdkConfig "github.com/stackitcloud/stackit-sdk-go/core/config"
12+
"github.com/stackitcloud/stackit-sdk-go/services/kms"
13+
)
14+
15+
func ConfigureClient(p *print.Printer, cliVersion string) (*kms.APIClient, error) {
16+
authCfgOption, err := auth.AuthenticationConfig(p, auth.AuthorizeUser)
17+
if err != nil {
18+
p.Debug(print.ErrorLevel, "configure authentication: %v", err)
19+
return nil, &errors.AuthError{}
20+
}
21+
cfgOptions := []sdkConfig.ConfigurationOption{
22+
utils.UserAgentConfigOption(cliVersion),
23+
authCfgOption,
24+
}
25+
26+
customEndpoint := viper.GetString(config.KMSCustomEndpointKey)
27+
28+
if customEndpoint != "" {
29+
cfgOptions = append(cfgOptions, sdkConfig.WithEndpoint(customEndpoint))
30+
}
31+
32+
if p.IsVerbosityDebug() {
33+
cfgOptions = append(cfgOptions,
34+
sdkConfig.WithMiddleware(print.RequestResponseCapturer(p, nil)),
35+
)
36+
}
37+
38+
apiClient, err := kms.NewAPIClient(cfgOptions...)
39+
if err != nil {
40+
p.Debug(print.ErrorLevel, "create new API client: %v", err)
41+
return nil, &errors.AuthError{}
42+
}
43+
44+
return apiClient, nil
45+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package utils
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"time"
7+
8+
"github.com/stackitcloud/stackit-sdk-go/services/kms"
9+
)
10+
11+
type KMSClient interface {
12+
GetKeyExecute(ctx context.Context, projectId string, regionId string, keyRingId string, keyId string) (*kms.Key, error)
13+
GetKeyRingExecute(ctx context.Context, projectId string, regionId string, keyRingId string) (*kms.KeyRing, error)
14+
GetWrappingKeyExecute(ctx context.Context, projectId string, regionId string, keyRingId string, wrappingKeyId string) (*kms.WrappingKey, error)
15+
}
16+
17+
func GetKeyName(ctx context.Context, apiClient KMSClient, projectId, region, keyRingId, keyId string) (string, error) {
18+
resp, err := apiClient.GetKeyExecute(ctx, projectId, region, keyRingId, keyId)
19+
if err != nil {
20+
return "", fmt.Errorf("get KMS Key: %w", err)
21+
}
22+
return *resp.DisplayName, nil
23+
}
24+
25+
func GetKeyDeletionDate(ctx context.Context, apiClient KMSClient, projectId, region, keyRingId, keyId string) (time.Time, error) {
26+
resp, err := apiClient.GetKeyExecute(ctx, projectId, region, keyRingId, keyId)
27+
if err != nil {
28+
return time.Now(), fmt.Errorf("get KMS Key: %w", err)
29+
}
30+
return *resp.DeletionDate, nil
31+
}
32+
33+
func GetKeyRingName(ctx context.Context, apiClient KMSClient, projectId, id, region string) (string, error) {
34+
resp, err := apiClient.GetKeyRingExecute(ctx, projectId, region, id)
35+
if err != nil {
36+
return "", fmt.Errorf("get KMS Key Ring: %w", err)
37+
}
38+
return *resp.DisplayName, nil
39+
}
40+
41+
func GetWrappingKeyName(ctx context.Context, apiClient KMSClient, projectId, region, keyRingId, wrappingKeyId string) (string, error) {
42+
resp, err := apiClient.GetWrappingKeyExecute(ctx, projectId, region, keyRingId, wrappingKeyId)
43+
if err != nil {
44+
return "", fmt.Errorf("get KMS Wrapping Key: %w", err)
45+
}
46+
return *resp.DisplayName, nil
47+
}
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
package utils
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
"time"
8+
9+
"github.com/google/uuid"
10+
"github.com/stackitcloud/stackit-sdk-go/services/kms"
11+
)
12+
13+
var (
14+
testProjectId = uuid.NewString()
15+
testRegion = "eu01"
16+
testKeyRingId = uuid.NewString()
17+
testKeyId = uuid.NewString()
18+
testWrappingKeyId = uuid.NewString()
19+
)
20+
21+
const (
22+
testKeyName = "my-test-key"
23+
testKeyRingName = "my-key-ring"
24+
testWrappingKeyName = "my-wrapping-key"
25+
)
26+
27+
type kmsClientMocked struct {
28+
getKeyFails bool
29+
getKeyResp *kms.Key
30+
getKeyRingFails bool
31+
getKeyRingResp *kms.KeyRing
32+
getWrappingKeyFails bool
33+
getWrappingKeyResp *kms.WrappingKey
34+
}
35+
36+
// Implement the KMSClient interface methods for the mock.
37+
func (m *kmsClientMocked) GetKeyExecute(_ context.Context, _, _, _, _ string) (*kms.Key, error) {
38+
if m.getKeyFails {
39+
return nil, fmt.Errorf("could not get key")
40+
}
41+
return m.getKeyResp, nil
42+
}
43+
44+
func (m *kmsClientMocked) GetKeyRingExecute(_ context.Context, _, _, _ string) (*kms.KeyRing, error) {
45+
if m.getKeyRingFails {
46+
return nil, fmt.Errorf("could not get key ring")
47+
}
48+
return m.getKeyRingResp, nil
49+
}
50+
51+
func (m *kmsClientMocked) GetWrappingKeyExecute(_ context.Context, _, _, _, _ string) (*kms.WrappingKey, error) {
52+
if m.getWrappingKeyFails {
53+
return nil, fmt.Errorf("could not get wrapping key")
54+
}
55+
return m.getWrappingKeyResp, nil
56+
}
57+
58+
func TestGetKeyName(t *testing.T) {
59+
keyName := testKeyName
60+
61+
tests := []struct {
62+
description string
63+
getKeyFails bool
64+
getKeyResp *kms.Key
65+
isValid bool
66+
expectedOutput string
67+
}{
68+
{
69+
description: "base",
70+
getKeyResp: &kms.Key{
71+
DisplayName: &keyName,
72+
},
73+
isValid: true,
74+
expectedOutput: testKeyName,
75+
},
76+
{
77+
description: "get key fails",
78+
getKeyFails: true,
79+
isValid: false,
80+
},
81+
}
82+
83+
for _, tt := range tests {
84+
t.Run(tt.description, func(t *testing.T) {
85+
client := &kmsClientMocked{
86+
getKeyFails: tt.getKeyFails,
87+
getKeyResp: tt.getKeyResp,
88+
}
89+
90+
output, err := GetKeyName(context.Background(), client, testProjectId, testRegion, testKeyRingId, testKeyId)
91+
92+
if tt.isValid && err != nil {
93+
t.Errorf("failed on valid input: %v", err)
94+
}
95+
if !tt.isValid && err == nil {
96+
t.Errorf("did not fail on invalid input")
97+
}
98+
if !tt.isValid {
99+
return
100+
}
101+
if output != tt.expectedOutput {
102+
t.Errorf("expected output to be %q, got %q", tt.expectedOutput, output)
103+
}
104+
})
105+
}
106+
}
107+
108+
// TestGetKeyDeletionDate tests the GetKeyDeletionDate function.
109+
func TestGetKeyDeletionDate(t *testing.T) {
110+
mockTime := time.Date(2025, 8, 20, 0, 0, 0, 0, time.UTC)
111+
112+
tests := []struct {
113+
description string
114+
getKeyFails bool
115+
getKeyResp *kms.Key
116+
isValid bool
117+
expectedOutput time.Time
118+
}{
119+
{
120+
description: "base",
121+
getKeyResp: &kms.Key{
122+
DeletionDate: &mockTime,
123+
},
124+
isValid: true,
125+
expectedOutput: mockTime,
126+
},
127+
{
128+
description: "get key fails",
129+
getKeyFails: true,
130+
isValid: false,
131+
},
132+
}
133+
134+
for _, tt := range tests {
135+
t.Run(tt.description, func(t *testing.T) {
136+
client := &kmsClientMocked{
137+
getKeyFails: tt.getKeyFails,
138+
getKeyResp: tt.getKeyResp,
139+
}
140+
141+
output, err := GetKeyDeletionDate(context.Background(), client, testProjectId, testRegion, testKeyRingId, testKeyId)
142+
143+
if tt.isValid && err != nil {
144+
t.Errorf("failed on valid input: %v", err)
145+
}
146+
if !tt.isValid && err == nil {
147+
t.Errorf("did not fail on invalid input")
148+
}
149+
if !tt.isValid {
150+
return
151+
}
152+
if !output.Equal(tt.expectedOutput) {
153+
t.Errorf("expected output to be %v, got %v", tt.expectedOutput, output)
154+
}
155+
})
156+
}
157+
}
158+
159+
// TestGetKeyRingName tests the GetKeyRingName function.
160+
func TestGetKeyRingName(t *testing.T) {
161+
keyRingName := testKeyRingName
162+
163+
tests := []struct {
164+
description string
165+
getKeyRingFails bool
166+
getKeyRingResp *kms.KeyRing
167+
isValid bool
168+
expectedOutput string
169+
}{
170+
{
171+
description: "base",
172+
getKeyRingResp: &kms.KeyRing{
173+
DisplayName: &keyRingName,
174+
},
175+
isValid: true,
176+
expectedOutput: testKeyRingName,
177+
},
178+
{
179+
description: "get key ring fails",
180+
getKeyRingFails: true,
181+
isValid: false,
182+
},
183+
}
184+
185+
for _, tt := range tests {
186+
t.Run(tt.description, func(t *testing.T) {
187+
client := &kmsClientMocked{
188+
getKeyRingFails: tt.getKeyRingFails,
189+
getKeyRingResp: tt.getKeyRingResp,
190+
}
191+
192+
output, err := GetKeyRingName(context.Background(), client, testProjectId, testKeyRingId, testRegion)
193+
194+
if tt.isValid && err != nil {
195+
t.Errorf("failed on valid input: %v", err)
196+
}
197+
if !tt.isValid && err == nil {
198+
t.Errorf("did not fail on invalid input")
199+
}
200+
if !tt.isValid {
201+
return
202+
}
203+
if output != tt.expectedOutput {
204+
t.Errorf("expected output to be %q, got %q", tt.expectedOutput, output)
205+
}
206+
})
207+
}
208+
}
209+
210+
func TestGetWrappingKeyName(t *testing.T) {
211+
wrappingKeyName := testWrappingKeyName
212+
tests := []struct {
213+
description string
214+
getWrappingKeyFails bool
215+
getWrappingKeyResp *kms.WrappingKey
216+
isValid bool
217+
expectedOutput string
218+
}{
219+
{
220+
description: "base",
221+
getWrappingKeyResp: &kms.WrappingKey{
222+
DisplayName: &wrappingKeyName,
223+
},
224+
isValid: true,
225+
expectedOutput: testWrappingKeyName,
226+
},
227+
{
228+
description: "get wrapping key fails",
229+
getWrappingKeyFails: true,
230+
isValid: false,
231+
},
232+
}
233+
234+
for _, tt := range tests {
235+
t.Run(tt.description, func(t *testing.T) {
236+
client := &kmsClientMocked{
237+
getWrappingKeyFails: tt.getWrappingKeyFails,
238+
getWrappingKeyResp: tt.getWrappingKeyResp,
239+
}
240+
241+
output, err := GetWrappingKeyName(context.Background(), client, testProjectId, testRegion, testKeyRingId, testWrappingKeyId)
242+
243+
if tt.isValid && err != nil {
244+
t.Errorf("failed on valid input: %v", err)
245+
}
246+
if !tt.isValid && err == nil {
247+
t.Errorf("did not fail on invalid input")
248+
}
249+
if !tt.isValid {
250+
return
251+
}
252+
if output != tt.expectedOutput {
253+
t.Errorf("expected output to be %q, got %q", tt.expectedOutput, output)
254+
}
255+
})
256+
}
257+
}

0 commit comments

Comments
 (0)