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 }