Skip to content

Commit

Permalink
Refresh external managed token secret if service account ns changes (#…
Browse files Browse the repository at this point in the history
…458)

Signed-off-by: zhujian <jiazhu@redhat.com>
  • Loading branch information
zhujian7 committed May 14, 2024
1 parent a31ee08 commit 5313ff7
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 24 deletions.
53 changes: 38 additions & 15 deletions pkg/operator/helpers/sa_syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,20 @@ import (
"k8s.io/utils/pointer"
)

type TokenGetterFunc func() ([]byte, []byte, error)
type TokenGetterFunc func() (token []byte, expiration []byte, additionalData map[string][]byte, err error)

// SATokenGetter get the saToken of target sa. If there is not secrets in the sa, use the tokenrequest to get a token.
func SATokenGetter(ctx context.Context, saName, saNamespace string, saClient kubernetes.Interface) TokenGetterFunc {
return func() ([]byte, []byte, error) {
return func() ([]byte, []byte, map[string][]byte, error) {
// get the service account
sa, err := saClient.CoreV1().ServiceAccounts(saNamespace).Get(ctx, saName, metav1.GetOptions{})
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}

additionalData := map[string][]byte{
"serviceaccount_namespace": []byte(saNamespace),
"serviceaccount_name": []byte(saName),
}

for _, secret := range sa.Secrets {
Expand All @@ -40,7 +45,7 @@ func SATokenGetter(ctx context.Context, saName, saNamespace string, saClient kub
// get the token secret
tokenSecret, err := saClient.CoreV1().Secrets(saNamespace).Get(ctx, tokenSecretName, metav1.GetOptions{})
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}

if tokenSecret.Type != corev1.SecretTypeServiceAccountToken {
Expand All @@ -52,7 +57,7 @@ func SATokenGetter(ctx context.Context, saName, saNamespace string, saClient kub
continue
}

return saToken, nil, nil
return saToken, nil, additionalData, nil
}

// 8640 hour
Expand All @@ -63,19 +68,19 @@ func SATokenGetter(ctx context.Context, saName, saNamespace string, saClient kub
},
}, metav1.CreateOptions{})
if err != nil {
return nil, nil, err
return nil, nil, additionalData, err
}
expiration, err := tr.Status.ExpirationTimestamp.MarshalText()
if err != nil {
return nil, nil, nil
return nil, nil, additionalData, nil
}
return []byte(tr.Status.Token), expiration, nil
return []byte(tr.Status.Token), expiration, additionalData, nil
}
}

