github.com/amtisyAts/helm@v2.17.0+incompatible/pkg/storage/driver/secrets.go (about) 1 /* 2 Copyright The Helm Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package driver // import "k8s.io/helm/pkg/storage/driver" 18 19 import ( 20 "fmt" 21 "strconv" 22 "strings" 23 "time" 24 25 "k8s.io/api/core/v1" 26 apierrors "k8s.io/apimachinery/pkg/api/errors" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 kblabels "k8s.io/apimachinery/pkg/labels" 29 "k8s.io/apimachinery/pkg/util/validation" 30 corev1 "k8s.io/client-go/kubernetes/typed/core/v1" 31 32 rspb "k8s.io/helm/pkg/proto/hapi/release" 33 storageerrors "k8s.io/helm/pkg/storage/errors" 34 ) 35 36 var _ Driver = (*Secrets)(nil) 37 38 // SecretsDriverName is the string name of the driver. 39 const SecretsDriverName = "Secret" 40 41 // Secrets is a wrapper around an implementation of a kubernetes 42 // SecretsInterface. 43 type Secrets struct { 44 impl corev1.SecretInterface 45 Log func(string, ...interface{}) 46 } 47 48 // NewSecrets initializes a new Secrets wrapping an implementation of 49 // the kubernetes SecretsInterface. 50 func NewSecrets(impl corev1.SecretInterface) *Secrets { 51 return &Secrets{ 52 impl: impl, 53 Log: func(_ string, _ ...interface{}) {}, 54 } 55 } 56 57 // Name returns the name of the driver. 58 func (secrets *Secrets) Name() string { 59 return SecretsDriverName 60 } 61 62 // Get fetches the release named by key. The corresponding release is returned 63 // or error if not found. 64 func (secrets *Secrets) Get(key string) (*rspb.Release, error) { 65 // fetch the secret holding the release named by key 66 obj, err := secrets.impl.Get(key, metav1.GetOptions{}) 67 if err != nil { 68 if apierrors.IsNotFound(err) { 69 return nil, storageerrors.ErrReleaseNotFound(key) 70 } 71 72 secrets.Log("get: failed to get %q: %s", key, err) 73 return nil, err 74 } 75 // found the secret, decode the base64 data string 76 r, err := decodeRelease(string(obj.Data["release"])) 77 if err != nil { 78 secrets.Log("get: failed to decode data %q: %s", key, err) 79 return nil, err 80 } 81 // return the release object 82 return r, nil 83 } 84 85 // List fetches all releases and returns the list releases such 86 // that filter(release) == true. An error is returned if the 87 // secret fails to retrieve the releases. 88 func (secrets *Secrets) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) { 89 lsel := kblabels.Set{"OWNER": "TILLER"}.AsSelector() 90 opts := metav1.ListOptions{LabelSelector: lsel.String()} 91 92 list, err := secrets.impl.List(opts) 93 if err != nil { 94 secrets.Log("list: failed to list: %s", err) 95 return nil, err 96 } 97 98 var results []*rspb.Release 99 100 // iterate over the secrets object list 101 // and decode each release 102 for _, item := range list.Items { 103 rls, err := decodeRelease(string(item.Data["release"])) 104 if err != nil { 105 secrets.Log("list: failed to decode release: %v: %s", item, err) 106 continue 107 } 108 if filter(rls) { 109 results = append(results, rls) 110 } 111 } 112 return results, nil 113 } 114 115 // Query fetches all releases that match the provided map of labels. 116 // An error is returned if the secret fails to retrieve the releases. 117 func (secrets *Secrets) Query(labels map[string]string) ([]*rspb.Release, error) { 118 ls := kblabels.Set{} 119 for k, v := range labels { 120 if errs := validation.IsValidLabelValue(v); len(errs) != 0 { 121 return nil, fmt.Errorf("invalid label value: %q: %s", v, strings.Join(errs, "; ")) 122 } 123 ls[k] = v 124 } 125 126 opts := metav1.ListOptions{LabelSelector: ls.AsSelector().String()} 127 128 list, err := secrets.impl.List(opts) 129 if err != nil { 130 secrets.Log("query: failed to query with labels: %s", err) 131 return nil, err 132 } 133 134 if len(list.Items) == 0 { 135 return nil, storageerrors.ErrReleaseNotFound(labels["NAME"]) 136 } 137 138 var results []*rspb.Release 139 for _, item := range list.Items { 140 rls, err := decodeRelease(string(item.Data["release"])) 141 if err != nil { 142 secrets.Log("query: failed to decode release: %s", err) 143 continue 144 } 145 results = append(results, rls) 146 } 147 return results, nil 148 } 149 150 // Create creates a new Secret holding the release. If the 151 // Secret already exists, ErrReleaseExists is returned. 152 func (secrets *Secrets) Create(key string, rls *rspb.Release) error { 153 // set labels for secrets object meta data 154 var lbs labels 155 156 lbs.init() 157 lbs.set("CREATED_AT", strconv.Itoa(int(time.Now().Unix()))) 158 159 // create a new secret to hold the release 160 obj, err := newSecretsObject(key, rls, lbs) 161 if err != nil { 162 secrets.Log("create: failed to encode release %q: %s", rls.Name, err) 163 return err 164 } 165 // push the secret object out into the kubiverse 166 if _, err := secrets.impl.Create(obj); err != nil { 167 if apierrors.IsAlreadyExists(err) { 168 return storageerrors.ErrReleaseExists(rls.Name) 169 } 170 171 secrets.Log("create: failed to create: %s", err) 172 return err 173 } 174 return nil 175 } 176 177 // Update updates the Secret holding the release. If not found 178 // the Secret is created to hold the release. 179 func (secrets *Secrets) Update(key string, rls *rspb.Release) error { 180 // set labels for secrets object meta data 181 var lbs labels 182 183 lbs.init() 184 lbs.set("MODIFIED_AT", strconv.Itoa(int(time.Now().Unix()))) 185 186 // create a new secret object to hold the release 187 obj, err := newSecretsObject(key, rls, lbs) 188 if err != nil { 189 secrets.Log("update: failed to encode release %q: %s", rls.Name, err) 190 return err 191 } 192 // push the secret object out into the kubiverse 193 _, err = secrets.impl.Update(obj) 194 if err != nil { 195 secrets.Log("update: failed to update: %s", err) 196 return err 197 } 198 return nil 199 } 200 201 // Delete deletes the Secret holding the release named by key. 202 func (secrets *Secrets) Delete(key string) (rls *rspb.Release, err error) { 203 // fetch the release to check existence 204 if rls, err = secrets.Get(key); err != nil { 205 if apierrors.IsNotFound(err) { 206 return nil, storageerrors.ErrReleaseExists(rls.Name) 207 } 208 209 secrets.Log("delete: failed to get release %q: %s", key, err) 210 return nil, err 211 } 212 // delete the release 213 if err = secrets.impl.Delete(key, &metav1.DeleteOptions{}); err != nil { 214 return rls, err 215 } 216 return rls, nil 217 } 218 219 // newSecretsObject constructs a kubernetes Secret object 220 // to store a release. Each secret data entry is the base64 221 // encoded string of a release's binary protobuf encoding. 222 // 223 // The following labels are used within each secret: 224 // 225 // "MODIFIED_AT" - timestamp indicating when this secret was last modified. (set in Update) 226 // "CREATED_AT" - timestamp indicating when this secret was created. (set in Create) 227 // "VERSION" - version of the release. 228 // "STATUS" - status of the release (see proto/hapi/release.status.pb.go for variants) 229 // "OWNER" - owner of the secret, currently "TILLER". 230 // "NAME" - name of the release. 231 // 232 func newSecretsObject(key string, rls *rspb.Release, lbs labels) (*v1.Secret, error) { 233 const owner = "TILLER" 234 235 // encode the release 236 s, err := encodeRelease(rls) 237 if err != nil { 238 return nil, err 239 } 240 241 if lbs == nil { 242 lbs.init() 243 } 244 245 // apply labels 246 lbs.set("NAME", rls.Name) 247 lbs.set("OWNER", owner) 248 lbs.set("STATUS", rspb.Status_Code_name[int32(rls.Info.Status.Code)]) 249 lbs.set("VERSION", strconv.Itoa(int(rls.Version))) 250 251 // create and return secret object 252 return &v1.Secret{ 253 ObjectMeta: metav1.ObjectMeta{ 254 Name: key, 255 Labels: lbs.toMap(), 256 }, 257 Data: map[string][]byte{"release": []byte(s)}, 258 }, nil 259 }