github.com/wangchanggan/helm@v0.0.0-20211020154240-11b1b7d5406d/pkg/storage/driver/cfgmaps.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 = (*ConfigMaps)(nil) 37 38 // ConfigMapsDriverName is the string name of the driver. 39 const ConfigMapsDriverName = "ConfigMap" 40 41 // ConfigMaps is a wrapper around an implementation of a kubernetes 42 // ConfigMapsInterface. 43 type ConfigMaps struct { 44 impl corev1.ConfigMapInterface 45 Log func(string, ...interface{}) 46 } 47 48 // NewConfigMaps initializes a new ConfigMaps wrapping an implementation of 49 // the kubernetes ConfigMapsInterface. 50 func NewConfigMaps(impl corev1.ConfigMapInterface) *ConfigMaps { 51 return &ConfigMaps{ 52 impl: impl, 53 Log: func(_ string, _ ...interface{}) {}, 54 } 55 } 56 57 // Name returns the name of the driver. 58 func (cfgmaps *ConfigMaps) Name() string { 59 return ConfigMapsDriverName 60 } 61 62 // Get fetches the release named by key. The corresponding release is returned 63 // or error if not found. 64 func (cfgmaps *ConfigMaps) Get(key string) (*rspb.Release, error) { 65 // fetch the configmap holding the release named by key 66 obj, err := cfgmaps.impl.Get(key, metav1.GetOptions{}) 67 if err != nil { 68 if apierrors.IsNotFound(err) { 69 return nil, storageerrors.ErrReleaseNotFound(key) 70 } 71 72 cfgmaps.Log("get: failed to get %q: %s", key, err) 73 return nil, err 74 } 75 // found the configmap, decode the base64 data string 76 r, err := decodeRelease(obj.Data["release"]) 77 if err != nil { 78 cfgmaps.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 // configmap fails to retrieve the releases. 88 // 如果filter release的值是true,则获取并返回所有Release 89 // 如果ConfigMap未能找到对应的Release,则返回error 90 func (cfgmaps *ConfigMaps) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) { 91 // 使用OWNER:TILLER作为标签在kube-system命名空间下查询对应的ConfigMap 92 lsel := kblabels.Set{"OWNER": "TILLER"}.AsSelector() 93 opts := metav1.ListOptions{LabelSelector: lsel.String()} 94 95 list, err := cfgmaps.impl.List(opts) 96 if err != nil { 97 cfgmaps.Log("list: failed to list: %s", err) 98 return nil, err 99 } 100 101 var results []*rspb.Release 102 103 // iterate over the configmaps object list 104 // and decode each release 105 // 遍历configmap对象列表并解码每个Release 106 for _, item := range list.Items { 107 // 将ConfigMap中的内容解码。 108 rls, err := decodeRelease(item.Data["release"]) 109 if err != nil { 110 cfgmaps.Log("list: failed to decode release: %v: %s", item, err) 111 continue 112 } 113 // 转换为Release结构体后返回前端。 114 if filter(rls) { 115 results = append(results, rls) 116 } 117 } 118 return results, nil 119 } 120 121 // Query fetches all releases that match the provided map of labels. 122 // An error is returned if the configmap fails to retrieve the releases. 123 func (cfgmaps *ConfigMaps) Query(labels map[string]string) ([]*rspb.Release, error) { 124 ls := kblabels.Set{} 125 for k, v := range labels { 126 if errs := validation.IsValidLabelValue(v); len(errs) != 0 { 127 return nil, fmt.Errorf("invalid label value: %q: %s", v, strings.Join(errs, "; ")) 128 } 129 ls[k] = v 130 } 131 132 opts := metav1.ListOptions{LabelSelector: ls.AsSelector().String()} 133 134 list, err := cfgmaps.impl.List(opts) 135 if err != nil { 136 cfgmaps.Log("query: failed to query with labels: %s", err) 137 return nil, err 138 } 139 140 if len(list.Items) == 0 { 141 return nil, storageerrors.ErrReleaseNotFound(labels["NAME"]) 142 } 143 144 var results []*rspb.Release 145 for _, item := range list.Items { 146 rls, err := decodeRelease(item.Data["release"]) 147 if err != nil { 148 cfgmaps.Log("query: failed to decode release: %s", err) 149 continue 150 } 151 results = append(results, rls) 152 } 153 return results, nil 154 } 155 156 // Create creates a new ConfigMap holding the release. If the 157 // ConfigMap already exists, ErrReleaseExists is returned. 158 func (cfgmaps *ConfigMaps) Create(key string, rls *rspb.Release) error { 159 // set labels for configmaps object meta data 160 var lbs labels 161 162 lbs.init() 163 lbs.set("CREATED_AT", strconv.Itoa(int(time.Now().Unix()))) 164 165 // create a new configmap to hold the release 166 obj, err := newConfigMapsObject(key, rls, lbs) 167 if err != nil { 168 cfgmaps.Log("create: failed to encode release %q: %s", rls.Name, err) 169 return err 170 } 171 // push the configmap object out into the kubiverse 172 if _, err := cfgmaps.impl.Create(obj); err != nil { 173 if apierrors.IsAlreadyExists(err) { 174 return storageerrors.ErrReleaseExists(key) 175 } 176 177 cfgmaps.Log("create: failed to create: %s", err) 178 return err 179 } 180 return nil 181 } 182 183 // Update updates the ConfigMap holding the release. If not found 184 // the ConfigMap is created to hold the release. 185 func (cfgmaps *ConfigMaps) Update(key string, rls *rspb.Release) error { 186 // set labels for configmaps object meta data 187 var lbs labels 188 189 lbs.init() 190 lbs.set("MODIFIED_AT", strconv.Itoa(int(time.Now().Unix()))) 191 192 // create a new configmap object to hold the release 193 obj, err := newConfigMapsObject(key, rls, lbs) 194 if err != nil { 195 cfgmaps.Log("update: failed to encode release %q: %s", rls.Name, err) 196 return err 197 } 198 // push the configmap object out into the kubiverse 199 _, err = cfgmaps.impl.Update(obj) 200 if err != nil { 201 cfgmaps.Log("update: failed to update: %s", err) 202 return err 203 } 204 return nil 205 } 206 207 // Delete deletes the ConfigMap holding the release named by key. 208 func (cfgmaps *ConfigMaps) Delete(key string) (rls *rspb.Release, err error) { 209 // fetch the release to check existence 210 if rls, err = cfgmaps.Get(key); err != nil { 211 if apierrors.IsNotFound(err) { 212 return nil, storageerrors.ErrReleaseExists(rls.Name) 213 } 214 215 cfgmaps.Log("delete: failed to get release %q: %s", key, err) 216 return nil, err 217 } 218 // delete the release 219 if err = cfgmaps.impl.Delete(key, &metav1.DeleteOptions{}); err != nil { 220 return rls, err 221 } 222 return rls, nil 223 } 224 225 // newConfigMapsObject constructs a kubernetes ConfigMap object 226 // to store a release. Each configmap data entry is the base64 227 // encoded string of a release's binary protobuf encoding. 228 // 229 // The following labels are used within each configmap: 230 // 231 // "MODIFIED_AT" - timestamp indicating when this configmap was last modified. (set in Update) 232 // "CREATED_AT" - timestamp indicating when this configmap was created. (set in Create) 233 // "VERSION" - version of the release. 234 // "STATUS" - status of the release (see proto/hapi/release.status.pb.go for variants) 235 // "OWNER" - owner of the configmap, currently "TILLER". 236 // "NAME" - name of the release. 237 // 238 func newConfigMapsObject(key string, rls *rspb.Release, lbs labels) (*v1.ConfigMap, error) { 239 const owner = "TILLER" 240 241 // encode the release 242 s, err := encodeRelease(rls) 243 if err != nil { 244 return nil, err 245 } 246 247 if lbs == nil { 248 lbs.init() 249 } 250 251 // apply labels 252 lbs.set("NAME", rls.Name) 253 lbs.set("OWNER", owner) 254 lbs.set("STATUS", rspb.Status_Code_name[int32(rls.Info.Status.Code)]) 255 lbs.set("VERSION", strconv.Itoa(int(rls.Version))) 256 257 // create and return configmap object 258 return &v1.ConfigMap{ 259 ObjectMeta: metav1.ObjectMeta{ 260 Name: key, 261 Labels: lbs.toMap(), 262 }, 263 Data: map[string]string{"release": s}, 264 }, nil 265 }