Skip to content

Commit 7e995f3

Browse files
author
bcm820
authored
[fix] Remove OBJ label field and add key ID status fields (#188)
1 parent 25eac0d commit 7e995f3

8 files changed

+38
-154
lines changed

api/v1alpha1/linodeobjectstoragebucket_types.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,6 @@ type LinodeObjectStorageBucketSpec struct {
3434
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
3535
Cluster string `json:"cluster"`
3636

37-
// Label specifies the name of the Object Storage Bucket.
38-
// If not supplied then the name of the LinodeObjectStorageBucket resource will be used.
39-
// +kubebuilder:validation:MinLength=3
40-
// +kubebuilder:validation:MaxLength=63
41-
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
42-
// +optional
43-
Label *string `json:"label,omitempty"`
44-
4537
// CredentialsRef is a reference to a Secret that contains the credentials to use for provisioning the bucket.
4638
// If not supplied then the credentials of the controller will be used.
4739
// +optional
@@ -88,6 +80,10 @@ type LinodeObjectStorageBucketStatus struct {
8880
// KeySecretName specifies the name of the Secret containing access keys for the bucket.
8981
// +optional
9082
KeySecretName *string `json:"keySecretName,omitempty"`
83+
84+
// AccessKeyRefs stores IDs for Object Storage keys provisioned along with the bucket.
85+
// +optional
86+
AccessKeyRefs []int `json:"accessKeyRefs,omitempty"`
9187
}
9288

9389
// +kubebuilder:object:root=true

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cloud/scope/object_storage_bucket.go

Lines changed: 6 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package scope
22

33
import (
44
"context"
5-
"encoding/json"
65
"errors"
76
"fmt"
87

@@ -103,86 +102,31 @@ func (s *ObjectStorageBucketScope) AddFinalizer(ctx context.Context) error {
103102

104103
// ApplyAccessKeySecret applies a Secret containing keys created for accessing the bucket.
105104
func (s *ObjectStorageBucketScope) ApplyAccessKeySecret(ctx context.Context, keys [NumAccessKeys]linodego.ObjectStorageKey, secretName string) error {
106-
var err error
107-
108-
accessKeys := make([]json.RawMessage, NumAccessKeys)
109-
for i, key := range keys {
110-
accessKeys[i], err = json.Marshal(key)
111-
if err != nil {
112-
return fmt.Errorf("could not unmarshal access key %s: %w", key.Label, err)
113-
}
114-
}
115-
116105
secret := &corev1.Secret{
117106
ObjectMeta: metav1.ObjectMeta{
118107
Name: secretName,
119108
Namespace: s.Bucket.Namespace,
120109
},
121110
StringData: map[string]string{
122-
"read_write": string(accessKeys[0]),
123-
"read_only": string(accessKeys[1]),
111+
"read_write": keys[0].AccessKey,
112+
"read_only": keys[1].AccessKey,
124113
},
125114
}
126115

127116
if err := controllerutil.SetOwnerReference(s.Bucket, secret, s.client.Scheme()); err != nil {
128117
return fmt.Errorf("could not set owner ref on access key secret %s: %w", secretName, err)
129118
}
130119

131-
// Add finalizer to secret so it isn't deleted when bucket deletion is triggered
132-
controllerutil.AddFinalizer(secret, infrav1alpha1.GroupVersion.String())
133-
134-
if s.Bucket.Status.KeySecretName == nil {
135-
if err := s.client.Create(ctx, secret); err != nil {
136-
return fmt.Errorf("could not create access key secret %s: %w", secretName, err)
137-
}
138-
139-
return nil
120+
result, err := controllerutil.CreateOrPatch(ctx, s.client, secret, func() error { return nil })
121+
if err != nil {
122+
return fmt.Errorf("could not create/patch access key secret %s: %w", secretName, err)
140123
}
141124

142-
if err := s.client.Update(ctx, secret); err != nil {
143-
return fmt.Errorf("could not update access key secret %s: %w", secretName, err)
144-
}
125+
s.Logger.Info(fmt.Sprintf("Secret %s was %s with new access keys", secret.Name, result))
145126

146127
return nil
147128
}
148129

149-
func (s *ObjectStorageBucketScope) GetAccessKeySecret(ctx context.Context) (*corev1.Secret, error) {
150-
secretName := fmt.Sprintf(AccessKeyNameTemplate, *s.Bucket.Spec.Label)
151-
152-
objKey := client.ObjectKey{
153-
Namespace: s.Bucket.Namespace,
154-
Name: secretName,
155-
}
156-
var secret corev1.Secret
157-
if err := s.client.Get(ctx, objKey, &secret); err != nil {
158-
return nil, err
159-
}
160-
161-
return &secret, nil
162-
}
163-
164-
// GetAccessKeysFromSecret gets the access key IDs for the OBJ buckets from a Secret.
165-
func (s *ObjectStorageBucketScope) GetAccessKeysFromSecret(ctx context.Context, secret *corev1.Secret) ([NumAccessKeys]int, error) {
166-
var keyIDs [NumAccessKeys]int
167-
168-
permissions := [NumAccessKeys]string{"read_write", "read_only"}
169-
for idx, permission := range permissions {
170-
secretDataForKey, ok := secret.Data[permission]
171-
if !ok {
172-
return keyIDs, fmt.Errorf("secret %s missing data field %s", secret.Name, permission)
173-
}
174-
175-
var key linodego.ObjectStorageKey
176-
if err := json.Unmarshal(secretDataForKey, &key); err != nil {
177-
return keyIDs, fmt.Errorf("error unmarshaling key: %w", err)
178-
}
179-
180-
keyIDs[idx] = key.ID
181-
}
182-
183-
return keyIDs, nil
184-
}
185-
186130
func (s *ObjectStorageBucketScope) ShouldRotateKeys() bool {
187131
return *s.Bucket.Spec.KeyGeneration != *s.Bucket.Status.LastKeyGeneration
188132
}

cloud/services/object_storage_buckets.go

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"net/http"
88

99
"github.com/linode/linodego"
10-
corev1 "k8s.io/api/core/v1"
1110
utilerrors "k8s.io/apimachinery/pkg/util/errors"
1211

1312
"github.com/linode/cluster-api-provider-linode/cloud/scope"
@@ -17,7 +16,7 @@ func EnsureObjectStorageBucket(ctx context.Context, bScope *scope.ObjectStorageB
1716
bucket, err := bScope.LinodeClient.GetObjectStorageBucket(
1817
ctx,
1918
bScope.Bucket.Spec.Cluster,
20-
*bScope.Bucket.Spec.Label,
19+
bScope.Bucket.Name,
2120
)
2221
linodeErr := &linodego.Error{}
2322
if errors.As(err, linodeErr) && linodeErr.StatusCode() != http.StatusNotFound {
@@ -31,7 +30,7 @@ func EnsureObjectStorageBucket(ctx context.Context, bScope *scope.ObjectStorageB
3130

3231
opts := linodego.ObjectStorageBucketCreateOptions{
3332
Cluster: bScope.Bucket.Spec.Cluster,
34-
Label: *bScope.Bucket.Spec.Label,
33+
Label: bScope.Bucket.Name,
3534
ACL: linodego.ACLPrivate,
3635
}
3736

@@ -54,7 +53,7 @@ func RotateObjectStorageKeys(ctx context.Context, bScope *scope.ObjectStorageBuc
5453
{"read_write", "rw"},
5554
{"read_only", "ro"},
5655
} {
57-
keyLabel := fmt.Sprintf("%s-%s", *bScope.Bucket.Spec.Label, permission.suffix)
56+
keyLabel := fmt.Sprintf("%s-%s", bScope.Bucket.Name, permission.suffix)
5857
key, err := createObjectStorageKey(ctx, bScope, keyLabel, permission.name)
5958
if err != nil {
6059
return newKeys, err
@@ -65,12 +64,7 @@ func RotateObjectStorageKeys(ctx context.Context, bScope *scope.ObjectStorageBuc
6564

6665
// If key revocation fails here, just log the errors since new keys have been created
6766
if bScope.Bucket.Status.LastKeyGeneration != nil && bScope.ShouldRotateKeys() {
68-
secret, err := bScope.GetAccessKeySecret(ctx)
69-
if err != nil {
70-
bScope.Logger.Error(err, "Failed to read secret with access keys to revoke; keys must be manually revoked")
71-
}
72-
73-
if err := RevokeObjectStorageKeys(ctx, bScope, secret); err != nil {
67+
if err := RevokeObjectStorageKeys(ctx, bScope); err != nil {
7468
bScope.Logger.Error(err, "Failed to revoke access keys; keys must be manually revoked")
7569
}
7670
}
@@ -83,7 +77,7 @@ func createObjectStorageKey(ctx context.Context, bScope *scope.ObjectStorageBuck
8377
Label: label,
8478
BucketAccess: &[]linodego.ObjectStorageKeyBucketAccess{
8579
{
86-
BucketName: *bScope.Bucket.Spec.Label,
80+
BucketName: bScope.Bucket.Name,
8781
Cluster: bScope.Bucket.Spec.Cluster,
8882
Permissions: permission,
8983
},
@@ -102,20 +96,9 @@ func createObjectStorageKey(ctx context.Context, bScope *scope.ObjectStorageBuck
10296
return key, nil
10397
}
10498

105-
func RevokeObjectStorageKeys(ctx context.Context, bScope *scope.ObjectStorageBucketScope, secret *corev1.Secret) error {
106-
if secret == nil {
107-
return errors.New("unable to read access keys from nil secret")
108-
}
109-
110-
keyIDs, err := bScope.GetAccessKeysFromSecret(ctx, secret)
111-
if err != nil {
112-
bScope.Logger.Error(err, "Failed to read secret with access keys to revoke; must be manually revoked")
113-
114-
return fmt.Errorf("failed to read secret %s with access keys: %w", secret.Name, err)
115-
}
116-
99+
func RevokeObjectStorageKeys(ctx context.Context, bScope *scope.ObjectStorageBucketScope) error {
117100
var errs []error
118-
for _, keyID := range keyIDs {
101+
for _, keyID := range bScope.Bucket.Status.AccessKeyRefs {
119102
if err := revokeObjectStorageKey(ctx, bScope, keyID); err != nil {
120103
errs = append(errs, err)
121104
}

config/crd/bases/infrastructure.cluster.x-k8s.io_linodeobjectstoragebuckets.yaml

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -83,23 +83,19 @@ spec:
8383
description: KeyGeneration may be modified to trigger rotations of
8484
access keys created for the bucket.
8585
type: integer
86-
label:
87-
description: |-
88-
Label specifies the name of the Object Storage Bucket.
89-
If not supplied then the name of the LinodeObjectStorageBucket resource will be used.
90-
maxLength: 63
91-
minLength: 3
92-
type: string
93-
x-kubernetes-validations:
94-
- message: Value is immutable
95-
rule: self == oldSelf
9686
required:
9787
- cluster
9888
type: object
9989
status:
10090
description: LinodeObjectStorageBucketStatus defines the observed state
10191
of LinodeObjectStorageBucket
10292
properties:
93+
accessKeyRefs:
94+
description: AccessKeyRefs stores IDs for Object Storage keys provisioned
95+
along with the bucket.
96+
items:
97+
type: integer
98+
type: array
10399
conditions:
104100
description: Conditions specify the service state of the LinodeObjectStorageBucket.
105101
items:

config/samples/infrastructure_v1alpha1_linodeobjectstoragebucket.yaml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,5 @@ metadata:
1010
app.kubernetes.io/created-by: cluster-api-provider-linode
1111
name: linodeobjectstoragebucket-sample
1212
spec:
13-
credentialsRef:
14-
name: api-key-secret
15-
namespace: default
1613
keyGeneration: 0
1714
cluster: us-ord-1
18-
---
19-
apiVersion: v1
20-
kind: Secret
21-
metadata:
22-
name: api-key-secret
23-
stringData:
24-
apiToken: "changeme"

controller/linodeobjectstoragebucket_controller.go

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package controller
1818

1919
import (
2020
"context"
21+
"errors"
2122
"fmt"
2223
"time"
2324

@@ -143,10 +144,6 @@ func (r *LinodeObjectStorageBucketReconciler) reconcileApply(ctx context.Context
143144
return err
144145
}
145146

146-
if bScope.Bucket.Spec.Label == nil {
147-
bScope.Bucket.Spec.Label = util.Pointer(bScope.Bucket.Name)
148-
}
149-
150147
bucket, err := services.EnsureObjectStorageBucket(ctx, bScope)
151148
if err != nil {
152149
bScope.Logger.Error(err, "Failed to ensure bucket exists")
@@ -165,8 +162,9 @@ func (r *LinodeObjectStorageBucketReconciler) reconcileApply(ctx context.Context
165162

166163
return err
167164
}
165+
bScope.Bucket.Status.AccessKeyRefs = []int{keys[0].ID, keys[1].ID}
168166

169-
secretName := fmt.Sprintf(scope.AccessKeyNameTemplate, *bScope.Bucket.Spec.Label)
167+
secretName := fmt.Sprintf(scope.AccessKeyNameTemplate, bScope.Bucket.Name)
170168
if err := bScope.ApplyAccessKeySecret(ctx, keys, secretName); err != nil {
171169
bScope.Logger.Error(err, "Failed to apply access key secret")
172170
r.setFailure(bScope, err)
@@ -188,38 +186,16 @@ func (r *LinodeObjectStorageBucketReconciler) reconcileApply(ctx context.Context
188186
func (r *LinodeObjectStorageBucketReconciler) reconcileDelete(ctx context.Context, bScope *scope.ObjectStorageBucketScope) error {
189187
bScope.Logger.Info("Reconciling delete")
190188

191-
secret, err := bScope.GetAccessKeySecret(ctx)
192-
if err != nil {
193-
bScope.Logger.Error(err, "Failed to read secret with access keys to revoke")
194-
r.setFailure(bScope, err)
195-
196-
return err
197-
}
198-
199-
if err := services.RevokeObjectStorageKeys(ctx, bScope, secret); err != nil {
200-
bScope.Logger.Error(err, "Failed to revoke access keys; keys must be manually revoked")
201-
r.setFailure(bScope, err)
202-
203-
return err
204-
}
205-
206-
// Only permit Secret and LinodeObjectStorageBucket deletion if keys were revoked
207-
if !controllerutil.RemoveFinalizer(secret, infrav1alpha1.GroupVersion.String()) {
208-
bScope.Logger.Error(err, "Failed to remove finalizer from secret; will not be deleted")
209-
r.setFailure(bScope, err)
210-
211-
return err
212-
}
213-
214-
if err := r.Client.Update(ctx, secret); err != nil {
215-
bScope.Logger.Error(err, "Failed to remove finalizer from secret; will not be deleted")
189+
if err := services.RevokeObjectStorageKeys(ctx, bScope); err != nil {
190+
bScope.Logger.Error(err, "failed to revoke access keys; keys must be manually revoked")
216191
r.setFailure(bScope, err)
217192

218193
return err
219194
}
220195

221196
if !controllerutil.RemoveFinalizer(bScope.Bucket, infrav1alpha1.GroupVersion.String()) {
222-
bScope.Logger.Error(err, "Failed to remove finalizer from bucket; will not be deleted")
197+
err := errors.New("failed to remove finalizer from bucket; unable to delete")
198+
bScope.Logger.Error(err, "controllerutil.RemoveFinalizer")
223199
r.setFailure(bScope, err)
224200

225201
return err

controller/linodeobjectstoragebucket_controller_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,12 @@ var _ = Describe("LinodeObjectStorageBucket controller", func() {
5656
},
5757
Spec: infrav1.LinodeObjectStorageBucketSpec{
5858
Cluster: "cluster",
59-
Label: util.Pointer("sample"),
6059
},
6160
}
6261

6362
recorder := record.NewFakeRecorder(3)
6463

65-
secretName := fmt.Sprintf(scope.AccessKeyNameTemplate, *obj.Spec.Label)
64+
secretName := fmt.Sprintf(scope.AccessKeyNameTemplate, obj.Name)
6665

6766
var secret corev1.Secret
6867
var mockCtrl *gomock.Controller
@@ -89,7 +88,7 @@ var _ = Describe("LinodeObjectStorageBucket controller", func() {
8988
createBucketCall := mockClient.EXPECT().
9089
CreateObjectStorageBucket(gomock.Any(), gomock.Any()).
9190
Return(&linodego.ObjectStorageBucket{
92-
Label: *obj.Spec.Label,
91+
Label: obj.Name,
9392
Cluster: obj.Spec.Cluster,
9493
Created: util.Pointer(time.Now()),
9594
Hostname: "hostname",
@@ -107,7 +106,7 @@ var _ = Describe("LinodeObjectStorageBucket controller", func() {
107106
return false
108107
}
109108

110-
return createOpt.Label == fmt.Sprintf("%s-%s", *obj.Spec.Label, permission)
109+
return createOpt.Label == fmt.Sprintf("%s-%s", obj.Name, permission)
111110
}),
112111
).
113112
Return(&linodego.ObjectStorageKey{ID: idx}, nil).

0 commit comments

Comments
 (0)