Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

actionConfigGetter: allow custom mapping from object to rest.Config #317

Merged
merged 1 commit into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module github.com/operator-framework/helm-operator-plugins

go 1.21

toolchain go1.21.8
toolchain go1.21.9

require (
github.com/blang/semver/v4 v4.0.0
Expand Down
13 changes: 7 additions & 6 deletions pkg/client/actionclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package client

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
Expand All @@ -40,13 +41,13 @@ import (
)

type ActionClientGetter interface {
ActionClientFor(obj client.Object) (ActionInterface, error)
ActionClientFor(ctx context.Context, obj client.Object) (ActionInterface, error)
}

type ActionClientGetterFunc func(obj client.Object) (ActionInterface, error)
type ActionClientGetterFunc func(ctx context.Context, obj client.Object) (ActionInterface, error)

func (acgf ActionClientGetterFunc) ActionClientFor(obj client.Object) (ActionInterface, error) {
return acgf(obj)
func (acgf ActionClientGetterFunc) ActionClientFor(ctx context.Context, obj client.Object) (ActionInterface, error) {
return acgf(ctx, obj)
}

type ActionInterface interface {
Expand Down Expand Up @@ -140,8 +141,8 @@ type actionClientGetter struct {

var _ ActionClientGetter = &actionClientGetter{}

func (hcg *actionClientGetter) ActionClientFor(obj client.Object) (ActionInterface, error) {
actionConfig, err := hcg.acg.ActionConfigFor(obj)
func (hcg *actionClientGetter) ActionClientFor(ctx context.Context, obj client.Object) (ActionInterface, error) {
actionConfig, err := hcg.acg.ActionConfigFor(ctx, obj)
if err != nil {
return nil, err
}
Expand Down
38 changes: 21 additions & 17 deletions pkg/client/actionclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"strconv"
"time"

"github.com/go-logr/logr"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"helm.sh/helm/v3/pkg/action"
Expand All @@ -45,6 +44,8 @@ import (
apitypes "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/rand"
"k8s.io/cli-runtime/pkg/resource"
"k8s.io/client-go/discovery"
"k8s.io/client-go/discovery/cached/memory"
"k8s.io/client-go/rest"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -71,7 +72,7 @@ var _ = Describe("ActionClient", func() {
})
var _ = Describe("NewActionClientGetter", func() {
It("should return a valid ActionConfigGetter", func() {
actionConfigGetter, err := NewActionConfigGetter(cfg, rm, logr.Discard())
actionConfigGetter, err := NewActionConfigGetter(cfg, rm)
Expect(err).ShouldNot(HaveOccurred())
acg, err := NewActionClientGetter(actionConfigGetter)
Expect(err).ToNot(HaveOccurred())
Expand All @@ -88,9 +89,12 @@ var _ = Describe("ActionClient", func() {
)
BeforeEach(func() {
var err error
actionConfigGetter, err = NewActionConfigGetter(cfg, rm, logr.Discard())
actionConfigGetter, err = NewActionConfigGetter(cfg, rm)
Expect(err).ShouldNot(HaveOccurred())
cli = kube.New(newRESTClientGetter(cfg, rm, ""))
dc, err := discovery.NewDiscoveryClientForConfig(cfg)
Expect(err).ShouldNot(HaveOccurred())
cdc := memory.NewMemCacheClient(dc)
cli = kube.New(newRESTClientGetter(cfg, rm, cdc, ""))
Expect(err).ShouldNot(HaveOccurred())
obj = testutil.BuildTestCR(gvk)
})
Expand All @@ -110,7 +114,7 @@ var _ = Describe("ActionClient", func() {
Expect(err).ToNot(HaveOccurred())
Expect(acg).NotTo(BeNil())

ac, err := acg.ActionClientFor(obj)
ac, err := acg.ActionClientFor(context.Background(), obj)
Expect(err).ToNot(HaveOccurred())
Expect(ac).NotTo(BeNil())

Expand All @@ -131,7 +135,7 @@ var _ = Describe("ActionClient", func() {
Expect(err).ToNot(HaveOccurred())
Expect(acg).NotTo(BeNil())

ac, err := acg.ActionClientFor(obj)
ac, err := acg.ActionClientFor(context.Background(), obj)
Expect(err).ToNot(HaveOccurred())
Expect(ac).NotTo(BeNil())

Expand All @@ -152,7 +156,7 @@ var _ = Describe("ActionClient", func() {
Expect(err).ToNot(HaveOccurred())
Expect(acg).NotTo(BeNil())

ac, err := acg.ActionClientFor(obj)
ac, err := acg.ActionClientFor(context.Background(), obj)
Expect(err).ToNot(HaveOccurred())
Expect(ac).NotTo(BeNil())

Expand All @@ -173,7 +177,7 @@ var _ = Describe("ActionClient", func() {
Expect(err).ToNot(HaveOccurred())
Expect(acg).NotTo(BeNil())

ac, err := acg.ActionClientFor(obj)
ac, err := acg.ActionClientFor(context.Background(), obj)
Expect(err).ToNot(HaveOccurred())
Expect(ac).NotTo(BeNil())

Expand All @@ -194,7 +198,7 @@ var _ = Describe("ActionClient", func() {
Expect(err).ToNot(HaveOccurred())
Expect(acg).NotTo(BeNil())

ac, err := acg.ActionClientFor(obj)
ac, err := acg.ActionClientFor(context.Background(), obj)
Expect(err).ToNot(HaveOccurred())
Expect(ac).NotTo(BeNil())

Expand Down Expand Up @@ -226,7 +230,7 @@ var _ = Describe("ActionClient", func() {
Expect(err).ToNot(HaveOccurred())
Expect(acg).NotTo(BeNil())

ac, err := acg.ActionClientFor(obj)
ac, err := acg.ActionClientFor(context.Background(), obj)
Expect(err).ToNot(HaveOccurred())
Expect(ac).NotTo(BeNil())

Expand Down Expand Up @@ -254,7 +258,7 @@ var _ = Describe("ActionClient", func() {
Expect(err).ToNot(HaveOccurred())
Expect(acg).NotTo(BeNil())

ac, err := acg.ActionClientFor(obj)
ac, err := acg.ActionClientFor(context.Background(), obj)
Expect(err).ToNot(HaveOccurred())

_, err = ac.Install(obj.GetName(), obj.GetNamespace(), &chrt, chartutil.Values{})
Expand Down Expand Up @@ -291,11 +295,11 @@ var _ = Describe("ActionClient", func() {
expectedObj := &unstructured.Unstructured{}
expectedObj.SetGroupVersionKind(gvk)
var actualObj client.Object
f := ActionClientGetterFunc(func(obj client.Object) (ActionInterface, error) {
f := ActionClientGetterFunc(func(_ context.Context, obj client.Object) (ActionInterface, error) {
actualObj = obj
return nil, nil
})
_, _ = f.ActionClientFor(expectedObj)
_, _ = f.ActionClientFor(context.Background(), expectedObj)
Expect(actualObj.GetObjectKind().GroupVersionKind()).To(Equal(gvk))
})
})
Expand All @@ -306,11 +310,11 @@ var _ = Describe("ActionClient", func() {
obj = testutil.BuildTestCR(gvk)
})
It("should return a valid ActionClient", func() {
actionConfGetter, err := NewActionConfigGetter(cfg, rm, logr.Discard())
actionConfGetter, err := NewActionConfigGetter(cfg, rm)
Expect(err).ShouldNot(HaveOccurred())
acg, err := NewActionClientGetter(actionConfGetter)
Expect(err).ToNot(HaveOccurred())
ac, err := acg.ActionClientFor(obj)
ac, err := acg.ActionClientFor(context.Background(), obj)
Expect(err).ToNot(HaveOccurred())
Expect(ac).NotTo(BeNil())
})
Expand All @@ -326,11 +330,11 @@ var _ = Describe("ActionClient", func() {
BeforeEach(func() {
obj = testutil.BuildTestCR(gvk)

actionConfigGetter, err := NewActionConfigGetter(cfg, rm, logr.Discard())
actionConfigGetter, err := NewActionConfigGetter(cfg, rm)
Expect(err).ShouldNot(HaveOccurred())
acg, err := NewActionClientGetter(actionConfigGetter)
Expect(err).ToNot(HaveOccurred())
ac, err = acg.ActionClientFor(obj)
ac, err = acg.ActionClientFor(context.Background(), obj)
Expect(err).ToNot(HaveOccurred())

cl, err = client.New(cfg, client.Options{})
Expand Down
103 changes: 64 additions & 39 deletions pkg/client/actionconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,38 +28,28 @@ import (
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/discovery"
"k8s.io/client-go/discovery/cached/memory"
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
)

type ActionConfigGetter interface {
ActionConfigFor(obj client.Object) (*action.Configuration, error)
ActionConfigFor(ctx context.Context, obj client.Object) (*action.Configuration, error)
}

func NewActionConfigGetter(cfg *rest.Config, rm meta.RESTMapper, log logr.Logger, opts ...ActionConfigGetterOption) (ActionConfigGetter, error) {
rcg := newRESTClientGetter(cfg, rm, "")
// Setup the debug log function that Helm will use
debugLog := func(format string, v ...interface{}) {
if log.GetSink() != nil {
log.V(1).Info(fmt.Sprintf(format, v...))
}
}

kc := kube.New(rcg)
kc.Log = debugLog

kcs, err := kc.Factory.KubernetesClientSet()
func NewActionConfigGetter(baseRestConfig *rest.Config, rm meta.RESTMapper, opts ...ActionConfigGetterOption) (ActionConfigGetter, error) {
dc, err := discovery.NewDiscoveryClientForConfig(baseRestConfig)
if err != nil {
return nil, fmt.Errorf("creating kubernetes client set: %w", err)
return nil, fmt.Errorf("create discovery client: %v", err)
}
cdc := memory.NewMemCacheClient(dc)

acg := &actionConfigGetter{
kubeClient: kc,
kubeClientSet: kcs,
debugLog: debugLog,
restClientGetter: rcg.restClientGetter,
baseRestConfig: baseRestConfig,
restMapper: rm,
discoveryClient: cdc,
}
for _, o := range opts {
o(acg)
Expand All @@ -70,6 +60,11 @@ func NewActionConfigGetter(cfg *rest.Config, rm meta.RESTMapper, log logr.Logger
if acg.objectToStorageNamespace == nil {
acg.objectToStorageNamespace = getObjectNamespace
}
if acg.objectToRestConfig == nil {
acg.objectToRestConfig = func(_ context.Context, _ client.Object, baseRestConfig *rest.Config) (*rest.Config, error) {
return rest.CopyConfig(baseRestConfig), nil
}
}
return acg, nil
}

Expand Down Expand Up @@ -97,28 +92,56 @@ func DisableStorageOwnerRefInjection(v bool) ActionConfigGetterOption {
}
}

func RestConfigMapper(f func(context.Context, client.Object, *rest.Config) (*rest.Config, error)) ActionConfigGetterOption {
return func(getter *actionConfigGetter) {
getter.objectToRestConfig = f
}
}

func getObjectNamespace(obj client.Object) (string, error) {
return obj.GetNamespace(), nil
}

type actionConfigGetter struct {
kubeClient *kube.Client
kubeClientSet kubernetes.Interface
debugLog func(string, ...interface{})
restClientGetter *restClientGetter
baseRestConfig *rest.Config
restMapper meta.RESTMapper
discoveryClient discovery.CachedDiscoveryInterface

objectToClientNamespace ObjectToStringMapper
objectToStorageNamespace ObjectToStringMapper
objectToRestConfig func(context.Context, client.Object, *rest.Config) (*rest.Config, error)
disableStorageOwnerRefInjection bool
}

func (acg *actionConfigGetter) ActionConfigFor(obj client.Object) (*action.Configuration, error) {
func (acg *actionConfigGetter) ActionConfigFor(ctx context.Context, obj client.Object) (*action.Configuration, error) {
storageNs, err := acg.objectToStorageNamespace(obj)
if err != nil {
return nil, fmt.Errorf("get storage namespace from object: %v", err)
return nil, fmt.Errorf("get storage namespace for object: %v", err)
}

restConfig, err := acg.objectToRestConfig(ctx, obj, acg.baseRestConfig)
if err != nil {
return nil, fmt.Errorf("get rest config for object: %v", err)
}

clientNamespace, err := acg.objectToClientNamespace(obj)
if err != nil {
return nil, fmt.Errorf("get client namespace for object: %v", err)
}

secretClient := acg.kubeClientSet.CoreV1().Secrets(storageNs)
rcg := newRESTClientGetter(restConfig, acg.restMapper, acg.discoveryClient, clientNamespace)
kc := kube.New(rcg)
kc.Namespace = clientNamespace

kcs, err := kc.Factory.KubernetesClientSet()
if err != nil {
return nil, fmt.Errorf("create kubernetes clientset: %v", err)
}

// Setup the debug log function that Helm will use
debugLog := getDebugLogger(ctx)

secretClient := kcs.CoreV1().Secrets(storageNs)
if !acg.disableStorageOwnerRefInjection {
ownerRef := metav1.NewControllerRef(obj, obj.GetObjectKind().GroupVersionKind())
secretClient = &ownerRefSecretClient{
Expand All @@ -127,27 +150,29 @@ func (acg *actionConfigGetter) ActionConfigFor(obj client.Object) (*action.Confi
}
}
d := driver.NewSecrets(secretClient)

// Also, use the debug log for the storage driver
d.Log = acg.debugLog
d.Log = debugLog

// Initialize the storage backend
s := storage.Init(d)

kubeClient := *acg.kubeClient
kubeClient.Namespace, err = acg.objectToClientNamespace(obj)
if err != nil {
return nil, fmt.Errorf("get client namespace from object: %v", err)
}

return &action.Configuration{
RESTClientGetter: acg.restClientGetter.ForNamespace(kubeClient.Namespace),
RESTClientGetter: rcg,
Releases: s,
KubeClient: &kubeClient,
Log: acg.debugLog,
KubeClient: kc,
Log: debugLog,
}, nil
}

func getDebugLogger(ctx context.Context) func(format string, v ...interface{}) {
logger, err := logr.FromContext(ctx)
if err != nil {
return func(format string, v ...interface{}) {}
}
return func(format string, v ...interface{}) {
logger.V(1).Info(fmt.Sprintf(format, v...))
}
}

var _ v1.SecretInterface = &ownerRefSecretClient{}

type ownerRefSecretClient struct {
Expand Down
Loading
Loading