github.com/doitroot/helm@v3.0.0-beta.3+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 "helm.sh/helm/pkg/storage/driver" 18 19 import ( 20 "strconv" 21 "strings" 22 "time" 23 24 "github.com/pkg/errors" 25 v1 "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 "helm.sh/helm/pkg/release" 33 ) 34 35 var _ Driver = (*Secrets)(nil) 36 37 // SecretsDriverName is the string name of the driver. 38 const SecretsDriverName = "Secret" 39 40 // Secrets is a wrapper around an implementation of a kubernetes 41 // SecretsInterface. 42 type Secrets struct { 43 impl corev1.SecretInterface 44 Log func(string, ...interface{}) 45 } 46 47 // NewSecrets initializes a new Secrets wrapping an implmenetation of 48 // the kubernetes SecretsInterface. 49 func NewSecrets(impl corev1.SecretInterface) *Secrets { 50 return &Secrets{ 51 impl: impl, 52 Log: func(_ string, _ ...interface{}) {}, 53 } 54 } 55 56 // Name returns the name of the driver. 57 func (secrets *Secrets) Name() string { 58 return SecretsDriverName 59 } 60 61 // Get fetches the release named by key. The corresponding release is returned 62 // or error if not found. 63 func (secrets *Secrets) Get(key string) (*rspb.Release, error) { 64 // fetch the secret holding the release named by key 65 obj, err := secrets.impl.Get(key, metav1.GetOptions{}) 66 if err != nil { 67 if apierrors.IsNotFound(err) { 68 return nil, ErrReleaseNotFound 69 } 70 return nil, errors.Wrapf(err, "get: failed to get %q", key) 71 } 72 // found the secret, decode the base64 data string 73 r, err := decodeRelease(string(obj.Data["release"])) 74 return r, errors.Wrapf(err, "get: failed to decode data %q", key) 75 } 76 77 // List fetches all releases and returns the list releases such 78 // that filter(release) == true. An error is returned if the 79 // secret fails to retrieve the releases. 80 func (secrets *Secrets) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) { 81 lsel := kblabels.Set{"owner": "helm"}.AsSelector() 82 opts := metav1.ListOptions{LabelSelector: lsel.String()} 83 84 list, err := secrets.impl.List(opts) 85 if err != nil { 86 return nil, errors.Wrap(err, "list: failed to list") 87 } 88 89 var results []*rspb.Release 90 91 // iterate over the secrets object list 92 // and decode each release 93 for _, item := range list.Items { 94 rls, err := decodeRelease(string(item.Data["release"])) 95 if err != nil { 96 secrets.Log("list: failed to decode release: %v: %s", item, err) 97 continue 98 } 99 if filter(rls) { 100 results = append(results, rls) 101 } 102 } 103 return results, nil 104 } 105 106 // Query fetches all releases that match the provided map of labels. 107 // An error is returned if the secret fails to retrieve the releases. 108 func (secrets *Secrets) Query(labels map[string]string) ([]*rspb.Release, error) { 109 ls := kblabels.Set{} 110 for k, v := range labels { 111 if errs := validation.IsValidLabelValue(v); len(errs) != 0 { 112 return nil, errors.Errorf("invalid label value: %q: %s", v, strings.Join(errs, "; ")) 113 } 114 ls[k] = v 115 } 116 117 opts := metav1.ListOptions{LabelSelector: ls.AsSelector().String()} 118 119 list, err := secrets.impl.List(opts) 120 if err != nil { 121 return nil, errors.Wrap(err, "query: failed to query with labels") 122 } 123 124 if len(list.Items) == 0 { 125 return nil, ErrReleaseNotFound 126 } 127 128 var results []*rspb.Release 129 for _, item := range list.Items { 130 rls, err := decodeRelease(string(item.Data["release"])) 131 if err != nil { 132 secrets.Log("query: failed to decode release: %s", err) 133 continue 134 } 135 results = append(results, rls) 136 } 137 return results, nil 138 } 139 140 // Create creates a new Secret holding the release. If the 141 // Secret already exists, ErrReleaseExists is returned. 142 func (secrets *Secrets) Create(key string, rls *rspb.Release) error { 143 // set labels for secrets object meta data 144 var lbs labels 145 146 lbs.init() 147 lbs.set("createdAt", strconv.Itoa(int(time.Now().Unix()))) 148 149 // create a new secret to hold the release 150 obj, err := newSecretsObject(key, rls, lbs) 151 if err != nil { 152 return errors.Wrapf(err, "create: failed to encode release %q", rls.Name) 153 } 154 // push the secret object out into the kubiverse 155 if _, err := secrets.impl.Create(obj); err != nil { 156 if apierrors.IsAlreadyExists(err) { 157 return ErrReleaseExists 158 } 159 160 return errors.Wrap(err, "create: failed to create") 161 } 162 return nil 163 } 164 165 // Update updates the Secret holding the release. If not found 166 // the Secret is created to hold the release. 167 func (secrets *Secrets) Update(key string, rls *rspb.Release) error { 168 // set labels for secrets object meta data 169 var lbs labels 170 171 lbs.init() 172 lbs.set("modifiedAt", strconv.Itoa(int(time.Now().Unix()))) 173 174 // create a new secret object to hold the release 175 obj, err := newSecretsObject(key, rls, lbs) 176 if err != nil { 177 return errors.Wrapf(err, "update: failed to encode release %q", rls.Name) 178 } 179 // push the secret object out into the kubiverse 180 _, err = secrets.impl.Update(obj) 181 return errors.Wrap(err, "update: failed to update") 182 } 183 184 // Delete deletes the Secret holding the release named by key. 185 func (secrets *Secrets) Delete(key string) (rls *rspb.Release, err error) { 186 // fetch the release to check existence 187 if rls, err = secrets.Get(key); err != nil { 188 if apierrors.IsNotFound(err) { 189 return nil, ErrReleaseExists 190 } 191 192 return nil, errors.Wrapf(err, "delete: failed to get release %q", key) 193 } 194 // delete the release 195 err = secrets.impl.Delete(key, &metav1.DeleteOptions{}) 196 return rls, err 197 } 198 199 // newSecretsObject constructs a kubernetes Secret object 200 // to store a release. Each secret data entry is the base64 201 // encoded string of a release's binary protobuf encoding. 202 // 203 // The following labels are used within each secret: 204 // 205 // "modifiedAt" - timestamp indicating when this secret was last modified. (set in Update) 206 // "createdAt" - timestamp indicating when this secret was created. (set in Create) 207 // "version" - version of the release. 208 // "status" - status of the release (see proto/hapi/release.status.pb.go for variants) 209 // "owner" - owner of the secret, currently "helm". 210 // "name" - name of the release. 211 // 212 func newSecretsObject(key string, rls *rspb.Release, lbs labels) (*v1.Secret, error) { 213 const owner = "helm" 214 215 // encode the release 216 s, err := encodeRelease(rls) 217 if err != nil { 218 return nil, err 219 } 220 221 if lbs == nil { 222 lbs.init() 223 } 224 225 // apply labels 226 lbs.set("name", rls.Name) 227 lbs.set("owner", owner) 228 lbs.set("status", rls.Info.Status.String()) 229 lbs.set("version", strconv.Itoa(rls.Version)) 230 231 // create and return secret object 232 return &v1.Secret{ 233 ObjectMeta: metav1.ObjectMeta{ 234 Name: key, 235 Labels: lbs.toMap(), 236 }, 237 Type: "helm.sh/release", 238 Data: map[string][]byte{"release": []byte(s)}, 239 }, nil 240 }