@@ -19,23 +19,25 @@ import (
19
19
"io"
20
20
"strings"
21
21
22
- "k8s.io/apimachinery/pkg/runtime"
23
-
24
- "k8s.io/apimachinery/pkg/api/meta"
25
-
22
+ helm2to3 "github.com/helm/helm-2to3/pkg/v3"
26
23
"github.com/martinlindhe/base36"
27
24
"github.com/pborman/uuid"
28
25
"helm.sh/helm/v3/pkg/action"
29
26
"helm.sh/helm/v3/pkg/chart/loader"
30
27
"helm.sh/helm/v3/pkg/kube"
31
- helmrelease "helm.sh/helm/v3/pkg/release"
32
- "helm.sh/helm/v3/pkg/storage"
33
- "helm.sh/helm/v3/pkg/storage/driver"
28
+ helmreleasev3 "helm.sh/helm/v3/pkg/release"
29
+ v3storage "helm.sh/helm/v3/pkg/storage"
30
+ v3driver "helm.sh/helm/v3/pkg/storage/driver"
31
+ "k8s.io/apimachinery/pkg/api/meta"
34
32
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
35
33
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
34
+ "k8s.io/apimachinery/pkg/runtime"
36
35
apitypes "k8s.io/apimachinery/pkg/types"
37
36
"k8s.io/cli-runtime/pkg/resource"
38
37
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
38
+ helmreleasev2 "k8s.io/helm/pkg/proto/hapi/release"
39
+ v2storage "k8s.io/helm/pkg/storage"
40
+ v2driver "k8s.io/helm/pkg/storage/driver"
39
41
crmanager "sigs.k8s.io/controller-runtime/pkg/manager"
40
42
41
43
"github.com/operator-framework/operator-sdk/pkg/helm/client"
@@ -65,7 +67,14 @@ func (f managerFactory) NewManager(cr *unstructured.Unstructured) (Manager, erro
65
67
if err != nil {
66
68
return nil , fmt .Errorf ("failed to get core/v1 client: %s" , err )
67
69
}
68
- storageBackend := storage .Init (driver .NewSecrets (clientv1 .Secrets (cr .GetNamespace ())))
70
+
71
+ v2StorageBackend := v2storage .Init (v2driver .NewSecrets (clientv1 .Secrets (cr .GetNamespace ())))
72
+ v3StorageBackend := v3storage .Init (v3driver .NewSecrets (clientv1 .Secrets (cr .GetNamespace ())))
73
+
74
+ if err := convertV2ToV3 (v2StorageBackend , v3StorageBackend , cr ); err != nil {
75
+ return nil , fmt .Errorf ("failed to convert releases from v2 to v3: %s" , err )
76
+ }
77
+
69
78
rcg , err := client .NewRESTClientGetter (f .mgr )
70
79
if err != nil {
71
80
return nil , fmt .Errorf ("failed to get REST client getter from manager: %s" , err )
@@ -75,7 +84,7 @@ func (f managerFactory) NewManager(cr *unstructured.Unstructured) (Manager, erro
75
84
if err != nil {
76
85
return nil , fmt .Errorf ("failed to load chart dir: %s" , err )
77
86
}
78
- releaseName , err := getReleaseName (storageBackend , crChart .Name (), cr )
87
+ releaseName , err := getReleaseName (v3StorageBackend , crChart .Name (), cr )
79
88
if err != nil {
80
89
return nil , fmt .Errorf ("failed to get helm release name: %s" , err )
81
90
}
@@ -90,13 +99,13 @@ func (f managerFactory) NewManager(cr *unstructured.Unstructured) (Manager, erro
90
99
}
91
100
actionConfig := & action.Configuration {
92
101
RESTClientGetter : rcg ,
93
- Releases : storageBackend ,
102
+ Releases : v3StorageBackend ,
94
103
KubeClient : ownerRefClient ,
95
104
Log : func (_ string , _ ... interface {}) {},
96
105
}
97
106
return & manager {
98
107
actionConfig : actionConfig ,
99
- storageBackend : storageBackend ,
108
+ storageBackend : v3StorageBackend ,
100
109
kubeClient : ownerRefClient ,
101
110
102
111
releaseName : releaseName ,
@@ -138,6 +147,55 @@ func (c *ownerRefInjectingClient) Build(reader io.Reader, validate bool) (kube.R
138
147
return resourceList , nil
139
148
}
140
149
150
+ func convertV2ToV3 (v2StorageBackend * v2storage.Storage , v3StorageBackend * v3storage.Storage , cr * unstructured.Unstructured ) error {
151
+ // If a v2 release with the legacy name exists, convert it to v3.
152
+ legacyName := getLegacyName (cr )
153
+ legacyHistoryV2 , legacyV2Exists , err := releaseHistoryV2 (v2StorageBackend , legacyName )
154
+ if err != nil {
155
+ return err
156
+ }
157
+ if legacyV2Exists {
158
+ if err := convertHistoryToV3 (legacyHistoryV2 , v2StorageBackend , v3StorageBackend ); err != nil {
159
+ return err
160
+ }
161
+ return nil
162
+ }
163
+
164
+ // If a v2 release with the CR name exists, convert it to v3.
165
+ releaseName := cr .GetName ()
166
+ historyV2 , existsV2 , err := releaseHistoryV2 (v2StorageBackend , releaseName )
167
+ if err != nil {
168
+ return err
169
+ }
170
+ if existsV2 {
171
+ if err := convertHistoryToV3 (historyV2 , v2StorageBackend , v3StorageBackend ); err != nil {
172
+ return err
173
+ }
174
+ return nil
175
+ }
176
+ return nil
177
+ }
178
+
179
+ func convertHistoryToV3 (history []* helmreleasev2.Release , v2StorageBackend * v2storage.Storage , v3StorageBackend * v3storage.Storage ) error {
180
+ for _ , v2Rel := range history {
181
+ v3Rel , err := helm2to3 .CreateRelease (v2Rel )
182
+ if err != nil {
183
+ return fmt .Errorf ("generate v3 release: %s" , err )
184
+ }
185
+ if err := v3StorageBackend .Create (v3Rel ); err != nil {
186
+ return fmt .Errorf ("create v3 release: %s" , err )
187
+ }
188
+ if _ , err := v2StorageBackend .Delete (v2Rel .GetName (), v2Rel .GetVersion ()); err != nil {
189
+ return fmt .Errorf ("delete v2 release: %s" , err )
190
+ }
191
+ }
192
+ return nil
193
+ }
194
+
195
+ func getLegacyName (cr * unstructured.Unstructured ) string {
196
+ return fmt .Sprintf ("%s-%s" , cr .GetName (), shortenUID (cr .GetUID ()))
197
+ }
198
+
141
199
// getReleaseName returns a release name for the CR. If a release for the
142
200
// legacy name exists, the legacy name is returned. This ensures
143
201
// backwards-compatibility for pre-existing CRs.
@@ -169,10 +227,11 @@ func (c *ownerRefInjectingClient) Build(reader io.Reader, validate bool) (kube.R
169
227
// admission webhook so that the CR owner receives immediate feedback of the
170
228
// collision. As is, the only indication of collision will be in the CR status
171
229
// and operator logs.
172
- func getReleaseName (storageBackend * storage.Storage , crChartName string , cr * unstructured.Unstructured ) (string , error ) {
173
- // If a release with the legacy name exists, return the legacy name.
174
- legacyName := fmt .Sprintf ("%s-%s" , cr .GetName (), shortenUID (cr .GetUID ()))
175
- _ , legacyExists , err := releaseHistory (storageBackend , legacyName )
230
+ func getReleaseName (storageBackend * v3storage.Storage , crChartName string , cr * unstructured.Unstructured ) (string , error ) {
231
+ // If a release with the legacy name exists as a v3 release,
232
+ // return the legacy name.
233
+ legacyName := getLegacyName (cr )
234
+ _ , legacyExists , err := releaseHistoryV3 (storageBackend , legacyName )
176
235
if err != nil {
177
236
return "" , err
178
237
}
@@ -182,7 +241,7 @@ func getReleaseName(storageBackend *storage.Storage, crChartName string, cr *uns
182
241
183
242
// If a release with the CR name does not exist, return the CR name.
184
243
releaseName := cr .GetName ()
185
- history , exists , err := releaseHistory (storageBackend , releaseName )
244
+ history , exists , err := releaseHistoryV3 (storageBackend , releaseName )
186
245
if err != nil {
187
246
return "" , err
188
247
}
@@ -204,7 +263,18 @@ func getReleaseName(storageBackend *storage.Storage, crChartName string, cr *uns
204
263
return releaseName , nil
205
264
}
206
265
207
- func releaseHistory (storageBackend * storage.Storage , releaseName string ) ([]* helmrelease.Release , bool , error ) {
266
+ func releaseHistoryV2 (storageBackend * v2storage.Storage , releaseName string ) ([]* helmreleasev2.Release , bool , error ) {
267
+ releaseHistory , err := storageBackend .History (releaseName )
268
+ if err != nil {
269
+ if notFoundErr (err ) {
270
+ return nil , false , nil
271
+ }
272
+ return nil , false , err
273
+ }
274
+ return releaseHistory , len (releaseHistory ) > 0 , nil
275
+ }
276
+
277
+ func releaseHistoryV3 (storageBackend * v3storage.Storage , releaseName string ) ([]* helmreleasev3.Release , bool , error ) {
208
278
releaseHistory , err := storageBackend .History (releaseName )
209
279
if err != nil {
210
280
if notFoundErr (err ) {
0 commit comments