1
16
17 package storage
18
19 import (
20 "fmt"
21 "strings"
22
23 "github.com/pkg/errors"
24
25 rspb "helm.sh/helm/v3/pkg/release"
26 relutil "helm.sh/helm/v3/pkg/releaseutil"
27 "helm.sh/helm/v3/pkg/storage/driver"
28 )
29
30
31
32
33
34
35 const HelmStorageType = "sh.helm.release.v1"
36
37
38 type Storage struct {
39 driver.Driver
40
41
42
43
44 MaxHistory int
45
46 Log func(string, ...interface{})
47 }
48
49
50
51
52 func (s *Storage) Get(name string, version int) (*rspb.Release, error) {
53 s.Log("getting release %q", makeKey(name, version))
54 return s.Driver.Get(makeKey(name, version))
55 }
56
57
58
59
60 func (s *Storage) Create(rls *rspb.Release) error {
61 s.Log("creating release %q", makeKey(rls.Name, rls.Version))
62 if s.MaxHistory > 0 {
63
64 if err := s.removeLeastRecent(rls.Name, s.MaxHistory-1); err != nil &&
65 !errors.Is(err, driver.ErrReleaseNotFound) {
66 return err
67 }
68 }
69 return s.Driver.Create(makeKey(rls.Name, rls.Version), rls)
70 }
71
72
73
74
75 func (s *Storage) Update(rls *rspb.Release) error {
76 s.Log("updating release %q", makeKey(rls.Name, rls.Version))
77 return s.Driver.Update(makeKey(rls.Name, rls.Version), rls)
78 }
79
80
81
82
83 func (s *Storage) Delete(name string, version int) (*rspb.Release, error) {
84 s.Log("deleting release %q", makeKey(name, version))
85 return s.Driver.Delete(makeKey(name, version))
86 }
87
88
89
90 func (s *Storage) ListReleases() ([]*rspb.Release, error) {
91 s.Log("listing all releases in storage")
92 return s.Driver.List(func(_ *rspb.Release) bool { return true })
93 }
94
95
96
97 func (s *Storage) ListUninstalled() ([]*rspb.Release, error) {
98 s.Log("listing uninstalled releases in storage")
99 return s.Driver.List(func(rls *rspb.Release) bool {
100 return relutil.StatusFilter(rspb.StatusUninstalled).Check(rls)
101 })
102 }
103
104
105
106 func (s *Storage) ListDeployed() ([]*rspb.Release, error) {
107 s.Log("listing all deployed releases in storage")
108 return s.Driver.List(func(rls *rspb.Release) bool {
109 return relutil.StatusFilter(rspb.StatusDeployed).Check(rls)
110 })
111 }
112
113
114
115 func (s *Storage) Deployed(name string) (*rspb.Release, error) {
116 ls, err := s.DeployedAll(name)
117 if err != nil {
118 return nil, err
119 }
120
121 if len(ls) == 0 {
122 return nil, driver.NewErrNoDeployedReleases(name)
123 }
124
125
126
127 relutil.Reverse(ls, relutil.SortByRevision)
128
129 return ls[0], nil
130 }
131
132
133
134 func (s *Storage) DeployedAll(name string) ([]*rspb.Release, error) {
135 s.Log("getting deployed releases from %q history", name)
136
137 ls, err := s.Driver.Query(map[string]string{
138 "name": name,
139 "owner": "helm",
140 "status": "deployed",
141 })
142 if err == nil {
143 return ls, nil
144 }
145 if strings.Contains(err.Error(), "not found") {
146 return nil, driver.NewErrNoDeployedReleases(name)
147 }
148 return nil, err
149 }
150
151
152
153 func (s *Storage) History(name string) ([]*rspb.Release, error) {
154 s.Log("getting release history for %q", name)
155
156 return s.Driver.Query(map[string]string{"name": name, "owner": "helm"})
157 }
158
159
160
161
162
163
164 func (s *Storage) removeLeastRecent(name string, max int) error {
165 if max < 0 {
166 return nil
167 }
168 h, err := s.History(name)
169 if err != nil {
170 return err
171 }
172 if len(h) <= max {
173 return nil
174 }
175
176
177 relutil.SortByRevision(h)
178
179 lastDeployed, err := s.Deployed(name)
180 if err != nil && !errors.Is(err, driver.ErrNoDeployedReleases) {
181 return err
182 }
183
184 var toDelete []*rspb.Release
185 for _, rel := range h {
186
187 if len(h)-len(toDelete) == max {
188 break
189 }
190 if lastDeployed != nil {
191 if rel.Version != lastDeployed.Version {
192 toDelete = append(toDelete, rel)
193 }
194 } else {
195 toDelete = append(toDelete, rel)
196 }
197 }
198
199
200
201 errs := []error{}
202 for _, rel := range toDelete {
203 err = s.deleteReleaseVersion(name, rel.Version)
204 if err != nil {
205 errs = append(errs, err)
206 }
207 }
208
209 s.Log("Pruned %d record(s) from %s with %d error(s)", len(toDelete), name, len(errs))
210 switch c := len(errs); c {
211 case 0:
212 return nil
213 case 1:
214 return errs[0]
215 default:
216 return errors.Errorf("encountered %d deletion errors. First is: %s", c, errs[0])
217 }
218 }
219
220 func (s *Storage) deleteReleaseVersion(name string, version int) error {
221 key := makeKey(name, version)
222 _, err := s.Delete(name, version)
223 if err != nil {
224 s.Log("error pruning %s from release history: %s", key, err)
225 return err
226 }
227 return nil
228 }
229
230
231 func (s *Storage) Last(name string) (*rspb.Release, error) {
232 s.Log("getting last revision of %q", name)
233 h, err := s.History(name)
234 if err != nil {
235 return nil, err
236 }
237 if len(h) == 0 {
238 return nil, errors.Errorf("no revision for release %q", name)
239 }
240
241 relutil.Reverse(h, relutil.SortByRevision)
242 return h[0], nil
243 }
244
245
246
247
248
249
250
251 func makeKey(rlsname string, version int) string {
252 return fmt.Sprintf("%s.%s.v%d", HelmStorageType, rlsname, version)
253 }
254
255
256
257 func Init(d driver.Driver) *Storage {
258
259 if d == nil {
260 d = driver.NewMemory()
261 }
262 return &Storage{
263 Driver: d,
264 Log: func(_ string, _ ...interface{}) {},
265 }
266 }
267
View as plain text