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  }