@@ -16,7 +16,6 @@ package release
16
16
17
17
import (
18
18
"fmt"
19
- "io"
20
19
"strings"
21
20
22
21
helm2to3 "github.com/helm/helm-2to3/pkg/v3"
@@ -28,12 +27,9 @@ import (
28
27
helmreleasev3 "helm.sh/helm/v3/pkg/release"
29
28
storagev3 "helm.sh/helm/v3/pkg/storage"
30
29
driverv3 "helm.sh/helm/v3/pkg/storage/driver"
31
- "k8s.io/apimachinery/pkg/api/meta"
32
30
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33
31
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
34
- "k8s.io/apimachinery/pkg/runtime"
35
32
apitypes "k8s.io/apimachinery/pkg/types"
36
- "k8s.io/cli-runtime/pkg/resource"
37
33
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
38
34
helmreleasev2 "k8s.io/helm/pkg/proto/hapi/release"
39
35
storagev2 "k8s.io/helm/pkg/storage"
@@ -63,46 +59,53 @@ func NewManagerFactory(mgr crmanager.Manager, chartDir string) ManagerFactory {
63
59
}
64
60
65
61
func (f managerFactory ) NewManager (cr * unstructured.Unstructured ) (Manager , error ) {
62
+ // Get both v2 and v3 storage backends
66
63
clientv1 , err := v1 .NewForConfig (f .mgr .GetConfig ())
67
64
if err != nil {
68
65
return nil , fmt .Errorf ("failed to get core/v1 client: %w" , err )
69
66
}
70
-
71
67
storageBackendV2 := storagev2 .Init (driverv2 .NewSecrets (clientv1 .Secrets (cr .GetNamespace ())))
72
68
storageBackendV3 := storagev3 .Init (driverv3 .NewSecrets (clientv1 .Secrets (cr .GetNamespace ())))
73
69
70
+ // Automatically convert V2 releases to V3 releases. This is required to
71
+ // maintain backward compatibility with old releases now that the
72
+ // operator reconciliation loop expects Helm V3 releases.
74
73
if err := convertV2ToV3 (storageBackendV2 , storageBackendV3 , cr ); err != nil {
75
74
return nil , fmt .Errorf ("failed to convert releases from v2 to v3: %w" , err )
76
75
}
77
76
77
+ // Get the necessary clients and client getters. Use a client that injects the CR
78
+ // as an owner reference into all resources templated by the chart.
78
79
rcg , err := client .NewRESTClientGetter (f .mgr )
79
80
if err != nil {
80
81
return nil , fmt .Errorf ("failed to get REST client getter from manager: %w" , err )
81
82
}
82
83
kubeClient := kube .New (nil )
84
+ ownerRef := metav1 .NewControllerRef (cr , cr .GroupVersionKind ())
85
+ ownerRefClient := client .NewOwnerRefInjectingClient (* kubeClient , * ownerRef )
86
+
83
87
crChart , err := loader .LoadDir (f .chartDir )
84
88
if err != nil {
85
89
return nil , fmt .Errorf ("failed to load chart dir: %w" , err )
86
90
}
91
+
87
92
releaseName , err := getReleaseName (storageBackendV3 , crChart .Name (), cr )
88
93
if err != nil {
89
94
return nil , fmt .Errorf ("failed to get helm release name: %w" , err )
90
95
}
96
+
91
97
values , ok := cr .Object ["spec" ].(map [string ]interface {})
92
98
if ! ok {
93
99
return nil , fmt .Errorf ("failed to get spec: expected map[string]interface{}" )
94
100
}
95
- controllerRef := metav1 .NewControllerRef (cr , cr .GroupVersionKind ())
96
- ownerRefClient := & ownerRefInjectingClient {
97
- refs : []metav1.OwnerReference {* controllerRef },
98
- Client : * kubeClient ,
99
- }
101
+
100
102
actionConfig := & action.Configuration {
101
103
RESTClientGetter : rcg ,
102
104
Releases : storageBackendV3 ,
103
105
KubeClient : ownerRefClient ,
104
106
Log : func (_ string , _ ... interface {}) {},
105
107
}
108
+
106
109
return & manager {
107
110
actionConfig : actionConfig ,
108
111
storageBackend : storageBackendV3 ,
@@ -117,36 +120,6 @@ func (f managerFactory) NewManager(cr *unstructured.Unstructured) (Manager, erro
117
120
}, nil
118
121
}
119
122
120
- type ownerRefInjectingClient struct {
121
- refs []metav1.OwnerReference
122
- kube.Client
123
- }
124
-
125
- func (c * ownerRefInjectingClient ) Build (reader io.Reader , validate bool ) (kube.ResourceList , error ) {
126
- resourceList , err := c .Client .Build (reader , validate )
127
- if err != nil {
128
- return resourceList , err
129
- }
130
- err = resourceList .Visit (func (r * resource.Info , err error ) error {
131
- if err != nil {
132
- return err
133
- }
134
- objMap , err := runtime .DefaultUnstructuredConverter .ToUnstructured (r .Object )
135
- if err != nil {
136
- return err
137
- }
138
- u := & unstructured.Unstructured {Object : objMap }
139
- if r .ResourceMapping ().Scope == meta .RESTScopeNamespace {
140
- u .SetOwnerReferences (c .refs )
141
- }
142
- return nil
143
- })
144
- if err != nil {
145
- return nil , err
146
- }
147
- return resourceList , nil
148
- }
149
-
150
123
func convertV2ToV3 (storageBackendV2 * storagev2.Storage , storageBackendV3 * storagev3.Storage , cr * unstructured.Unstructured ) error {
151
124
// If a v2 release with the legacy name exists, convert it to v3.
152
125
legacyName := getLegacyName (cr )
@@ -217,10 +190,10 @@ func getLegacyName(cr *unstructured.Unstructured) string {
217
190
// identifying names or characters added by templates.
218
191
//
219
192
// TODO(jlanford): As noted above, using the CR name as the release name raises
220
- // the possibility of collision. We should move this logic to a validating
221
- // admission webhook so that the CR owner receives immediate feedback of the
222
- // collision. As is, the only indication of collision will be in the CR status
223
- // and operator logs.
193
+ // the possibility of collision. We should move this logic to a validating
194
+ // admission webhook so that the CR owner receives immediate feedback of the
195
+ // collision. As is, the only indication of collision will be in the CR status
196
+ // and operator logs.
224
197
func getReleaseName (storageBackend * storagev3.Storage , crChartName string , cr * unstructured.Unstructured ) (string , error ) {
225
198
// If a release with the legacy name exists as a v3 release,
226
199
// return the legacy name.
0 commit comments