github.com/danielqsj/helm@v2.0.0-alpha.4.0.20160908204436-976e0ba5199b+incompatible/pkg/storage/driver/cfgmaps.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors All rights reserved. 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 "encoding/base64" 21 "fmt" 22 "log" 23 "strconv" 24 "time" 25 26 "github.com/golang/protobuf/proto" 27 28 rspb "k8s.io/helm/pkg/proto/hapi/release" 29 30 "k8s.io/kubernetes/pkg/api" 31 kberrs "k8s.io/kubernetes/pkg/api/errors" 32 client "k8s.io/kubernetes/pkg/client/unversioned" 33 ) 34 35 // ConfigMapsDriverName is the string name of the driver. 36 const ConfigMapsDriverName = "ConfigMap" 37 38 var b64 = base64.StdEncoding 39 40 // labels is a map of key value pairs to be included as metadata in a configmap object. 41 type labels map[string]string 42 43 func (lbs *labels) init() { *lbs = labels(make(map[string]string)) } 44 func (lbs labels) get(key string) string { return lbs[key] } 45 func (lbs labels) set(key, val string) { lbs[key] = val } 46 func (lbs labels) toMap() map[string]string { return lbs } 47 48 // ConfigMaps is a wrapper around an implementation of a kubernetes 49 // ConfigMapsInterface. 50 type ConfigMaps struct { 51 impl client.ConfigMapsInterface 52 } 53 54 // NewConfigMaps initializes a new ConfigMaps wrapping an implmenetation of 55 // the kubernetes ConfigMapsInterface. 56 func NewConfigMaps(impl client.ConfigMapsInterface) *ConfigMaps { 57 return &ConfigMaps{impl: impl} 58 } 59 60 // Name returns the name of the driver. 61 func (cfgmaps *ConfigMaps) Name() string { 62 return ConfigMapsDriverName 63 } 64 65 // Get fetches the release named by key. The corresponding release is returned 66 // or error if not found. 67 func (cfgmaps *ConfigMaps) Get(key string) (*rspb.Release, error) { 68 // fetch the configmap holding the release named by key 69 obj, err := cfgmaps.impl.Get(key) 70 if err != nil { 71 if kberrs.IsNotFound(err) { 72 return nil, ErrReleaseNotFound 73 } 74 75 logerrf(err, "get: failed to get %q", key) 76 return nil, err 77 } 78 // found the configmap, decode the base64 data string 79 r, err := decodeRelease(obj.Data["release"]) 80 if err != nil { 81 logerrf(err, "get: failed to decode data %q", key) 82 return nil, err 83 } 84 // return the release object 85 return r, nil 86 } 87 88 // List fetches all releases and returns the list releases such 89 // that filter(release) == true. An error is returned if the 90 // configmap fails to retrieve the releases. 91 func (cfgmaps *ConfigMaps) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) { 92 list, err := cfgmaps.impl.List(api.ListOptions{}) 93 if err != nil { 94 logerrf(err, "list: failed to list") 95 return nil, err 96 } 97 98 var results []*rspb.Release 99 100 // iterate over the configmaps object list 101 // and decode each release 102 for _, item := range list.Items { 103 rls, err := decodeRelease(item.Data["release"]) 104 if err != nil { 105 logerrf(err, "list: failed to decode release: %s", rls) 106 continue 107 } 108 if filter(rls) { 109 results = append(results, rls) 110 } 111 } 112 return results, nil 113 } 114 115 // Create creates a new ConfigMap holding the release. If the 116 // ConfigMap already exists, ErrReleaseExists is returned. 117 func (cfgmaps *ConfigMaps) Create(rls *rspb.Release) error { 118 // set labels for configmaps object meta data 119 var lbs labels 120 121 lbs.init() 122 lbs.set("CREATED_AT", strconv.Itoa(int(time.Now().Unix()))) 123 124 // create a new configmap to hold the release 125 obj, err := newConfigMapsObject(rls, lbs) 126 if err != nil { 127 logerrf(err, "create: failed to encode release %q", rls.Name) 128 return err 129 } 130 // push the configmap object out into the kubiverse 131 if _, err := cfgmaps.impl.Create(obj); err != nil { 132 if kberrs.IsAlreadyExists(err) { 133 return ErrReleaseExists 134 } 135 136 logerrf(err, "create: failed to create") 137 return err 138 } 139 return nil 140 } 141 142 // Update updates the ConfigMap holding the release. If not found 143 // the ConfigMap is created to hold the release. 144 func (cfgmaps *ConfigMaps) Update(rls *rspb.Release) error { 145 // set labels for configmaps object meta data 146 var lbs labels 147 148 lbs.init() 149 lbs.set("MODIFIED_AT", strconv.Itoa(int(time.Now().Unix()))) 150 151 // create a new configmap object to hold the release 152 obj, err := newConfigMapsObject(rls, lbs) 153 if err != nil { 154 logerrf(err, "update: failed to encode release %q", rls.Name) 155 return err 156 } 157 // push the configmap object out into the kubiverse 158 _, err = cfgmaps.impl.Update(obj) 159 if err != nil { 160 logerrf(err, "update: failed to update") 161 return err 162 } 163 return nil 164 } 165 166 // Delete deletes the ConfigMap holding the release named by key. 167 func (cfgmaps *ConfigMaps) Delete(key string) (rls *rspb.Release, err error) { 168 // fetch the release to check existence 169 if rls, err = cfgmaps.Get(key); err != nil { 170 if kberrs.IsNotFound(err) { 171 return nil, ErrReleaseNotFound 172 } 173 174 logerrf(err, "delete: failed to get release %q", rls.Name) 175 return nil, err 176 } 177 // delete the release 178 if err = cfgmaps.impl.Delete(key); err != nil { 179 return rls, err 180 } 181 return rls, nil 182 } 183 184 // newConfigMapsObject constructs a kubernetes ConfigMap object 185 // to store a release. Each configmap data entry is the base64 186 // encoded string of a release's binary protobuf encoding. 187 // 188 // The following labels are used within each configmap: 189 // 190 // "MODIFIED_AT" - timestamp indicating when this configmap was last modified. (set in Update) 191 // "CREATED_AT" - timestamp indicating when this configmap was created. (set in Create) 192 // "VERSION" - version of the release. 193 // "STATUS" - status of the release (see proto/hapi/release.status.pb.go for variants) 194 // "OWNER" - owner of the configmap, currently "TILLER". 195 // "NAME" - name of the release. 196 // 197 func newConfigMapsObject(rls *rspb.Release, lbs labels) (*api.ConfigMap, error) { 198 const owner = "TILLER" 199 200 // encode the release 201 s, err := encodeRelease(rls) 202 if err != nil { 203 return nil, err 204 } 205 206 if lbs == nil { 207 lbs.init() 208 } 209 210 // apply labels 211 lbs.set("NAME", rls.Name) 212 lbs.set("OWNER", owner) 213 lbs.set("STATUS", rspb.Status_Code_name[int32(rls.Info.Status.Code)]) 214 lbs.set("VERSION", strconv.Itoa(int(rls.Version))) 215 216 // create and return configmap object 217 return &api.ConfigMap{ 218 ObjectMeta: api.ObjectMeta{ 219 Name: rls.Name, 220 Labels: lbs.toMap(), 221 }, 222 Data: map[string]string{"release": s}, 223 }, nil 224 } 225 226 // encodeRelease encodes a release returning a base64 encoded 227 // binary protobuf encoding representation, or error. 228 func encodeRelease(rls *rspb.Release) (string, error) { 229 b, err := proto.Marshal(rls) 230 if err != nil { 231 return "", err 232 } 233 return b64.EncodeToString(b), nil 234 } 235 236 // decodeRelease decodes the bytes in data into a release 237 // type. Data must contain a base64 encoded string of a 238 // valid protobuf encoding of a release, otherwise 239 // an error is returned. 240 func decodeRelease(data string) (*rspb.Release, error) { 241 // base64 decode string 242 b, err := b64.DecodeString(data) 243 if err != nil { 244 return nil, err 245 } 246 247 var rls rspb.Release 248 // unmarshal protobuf bytes 249 if err := proto.Unmarshal(b, &rls); err != nil { 250 return nil, err 251 } 252 return &rls, nil 253 } 254 255 // logerrf wraps an error with the a formatted string (used for debugging) 256 func logerrf(err error, format string, args ...interface{}) { 257 log.Printf("configmaps: %s: %s\n", fmt.Sprintf(format, args...), err) 258 }