Skip to content

Commit cbf31ca

Browse files
committed
pkg/helm: move ownerref-injecting client to client package
1 parent 0a08b77 commit cbf31ca

File tree

2 files changed

+62
-44
lines changed

2 files changed

+62
-44
lines changed

pkg/helm/client/client.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,15 @@
1515
package client
1616

1717
import (
18+
"io"
19+
1820
"helm.sh/helm/v3/pkg/kube"
1921
"k8s.io/apimachinery/pkg/api/meta"
22+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
24+
"k8s.io/apimachinery/pkg/runtime"
2025
"k8s.io/cli-runtime/pkg/genericclioptions"
26+
"k8s.io/cli-runtime/pkg/resource"
2127
"k8s.io/client-go/discovery"
2228
cached "k8s.io/client-go/discovery/cached"
2329
"k8s.io/client-go/rest"
@@ -74,3 +80,42 @@ func NewRESTClientGetter(mgr manager.Manager) (genericclioptions.RESTClientGette
7480
restMapper: rm,
7581
}, nil
7682
}
83+
84+
var _ kube.Interface = &ownerRefInjectingClient{}
85+
86+
func NewOwnerRefInjectingClient(base kube.Client, ownerRef metav1.OwnerReference) kube.Interface {
87+
return &ownerRefInjectingClient{
88+
refs: []metav1.OwnerReference{ownerRef},
89+
Client: base,
90+
}
91+
}
92+
93+
type ownerRefInjectingClient struct {
94+
refs []metav1.OwnerReference
95+
kube.Client
96+
}
97+
98+
func (c *ownerRefInjectingClient) Build(reader io.Reader, validate bool) (kube.ResourceList, error) {
99+
resourceList, err := c.Client.Build(reader, validate)
100+
if err != nil {
101+
return resourceList, err
102+
}
103+
err = resourceList.Visit(func(r *resource.Info, err error) error {
104+
if err != nil {
105+
return err
106+
}
107+
objMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(r.Object)
108+
if err != nil {
109+
return err
110+
}
111+
u := &unstructured.Unstructured{Object: objMap}
112+
if r.ResourceMapping().Scope == meta.RESTScopeNamespace {
113+
u.SetOwnerReferences(c.refs)
114+
}
115+
return nil
116+
})
117+
if err != nil {
118+
return nil, err
119+
}
120+
return resourceList, nil
121+
}

pkg/helm/release/manager_factory.go

Lines changed: 17 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ package release
1616

1717
import (
1818
"fmt"
19-
"io"
2019
"strings"
2120

2221
helm2to3 "github.com/helm/helm-2to3/pkg/v3"
@@ -28,12 +27,9 @@ import (
2827
helmreleasev3 "helm.sh/helm/v3/pkg/release"
2928
storagev3 "helm.sh/helm/v3/pkg/storage"
3029
driverv3 "helm.sh/helm/v3/pkg/storage/driver"
31-
"k8s.io/apimachinery/pkg/api/meta"
3230
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3331
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
34-
"k8s.io/apimachinery/pkg/runtime"
3532
apitypes "k8s.io/apimachinery/pkg/types"
36-
"k8s.io/cli-runtime/pkg/resource"
3733
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
3834
helmreleasev2 "k8s.io/helm/pkg/proto/hapi/release"
3935
storagev2 "k8s.io/helm/pkg/storage"
@@ -63,46 +59,53 @@ func NewManagerFactory(mgr crmanager.Manager, chartDir string) ManagerFactory {
6359
}
6460

6561
func (f managerFactory) NewManager(cr *unstructured.Unstructured) (Manager, error) {
62+
// Get both v2 and v3 storage backends
6663
clientv1, err := v1.NewForConfig(f.mgr.GetConfig())
6764
if err != nil {
6865
return nil, fmt.Errorf("failed to get core/v1 client: %w", err)
6966
}
70-
7167
storageBackendV2 := storagev2.Init(driverv2.NewSecrets(clientv1.Secrets(cr.GetNamespace())))
7268
storageBackendV3 := storagev3.Init(driverv3.NewSecrets(clientv1.Secrets(cr.GetNamespace())))
7369

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.
7473
if err := convertV2ToV3(storageBackendV2, storageBackendV3, cr); err != nil {
7574
return nil, fmt.Errorf("failed to convert releases from v2 to v3: %w", err)
7675
}
7776

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.
7879
rcg, err := client.NewRESTClientGetter(f.mgr)
7980
if err != nil {
8081
return nil, fmt.Errorf("failed to get REST client getter from manager: %w", err)
8182
}
8283
kubeClient := kube.New(nil)
84+
ownerRef := metav1.NewControllerRef(cr, cr.GroupVersionKind())
85+
ownerRefClient := client.NewOwnerRefInjectingClient(*kubeClient, *ownerRef)
86+
8387
crChart, err := loader.LoadDir(f.chartDir)
8488
if err != nil {
8589
return nil, fmt.Errorf("failed to load chart dir: %w", err)
8690
}
91+
8792
releaseName, err := getReleaseName(storageBackendV3, crChart.Name(), cr)
8893
if err != nil {
8994
return nil, fmt.Errorf("failed to get helm release name: %w", err)
9095
}
96+
9197
values, ok := cr.Object["spec"].(map[string]interface{})
9298
if !ok {
9399
return nil, fmt.Errorf("failed to get spec: expected map[string]interface{}")
94100
}
95-
controllerRef := metav1.NewControllerRef(cr, cr.GroupVersionKind())
96-
ownerRefClient := &ownerRefInjectingClient{
97-
refs: []metav1.OwnerReference{*controllerRef},
98-
Client: *kubeClient,
99-
}
101+
100102
actionConfig := &action.Configuration{
101103
RESTClientGetter: rcg,
102104
Releases: storageBackendV3,
103105
KubeClient: ownerRefClient,
104106
Log: func(_ string, _ ...interface{}) {},
105107
}
108+
106109
return &manager{
107110
actionConfig: actionConfig,
108111
storageBackend: storageBackendV3,
@@ -117,36 +120,6 @@ func (f managerFactory) NewManager(cr *unstructured.Unstructured) (Manager, erro
117120
}, nil
118121
}
119122

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-
150123
func convertV2ToV3(storageBackendV2 *storagev2.Storage, storageBackendV3 *storagev3.Storage, cr *unstructured.Unstructured) error {
151124
// If a v2 release with the legacy name exists, convert it to v3.
152125
legacyName := getLegacyName(cr)
@@ -217,10 +190,10 @@ func getLegacyName(cr *unstructured.Unstructured) string {
217190
// identifying names or characters added by templates.
218191
//
219192
// 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.
224197
func getReleaseName(storageBackend *storagev3.Storage, crChartName string, cr *unstructured.Unstructured) (string, error) {
225198
// If a release with the legacy name exists as a v3 release,
226199
// return the legacy name.

0 commit comments

Comments
 (0)