// SATokenCreater create the saToken of target sa.
func SATokenCreater(ctx context.Context, saName, saNamespace string, saClient kubernetes.Interface) TokenGetterFunc {
return func() ([]byte, []byte, error) {
return func() ([]byte, []byte, map[string][]byte, error) {
// 8640 hour
tr, err := saClient.CoreV1().ServiceAccounts(saNamespace).
CreateToken(ctx, saName, &authv1.TokenRequest{
Expand All @@ -84,13 +89,16 @@ func SATokenCreater(ctx context.Context, saName, saNamespace string, saClient ku
},
}, metav1.CreateOptions{})
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
expiration, err := tr.Status.ExpirationTimestamp.MarshalText()
if err != nil {
return nil, nil, nil
return nil, nil, nil, nil
}
return []byte(tr.Status.Token), expiration, nil
return []byte(tr.Status.Token), expiration, map[string][]byte{
"serviceaccount_namespace": []byte(saNamespace),
"serviceaccount_name": []byte(saName),
}, nil
}
}

Expand All @@ -106,14 +114,14 @@ func SyncKubeConfigSecret(ctx context.Context, secretName, secretNamespace, kube
return err
}

if tokenValid(secret) && clusterInfoNotChanged(secret, templateKubeconfig) {
if tokenValid(secret, tokenGetter) && clusterInfoNotChanged(secret, templateKubeconfig) {
return nil
}

return applyKubeconfigSecret(ctx, templateKubeconfig, secretName, secretNamespace, kubeconfigPath, secretClient, tokenGetter, recorder)
}

func tokenValid(secret *corev1.Secret) bool {
func tokenValid(secret *corev1.Secret, tokenGetter TokenGetterFunc) bool {
_, tokenFound := secret.Data["token"]
expiration, expirationFound := secret.Data["expiration"]

Expand All @@ -135,6 +143,17 @@ func tokenValid(secret *corev1.Secret) bool {
}
}

_, _, additionalData, err := tokenGetter()
if err != nil {
return false
}

for k, v := range additionalData {
if !bytes.Equal(secret.Data[k], v) {
return false
}
}

return true
}

Expand Down Expand Up @@ -183,7 +202,7 @@ func applyKubeconfigSecret(ctx context.Context, templateKubeconfig *rest.Config,
kubeconfigPath string, secretClient coreclientv1.SecretsGetter, tokenGetter TokenGetterFunc,
recorder events.Recorder) error {

token, expiration, err := tokenGetter()
token, expiration, additionalData, err := tokenGetter()
if err != nil {
return err
}
Expand Down Expand Up @@ -231,6 +250,10 @@ func applyKubeconfigSecret(ctx context.Context, templateKubeconfig *rest.Config,
secret.Data["expiration"] = expiration
}

for k, v := range additionalData {
secret.Data[k] = v
}

_, _, err = resourceapply.ApplySecret(ctx, secretClient, recorder, secret)
return err
}
Expand Down
91 changes: 82 additions & 9 deletions pkg/operator/helpers/sa_syncer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func TestTokenGetter(t *testing.T) {
return true, &authv1.TokenRequest{Status: authv1.TokenRequestStatus{Token: "aaa", ExpirationTimestamp: tt.expirationTime}}, nil
})
tokenGetter := SATokenGetter(context.TODO(), saName, saNamespace, client)
token, _, err := tokenGetter()
token, _, additionalData, err := tokenGetter()
fmt.Printf("client action is %v\n", client.Actions())
if err != nil && !tt.wantErr {
t.Error(err)
Expand All @@ -82,6 +82,18 @@ func TestTokenGetter(t *testing.T) {
if !reflect.DeepEqual(token, tt.wantToken) {
t.Errorf("token is not correct, got %s", string(token))
}
if !tt.wantErr {
if additionalData == nil {
t.Errorf("additional data is nil")
}
if !reflect.DeepEqual(additionalData["serviceaccount_namespace"], []byte(saNamespace)) {
t.Errorf("serviceaccount_namespace is not correct, got %s", string(additionalData["serviceaccount_namespace"]))
}
if !reflect.DeepEqual(additionalData["serviceaccount_name"], []byte(saName)) {
t.Errorf("serviceaccount_name is not correct, got %s", string(additionalData["serviceaccount_name"]))
}
}

})
}
}
Expand All @@ -100,11 +112,12 @@ func TestApplyKubeconfigSecret(t *testing.T) {
now, _ := metav1.Now().MarshalText()

tests := []struct {
name string
secrets []runtime.Object
token []byte
tokenGetError error
expiration *metav1.Time
name string
secrets []runtime.Object
token []byte
additionalData map[string][]byte
tokenGetError error
expiration *metav1.Time

validateActions func(t *testing.T, actions []clienttesting.Action)
wantErr bool
Expand Down Expand Up @@ -176,6 +189,66 @@ func TestApplyKubeconfigSecret(t *testing.T) {
t.Fatalf("expect 4 actions, but get %d", len(actions))
}

secret := actions[3].(clienttesting.CreateAction).GetObject().(*corev1.Secret)
if !reflect.DeepEqual(secret.Data["token"], []byte("aaa")) {
t.Errorf("secret update is not correct, got %v", secret.Data)
}
},
},
{
name: "update secret if additional data changed",
token: []byte("aaa"),
secrets: []runtime.Object{
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: secretNamespace,
},
Data: map[string][]byte{
"token": []byte("aaa"),
"serviceaccount_name": []byte("test-sa"),
"serviceaccount_namespace": []byte("test-ns"),
},
},
},
additionalData: map[string][]byte{
"serviceaccount_name": []byte("test-sa"),
"serviceaccount_namespace": []byte("test-ns-new"),
},
validateActions: func(t *testing.T, actions []clienttesting.Action) {
if len(actions) != 4 {
t.Fatalf("expect 4 actions, but get %d", len(actions))
}

secret := actions[3].(clienttesting.CreateAction).GetObject().(*corev1.Secret)
if !reflect.DeepEqual(secret.Data["token"], []byte("aaa")) {
t.Errorf("secret update is not correct, got %v", secret.Data)
}
},
},
{
name: "update secret if new additional data added",
token: []byte("aaa"),
secrets: []runtime.Object{
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: secretNamespace,
},
Data: map[string][]byte{
"token": []byte("aaa"),
},
},
},
additionalData: map[string][]byte{
"serviceaccount_name": []byte("test-sa"),
"serviceaccount_namespace": []byte("test-ns"),
},
validateActions: func(t *testing.T, actions []clienttesting.Action) {
if len(actions) != 4 {
t.Fatalf("expect 4 actions, but get %d", len(actions))
}

secret := actions[3].(clienttesting.CreateAction).GetObject().(*corev1.Secret)
if !reflect.DeepEqual(secret.Data["token"], []byte("aaa")) {
t.Errorf("secret update is not correct, got %v", secret.Data)
Expand All @@ -185,13 +258,13 @@ func TestApplyKubeconfigSecret(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tokenGetter := func() ([]byte, []byte, error) {
tokenGetter := func() ([]byte, []byte, map[string][]byte, error) {
if tt.expiration == nil {
return tt.token, nil, tt.tokenGetError
return tt.token, nil, tt.additionalData, tt.tokenGetError
}

expiration, _ := tt.expiration.MarshalText()
return tt.token, expiration, tt.tokenGetError
return tt.token, expiration, tt.additionalData, tt.tokenGetError
}
client := testclient.NewSimpleClientset(tt.secrets...)
err := SyncKubeConfigSecret(
Expand Down

0 comments on commit 5313ff7

Please sign in to comment.