Skip to content

Commit aee6e85

Browse files
authored
Merge pull request #6532 from whitewindmills/pr-3370
feat: retain registry credentials for ServiceAccount
2 parents 55aead1 + e2d70a7 commit aee6e85

File tree

2 files changed

+92
-23
lines changed

2 files changed

+92
-23
lines changed

pkg/util/lifted/retain.go

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import (
2929
const (
3030
// SecretsField indicates the 'secrets' field of a service account
3131
SecretsField = "secrets"
32+
// ImagePullSecretsField indicates the 'imagePullSecrets' field of a service account
33+
ImagePullSecretsField = "imagePullSecrets"
3234
)
3335

3436
// +lifted:source=https://github.com/kubernetes-sigs/kubefed/blob/master/pkg/controller/sync/dispatch/retain.go
@@ -84,19 +86,36 @@ func retainServiceClusterIP(desired, observed *unstructured.Unstructured) error
8486
// +lifted:source=https://github.com/kubernetes-sigs/kubefed/blob/master/pkg/controller/sync/dispatch/retain.go
8587
// +lifted:changed
8688

87-
// RetainServiceAccountFields merges the 'secrets' field in the service account
89+
// RetainServiceAccountFields merges the 'secrets' field and the 'imagePullSecrets' field in the service account
8890
// of the control plane and the member clusters and retains the merged service account. This
8991
// ensures that the karmada-controller-manager doesn't continually clear a generated
9092
// secret from a service account, prompting continual regeneration by the
9193
// service account controller in the member cluster.
92-
// Related issue: https://github.com/karmada-io/karmada/issues/2573
94+
// Related issue:
95+
// https://github.com/karmada-io/karmada/issues/2573
96+
// https://github.com/karmada-io/karmada/issues/3370
9397
func RetainServiceAccountFields(desired, observed *unstructured.Unstructured) (*unstructured.Unstructured, error) {
98+
secretsFields := []string{
99+
SecretsField,
100+
ImagePullSecretsField,
101+
}
102+
103+
for _, field := range secretsFields {
104+
if err := retainSecretsField(desired, observed, field); err != nil {
105+
return nil, err
106+
}
107+
}
108+
return desired, nil
109+
}
110+
111+
// retainSecretsField merges a given secrets field (e.g. 'secrets' or 'imagePullSecrets') in the service account.
112+
func retainSecretsField(desired, observed *unstructured.Unstructured, fieldName string) error {
94113
var mergedSecrets []interface{}
95114
isSecretExistMap := make(map[string]struct{})
96115

97-
desiredSecrets, ok, err := unstructured.NestedSlice(desired.Object, SecretsField)
116+
desiredSecrets, ok, err := unstructured.NestedSlice(desired.Object, fieldName)
98117
if err != nil {
99-
return nil, fmt.Errorf("error retrieving secrets from desired service account: %w", err)
118+
return fmt.Errorf("error retrieving %s from desired service account: %w", fieldName, err)
100119
}
101120

102121
if ok && len(desiredSecrets) > 0 {
@@ -107,9 +126,9 @@ func RetainServiceAccountFields(desired, observed *unstructured.Unstructured) (*
107126
}
108127
}
109128

110-
secrets, ok, err := unstructured.NestedSlice(observed.Object, SecretsField)
129+
secrets, ok, err := unstructured.NestedSlice(observed.Object, fieldName)
111130
if err != nil {
112-
return nil, fmt.Errorf("error retrieving secrets from service account: %w", err)
131+
return fmt.Errorf("error retrieving %s from service account: %w", fieldName, err)
113132
}
114133

115134
if ok && len(secrets) > 0 {
@@ -123,10 +142,9 @@ func RetainServiceAccountFields(desired, observed *unstructured.Unstructured) (*
123142
}
124143
}
125144

126-
err = unstructured.SetNestedField(desired.Object, mergedSecrets, SecretsField)
145+
err = unstructured.SetNestedField(desired.Object, mergedSecrets, fieldName)
127146
if err != nil {
128-
return nil, fmt.Errorf("error setting secrets for service account: %w", err)
147+
return fmt.Errorf("error setting %s for service account: %w", fieldName, err)
129148
}
130-
131-
return desired, nil
149+
return nil
132150
}

pkg/util/lifted/retain_test.go

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,12 @@ func TestRetainClusterIPInServiceFields(t *testing.T) {
191191

192192
func TestRetainServiceAccountFields(t *testing.T) {
193193
tests := []struct {
194-
name string
195-
desiredObj *unstructured.Unstructured
196-
observedObj *unstructured.Unstructured
197-
expectedErr bool
198-
expectedSecretsValue []interface{}
194+
name string
195+
desiredObj *unstructured.Unstructured
196+
observedObj *unstructured.Unstructured
197+
expectedErr bool
198+
expectedSecretsValue []interface{}
199+
expectedImagePullSecretsValue []interface{}
199200
}{
200201
{
201202
name: "neither desired or observed service account has the secrets field",
@@ -234,6 +235,7 @@ func TestRetainServiceAccountFields(t *testing.T) {
234235
"name": "test",
235236
},
236237
},
238+
expectedImagePullSecretsValue: nil,
237239
},
238240
{
239241
name: "desired and observed service account have the different secrets field",
@@ -267,6 +269,44 @@ func TestRetainServiceAccountFields(t *testing.T) {
267269
"name": "test-token",
268270
},
269271
},
272+
expectedImagePullSecretsValue: nil,
273+
},
274+
{
275+
name: "observed service account has the imagePullSecrets field but the desired not",
276+
desiredObj: &unstructured.Unstructured{
277+
Object: map[string]interface{}{
278+
"secrets": []interface{}{
279+
map[string]interface{}{
280+
"name": "test",
281+
},
282+
},
283+
},
284+
},
285+
observedObj: &unstructured.Unstructured{
286+
Object: map[string]interface{}{
287+
"secrets": []interface{}{
288+
map[string]interface{}{
289+
"name": "test",
290+
},
291+
},
292+
"imagePullSecrets": []interface{}{
293+
map[string]interface{}{
294+
"name": "foo",
295+
},
296+
},
297+
},
298+
},
299+
expectedErr: false,
300+
expectedSecretsValue: []interface{}{
301+
map[string]interface{}{
302+
"name": "test",
303+
},
304+
},
305+
expectedImagePullSecretsValue: []interface{}{
306+
map[string]interface{}{
307+
"name": "foo",
308+
},
309+
},
270310
},
271311
}
272312
for _, test := range tests {
@@ -277,15 +317,26 @@ func TestRetainServiceAccountFields(t *testing.T) {
277317
}
278318

279319
if err == nil {
280-
currentSecretValue, ok, err := unstructured.NestedSlice(desiredObj.Object, SecretsField)
281-
if err != nil {
282-
t.Fatalf("failed to get the secrets field from the serviceaccount, err is: %v", err)
320+
table := []struct {
321+
name string
322+
field string
323+
expectedValue []interface{}
324+
}{
325+
{name: "secrets", field: SecretsField, expectedValue: test.expectedSecretsValue},
326+
{name: "imagePullSecrets", field: ImagePullSecretsField, expectedValue: test.expectedImagePullSecretsValue},
283327
}
284-
if !ok && test.expectedSecretsValue != nil {
285-
t.Fatalf("expect specified secrets %s but not found", test.expectedSecretsValue)
286-
}
287-
if ok && !reflect.DeepEqual(test.expectedSecretsValue, currentSecretValue) {
288-
t.Fatalf("expect specified secrets %s but get %s", test.expectedSecretsValue, currentSecretValue)
328+
329+
for _, entry := range table {
330+
currentValue, ok, err := unstructured.NestedSlice(desiredObj.Object, entry.field)
331+
if err != nil {
332+
t.Fatalf("failed to get %q field from serviceaccount: %v", entry.name, err)
333+
}
334+
if !ok && entry.expectedValue != nil {
335+
t.Fatalf("expected %q %v but not found", entry.name, entry.expectedValue)
336+
}
337+
if ok && !reflect.DeepEqual(entry.expectedValue, currentValue) {
338+
t.Fatalf("unexpected %q: got %v, want %v", entry.name, currentValue, entry.expectedValue)
339+
}
289340
}
290341
}
291342
})

0 commit comments

Comments
 (0)