github.com/SupersunnySea/draft@v0.16.0/pkg/storage/kube/configmap/store.go (about)

     1  package configmap
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	"github.com/Azure/draft/pkg/storage"
     8  	"github.com/golang/protobuf/ptypes"
     9  	"k8s.io/api/core/v1"
    10  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    11  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
    13  )
    14  
    15  // ConfigMaps represents a Kubernetes configmap storage engine for a storage.Object .
    16  type ConfigMaps struct {
    17  	impl corev1.ConfigMapInterface
    18  }
    19  
    20  // compile-time guarantee that *ConfigMaps implements storage.Store
    21  var _ storage.Store = (*ConfigMaps)(nil)
    22  
    23  // NewConfigMaps returns an implementation of storage.Store backed by kubernetes
    24  // ConfigMap objects to store draft application build context.
    25  func NewConfigMaps(impl corev1.ConfigMapInterface) *ConfigMaps {
    26  	return &ConfigMaps{impl}
    27  }
    28  
    29  // DeleteBuilds deletes all draft builds for the application specified by appName.
    30  //
    31  // DeleteBuilds implements storage.Deleter.
    32  func (s *ConfigMaps) DeleteBuilds(ctx context.Context, appName string) ([]*storage.Object, error) {
    33  	builds, err := s.GetBuilds(ctx, appName)
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  	err = s.impl.Delete(appName, &metav1.DeleteOptions{})
    38  	return builds, err
    39  }
    40  
    41  // DeleteBuild deletes the draft build given by buildID for the application specified by appName.
    42  //
    43  // DeleteBuild implements storage.Deleter.
    44  func (s *ConfigMaps) DeleteBuild(ctx context.Context, appName, buildID string) (obj *storage.Object, err error) {
    45  	var cfgmap *v1.ConfigMap
    46  	if cfgmap, err = s.impl.Get(appName, metav1.GetOptions{}); err != nil {
    47  		if apierrors.IsNotFound(err) {
    48  			return nil, storage.NewErrAppStorageNotFound(appName)
    49  		}
    50  		return nil, err
    51  	}
    52  	if build, ok := cfgmap.Data[buildID]; ok {
    53  		if obj, err = storage.DecodeString(build); err != nil {
    54  			return nil, err
    55  		}
    56  		delete(cfgmap.Data, buildID)
    57  		_, err = s.impl.Update(cfgmap)
    58  		return obj, err
    59  	}
    60  	return nil, storage.NewErrAppBuildNotFound(appName, buildID)
    61  }
    62  
    63  // CreateBuild creates new storage for the application specified by appName to include build.
    64  //
    65  // If the configmap storage already exists for the application, ErrAppStorageExists is returned.
    66  //
    67  // CreateBuild implements storage.Creater.
    68  func (s *ConfigMaps) CreateBuild(ctx context.Context, appName string, build *storage.Object) error {
    69  	now, err := ptypes.TimestampProto(time.Now())
    70  	if err != nil {
    71  		return err
    72  	}
    73  	build.CreatedAt = now
    74  
    75  	cfgmap, err := newConfigMap(appName, build)
    76  	if err != nil {
    77  		return err
    78  	}
    79  	if _, err = s.impl.Create(cfgmap); err != nil {
    80  		if apierrors.IsAlreadyExists(err) {
    81  			return storage.NewErrAppStorageExists(appName)
    82  		}
    83  		return err
    84  	}
    85  	return nil
    86  }
    87  
    88  // UpdateBuild updates the application configmap storage specified by appName to include build.
    89  //
    90  // If build does not exist, a new storage entry is created. Otherwise the existing storage
    91  // is updated.
    92  //
    93  // UpdateBuild implements storage.Updater.
    94  func (s *ConfigMaps) UpdateBuild(ctx context.Context, appName string, build *storage.Object) (err error) {
    95  	var cfgmap *v1.ConfigMap
    96  	if cfgmap, err = s.impl.Get(appName, metav1.GetOptions{}); err != nil {
    97  		if apierrors.IsNotFound(err) {
    98  			return s.CreateBuild(ctx, appName, build)
    99  		}
   100  		return err
   101  	}
   102  	if _, ok := cfgmap.Data[build.BuildID]; ok {
   103  		return storage.NewErrAppBuildExists(appName, build.BuildID)
   104  	}
   105  	if build.CreatedAt, err = ptypes.TimestampProto(time.Now()); err != nil {
   106  		return err
   107  	}
   108  	content, err := storage.EncodeToString(build)
   109  	if err != nil {
   110  		return err
   111  	}
   112  	cfgmap.Data[build.BuildID] = content
   113  	_, err = s.impl.Update(cfgmap)
   114  	return err
   115  }
   116  
   117  // GetBuilds returns a slice of builds for the given app name.
   118  //
   119  // GetBuilds implements storage.Getter.
   120  func (s *ConfigMaps) GetBuilds(ctx context.Context, appName string) (builds []*storage.Object, err error) {
   121  	var cfgmap *v1.ConfigMap
   122  	if cfgmap, err = s.impl.Get(appName, metav1.GetOptions{}); err != nil {
   123  		if apierrors.IsNotFound(err) {
   124  			return nil, storage.NewErrAppStorageNotFound(appName)
   125  		}
   126  		return nil, err
   127  	}
   128  	for _, obj := range cfgmap.Data {
   129  		build, err := storage.DecodeString(obj)
   130  		if err != nil {
   131  			return nil, err
   132  		}
   133  		builds = append(builds, build)
   134  	}
   135  	return builds, nil
   136  }
   137  
   138  // GetBuild returns the build associated with buildID for the specified app name.
   139  //
   140  // GetBuild implements storage.Getter.
   141  func (s *ConfigMaps) GetBuild(ctx context.Context, appName, buildID string) (obj *storage.Object, err error) {
   142  	var cfgmap *v1.ConfigMap
   143  	if cfgmap, err = s.impl.Get(appName, metav1.GetOptions{}); err != nil {
   144  		if apierrors.IsNotFound(err) {
   145  			return nil, storage.NewErrAppStorageNotFound(appName)
   146  		}
   147  		return nil, err
   148  	}
   149  	if data, ok := cfgmap.Data[buildID]; ok {
   150  		if obj, err = storage.DecodeString(data); err != nil {
   151  			return nil, err
   152  		}
   153  		return obj, nil
   154  	}
   155  	return nil, storage.NewErrAppBuildNotFound(appName, buildID)
   156  }
   157  
   158  // newConfigMap constructs a kubernetes ConfigMap object to store a build.
   159  //
   160  // Each configmap data entry is the base64 encoded string of a *storage.Object
   161  // binary protobuf encoding.
   162  func newConfigMap(appName string, build *storage.Object) (*v1.ConfigMap, error) {
   163  	content, err := storage.EncodeToString(build)
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  	cfgmap := &v1.ConfigMap{
   168  		ObjectMeta: metav1.ObjectMeta{
   169  			Name: appName,
   170  			Labels: map[string]string{
   171  				"heritage": "draft",
   172  				"appname":  appName,
   173  			},
   174  		},
   175  		Data: map[string]string{build.BuildID: content},
   176  	}
   177  	return cfgmap, nil
   178  }