github.com/banzaicloud/operator-tools@v0.28.10/pkg/secret/secretgetter.go (about) 1 // Copyright © 2022 Banzai Cloud 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package secret 16 17 import ( 18 "context" 19 "time" 20 21 "emperror.dev/errors" 22 23 corev1 "k8s.io/api/core/v1" 24 k8sErrors "k8s.io/apimachinery/pkg/api/errors" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/types" 27 "k8s.io/apimachinery/pkg/util/wait" 28 "sigs.k8s.io/controller-runtime/pkg/client" 29 ) 30 31 type SecretGetter interface { 32 Get(objectKey client.ObjectKey) (K8sSecret, error) 33 } 34 35 type K8sSecret interface { 36 GetToken() []byte 37 GetCACert() []byte 38 } 39 40 type readerSecretGetter struct { 41 client client.Client 42 // Backoff wait for reader secret to be created 43 backoff *wait.Backoff 44 } 45 46 type ReaderSecretGetterOption = func(r *readerSecretGetter) 47 48 func WithBackOff(backoff *wait.Backoff) ReaderSecretGetterOption { 49 return func(rsG *readerSecretGetter) { 50 rsG.backoff = backoff 51 } 52 } 53 54 var defaultBackoffWaitSecret = &wait.Backoff{ 55 Duration: time.Second * 3, 56 Factor: 1, 57 Jitter: 0, 58 Steps: 3, 59 } 60 61 func NewReaderSecretGetter(client client.Client, opts ...ReaderSecretGetterOption) (SecretGetter, error) { 62 rsGetter := &readerSecretGetter{client: client} 63 64 if rsGetter.client == nil { 65 return nil, errors.New("k8s client should be set for reader-secret getter") 66 } 67 68 for _, opt := range opts { 69 opt(rsGetter) 70 } 71 72 if rsGetter.backoff == nil { 73 rsGetter.backoff = defaultBackoffWaitSecret 74 } 75 76 return rsGetter, nil 77 } 78 79 type readerSecret struct { 80 Token []byte 81 CACert []byte 82 } 83 84 func (r *readerSecret) GetToken() []byte { 85 return r.Token 86 } 87 88 func (r *readerSecret) GetCACert() []byte { 89 return r.CACert 90 } 91 92 func (r *readerSecretGetter) Get(objectKey client.ObjectKey) (K8sSecret, error) { 93 ctx := context.Background() 94 95 // fetch SA object so that we can get the Secret object that relates to the SA 96 saObjectKey := objectKey 97 sa, err := r.getReaderSecretServiceAccount(ctx, saObjectKey) 98 if err != nil { 99 return nil, errors.WrapIf(err, "error getting reader secret") 100 } 101 102 // After K8s v1.24, Secret objects containing ServiceAccount tokens are no longer auto-generated, so we will have to 103 // manually create Secret in order to get the token. 104 // Reference: https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.24.md#no-really-you-must-read-this-before-you-upgrade 105 return r.getOrCreateReaderSecretWithServiceAccount(ctx, sa) 106 } 107 108 func (r *readerSecretGetter) getReaderSecretServiceAccount(ctx context.Context, saObjectKey client.ObjectKey) (*corev1.ServiceAccount, error) { 109 sa := &corev1.ServiceAccount{} 110 err := r.client.Get(ctx, saObjectKey, sa) 111 if err != nil { 112 return nil, errors.WrapIff(err, "error getting service account object, service account name: %s, service account namespace: %s", 113 saObjectKey.Name, 114 saObjectKey.Namespace) 115 } 116 117 return sa, nil 118 } 119 120 func (r *readerSecretGetter) getOrCreateReaderSecretWithServiceAccount(ctx context.Context, sa *corev1.ServiceAccount) (K8sSecret, error) { 121 secretObj := &corev1.Secret{} 122 123 readerSecretName := sa.Name + "-token" 124 if len(sa.Secrets) != 0 { 125 readerSecretName = sa.Secrets[0].Name 126 } 127 secretObjRef := types.NamespacedName{ 128 Namespace: sa.Namespace, 129 Name: readerSecretName, 130 } 131 132 err := r.client.Get(ctx, secretObjRef, secretObj) 133 if err != nil && k8sErrors.IsNotFound(err) { 134 secretObj = &corev1.Secret{ 135 ObjectMeta: metav1.ObjectMeta{ 136 Namespace: secretObjRef.Namespace, 137 Name: secretObjRef.Name, 138 Annotations: map[string]string{ 139 "kubernetes.io/service-account.name": sa.Name, 140 }, 141 }, 142 Type: "kubernetes.io/service-account-token", 143 } 144 145 err = r.client.Create(ctx, secretObj) 146 if err != nil { 147 return nil, errors.WrapIfWithDetails(err, "creating kubernetes secret failed", "namespace", 148 secretObjRef.Namespace, 149 "secret name", 150 secretObjRef.Name) 151 } 152 153 return r.waitAndGetReaderSecret(ctx, secretObjRef.Namespace, secretObjRef.Name) 154 } 155 156 readerSecret := &readerSecret{ 157 Token: secretObj.Data["token"], 158 CACert: secretObj.Data["ca.crt"], 159 } 160 161 return readerSecret, nil 162 } 163 164 func (r *readerSecretGetter) waitAndGetReaderSecret(ctx context.Context, secretNamespace string, secretName string) (K8sSecret, error) { 165 var token, caCert []byte 166 167 secretObjRef := types.NamespacedName{ 168 Namespace: secretNamespace, 169 Name: secretName, 170 } 171 172 backoffWaitForSecretCreation := *r.backoff 173 err := wait.ExponentialBackoff(backoffWaitForSecretCreation, func() (bool, error) { 174 tokenSecret := &corev1.Secret{} 175 err := r.client.Get(ctx, secretObjRef, tokenSecret) 176 if err != nil { 177 if k8sErrors.IsNotFound(err) { 178 return false, nil 179 } 180 181 return false, err 182 } 183 184 token = tokenSecret.Data["token"] 185 caCert = tokenSecret.Data["ca.crt"] 186 187 if token == nil || caCert == nil { 188 return false, nil 189 } 190 191 return true, nil 192 }) 193 194 readerSecret := &readerSecret{ 195 Token: token, 196 CACert: caCert, 197 } 198 199 return readerSecret, errors.WrapIfWithDetails(err, "fail to wait for the token and CA cert to be generated", 200 "secret namespace", secretObjRef.Namespace, "secret name", secretObjRef.Name) 201 }