1
16
17 package driver
18
19 import (
20 "context"
21 "strconv"
22 "strings"
23 "time"
24
25 "github.com/pkg/errors"
26 v1 "k8s.io/api/core/v1"
27 apierrors "k8s.io/apimachinery/pkg/api/errors"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 kblabels "k8s.io/apimachinery/pkg/labels"
30 "k8s.io/apimachinery/pkg/util/validation"
31 corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
32
33 rspb "helm.sh/helm/v3/pkg/release"
34 )
35
36 var _ Driver = (*ConfigMaps)(nil)
37
38
39 const ConfigMapsDriverName = "ConfigMap"
40
41
42
43 type ConfigMaps struct {
44 impl corev1.ConfigMapInterface
45 Log func(string, ...interface{})
46 }
47
48
49
50 func NewConfigMaps(impl corev1.ConfigMapInterface) *ConfigMaps {
51 return &ConfigMaps{
52 impl: impl,
53 Log: func(_ string, _ ...interface{}) {},
54 }
55 }
56
57
58 func (cfgmaps *ConfigMaps) Name() string {
59 return ConfigMapsDriverName
60 }
61
62
63
64 func (cfgmaps *ConfigMaps) Get(key string) (*rspb.Release, error) {
65
66 obj, err := cfgmaps.impl.Get(context.Background(), key, metav1.GetOptions{})
67 if err != nil {
68 if apierrors.IsNotFound(err) {
69 return nil, ErrReleaseNotFound
70 }
71
72 cfgmaps.Log("get: failed to get %q: %s", key, err)
73 return nil, err
74 }
75
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 r.Labels = filterSystemLabels(obj.ObjectMeta.Labels)
82
83 return r, nil
84 }
85
86
87
88
89 func (cfgmaps *ConfigMaps) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) {
90 lsel := kblabels.Set{"owner": "helm"}.AsSelector()
91 opts := metav1.ListOptions{LabelSelector: lsel.String()}
92
93 list, err := cfgmaps.impl.List(context.Background(), opts)
94 if err != nil {
95 cfgmaps.Log("list: failed to list: %s", err)
96 return nil, err
97 }
98
99 var results []*rspb.Release
100
101
102
103 for _, item := range list.Items {
104 rls, err := decodeRelease(item.Data["release"])
105 if err != nil {
106 cfgmaps.Log("list: failed to decode release: %v: %s", item, err)
107 continue
108 }
109
110 rls.Labels = item.ObjectMeta.Labels
111
112 if filter(rls) {
113 results = append(results, rls)
114 }
115 }
116 return results, nil
117 }
118
119
120
121 func (cfgmaps *ConfigMaps) Query(labels map[string]string) ([]*rspb.Release, error) {
122 ls := kblabels.Set{}
123 for k, v := range labels {
124 if errs := validation.IsValidLabelValue(v); len(errs) != 0 {
125 return nil, errors.Errorf("invalid label value: %q: %s", v, strings.Join(errs, "; "))
126 }
127 ls[k] = v
128 }
129
130 opts := metav1.ListOptions{LabelSelector: ls.AsSelector().String()}
131
132 list, err := cfgmaps.impl.List(context.Background(), opts)
133 if err != nil {
134 cfgmaps.Log("query: failed to query with labels: %s", err)
135 return nil, err
136 }
137
138 if len(list.Items) == 0 {
139 return nil, ErrReleaseNotFound
140 }
141
142 var results []*rspb.Release
143 for _, item := range list.Items {
144 rls, err := decodeRelease(item.Data["release"])
145 if err != nil {
146 cfgmaps.Log("query: failed to decode release: %s", err)
147 continue
148 }
149 rls.Labels = item.ObjectMeta.Labels
150 results = append(results, rls)
151 }
152 return results, nil
153 }
154
155
156
157 func (cfgmaps *ConfigMaps) Create(key string, rls *rspb.Release) error {
158
159 var lbs labels
160
161 lbs.init()
162 lbs.fromMap(rls.Labels)
163 lbs.set("createdAt", strconv.Itoa(int(time.Now().Unix())))
164
165
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
172 if _, err := cfgmaps.impl.Create(context.Background(), obj, metav1.CreateOptions{}); err != nil {
173 if apierrors.IsAlreadyExists(err) {
174 return ErrReleaseExists
175 }
176
177 cfgmaps.Log("create: failed to create: %s", err)
178 return err
179 }
180 return nil
181 }
182
183
184
185 func (cfgmaps *ConfigMaps) Update(key string, rls *rspb.Release) error {
186
187 var lbs labels
188
189 lbs.init()
190 lbs.fromMap(rls.Labels)
191 lbs.set("modifiedAt", strconv.Itoa(int(time.Now().Unix())))
192
193
194 obj, err := newConfigMapsObject(key, rls, lbs)
195 if err != nil {
196 cfgmaps.Log("update: failed to encode release %q: %s", rls.Name, err)
197 return err
198 }
199
200 _, err = cfgmaps.impl.Update(context.Background(), obj, metav1.UpdateOptions{})
201 if err != nil {
202 cfgmaps.Log("update: failed to update: %s", err)
203 return err
204 }
205 return nil
206 }
207
208
209 func (cfgmaps *ConfigMaps) Delete(key string) (rls *rspb.Release, err error) {
210
211 if rls, err = cfgmaps.Get(key); err != nil {
212 return nil, err
213 }
214
215 if err = cfgmaps.impl.Delete(context.Background(), key, metav1.DeleteOptions{}); err != nil {
216 return rls, err
217 }
218 return rls, nil
219 }
220
221
222
223
224
225
226
227
228
229
230
231
232
233 func newConfigMapsObject(key string, rls *rspb.Release, lbs labels) (*v1.ConfigMap, error) {
234 const owner = "helm"
235
236
237 s, err := encodeRelease(rls)
238 if err != nil {
239 return nil, err
240 }
241
242 if lbs == nil {
243 lbs.init()
244 }
245
246
247 lbs.fromMap(rls.Labels)
248
249
250 lbs.set("name", rls.Name)
251 lbs.set("owner", owner)
252 lbs.set("status", rls.Info.Status.String())
253 lbs.set("version", strconv.Itoa(rls.Version))
254
255
256 return &v1.ConfigMap{
257 ObjectMeta: metav1.ObjectMeta{
258 Name: key,
259 Labels: lbs.toMap(),
260 },
261 Data: map[string]string{"release": s},
262 }, nil
263 }
264
View as plain text