Skip to content

Commit

Permalink
helm: use chunking release driver in systemNamespace
Browse files Browse the repository at this point in the history
Signed-off-by: Joe Lanford <joe.lanford@gmail.com>
  • Loading branch information
joelanford committed Jul 18, 2024
1 parent 0796ecf commit 011246e
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 8 deletions.
21 changes: 15 additions & 6 deletions cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,22 @@ func main() {
os.Exit(1)
}

installNamespaceMapper := helmclient.ObjectToStringMapper(func(obj client.Object) (string, error) {
ext := obj.(*ocv1alpha1.ClusterExtension)
return ext.Spec.InstallNamespace, nil
})
// Setup a custom storage driver to use. We do this for two reasons:
// 1. We need to chunk secrets to avoid hitting etcd limits on the size of a single object.
// 2. We need to avoid using the same storage driver as the Helm CLI to avoid conflicts and
// accidental inheritance of behavior (e.g. adoption of a Helm CLI-created release by
// operator-controller)
storageDriverMapper, err := action.ChunkedStorageDriverMapper(mgr, systemNamespace)
if err != nil {
setupLog.Error(err, "unable to create storage driver mapper")
os.Exit(1)
}
cfgGetter, err := helmclient.NewActionConfigGetter(mgr.GetConfig(), mgr.GetRESTMapper(),
helmclient.StorageNamespaceMapper(installNamespaceMapper),
helmclient.ClientNamespaceMapper(installNamespaceMapper),
helmclient.StorageDriverMapper(storageDriverMapper),
helmclient.ClientNamespaceMapper(func(obj client.Object) (string, error) {
ext := obj.(*ocv1alpha1.ClusterExtension)
return ext.Spec.InstallNamespace, nil
}),
)
if err != nil {
setupLog.Error(err, "unable to config for creating helm client")
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ require (
sigs.k8s.io/yaml v1.4.0
)

replace github.com/operator-framework/helm-operator-plugins => github.com/joelanford/helm-operator v0.0.8-0.20240718195442-f187d057d430

require (
cloud.google.com/go/compute/metadata v0.3.0 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,8 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/joelanford/helm-operator v0.0.8-0.20240718195442-f187d057d430 h1:xFJglVJFjVvHF63LHQZpKr81L5Lu74JGFMiXsngTMo0=
github.com/joelanford/helm-operator v0.0.8-0.20240718195442-f187d057d430/go.mod h1:5Kx1PyLnRVPyQmLq+frv+HJgSZzXG+W6LavSCxzm8sI=
github.com/joelanford/ignore v0.1.0 h1:VawbTDeg5EL+PN7W8gxVzGerfGpVo3gFdR5ZAqnkYRk=
github.com/joelanford/ignore v0.1.0/go.mod h1:Vb0PQMAQXK29fmiPjDukpO8I2NTcp1y8LbhFijD1/0o=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
Expand Down Expand Up @@ -588,8 +590,6 @@ github.com/operator-framework/api v0.26.0 h1:YVntU2NkVl5zSLLwK5kFcH6P3oSvN9QDgTs
github.com/operator-framework/api v0.26.0/go.mod h1:3IxOwzVUeGxYlzfwKCcfCyS+q3EEhWA/4kv7UehbeyM=
github.com/operator-framework/catalogd v0.18.0 h1:3eFoURYkn+vspoqYL5ijzUjAyVMSSR60BMOiKFvdVmM=
github.com/operator-framework/catalogd v0.18.0/go.mod h1:3i2dDt0yg6L/xHbNt93/Teao6xBC8rhL6UgewjQpvI8=
github.com/operator-framework/helm-operator-plugins v0.3.0 h1:LNhcb5nPT/TAxZSsKH2LTYh79RgiN2twGFptQR96sRM=
github.com/operator-framework/helm-operator-plugins v0.3.0/go.mod h1:ly6Bd9rSzmt37Wy6WtZHmA+IY9zG958MryJFLcVpCXw=
github.com/operator-framework/operator-lib v0.14.0 h1:er+BgZymZD1im2wytLJiPLZpGALAX6N0gXaHx3PKbO4=
github.com/operator-framework/operator-lib v0.14.0/go.mod h1:wUu4Xb9xzXnIpglvaZ3yucTMSlqGXHIoUEH9+5gWiu0=
github.com/operator-framework/operator-registry v1.44.0 h1:NW5/xHYR77J2EUYm+6iBER1WNGLNS8gM+G5GBQWqTTs=
Expand Down
113 changes: 113 additions & 0 deletions internal/action/storagedriver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package action

import (
"context"
"fmt"

"helm.sh/helm/v3/pkg/storage/driver"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
k8slabels "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/watch"
clientcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
crcache "sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/manager"

helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client"
"github.com/operator-framework/helm-operator-plugins/pkg/storage"
)

func ChunkedStorageDriverMapper(mgr manager.Manager, releaseStorageNamespace string) (helmclient.ObjectToStorageDriverMapper, error) {
corev1Client, err := clientcorev1.NewForConfig(mgr.GetConfig())
if err != nil {
return nil, fmt.Errorf("unable to create corev1 client: %w", err)
}
secretsClient := newSecretsDelegatingClient(corev1Client.Secrets(releaseStorageNamespace), releaseStorageNamespace, mgr.GetCache())

return func(ctx context.Context, object client.Object, config *rest.Config) (driver.Driver, error) {
log := logf.FromContext(ctx).V(2)
return storage.NewChunkedSecrets(secretsClient, "operator-controller", storage.ChunkedSecretsConfig{
ChunkSize: 1024 * 1024,
MaxReadChunks: 10,
MaxWriteChunks: 10,
Log: func(format string, args ...interface{}) { log.Info(fmt.Sprintf(format, args...)) },
}), nil
}, nil
}

var _ clientcorev1.SecretInterface = &secretsDelegatingClient{}

type secretsDelegatingClient struct {
clientcorev1.SecretInterface
namespace string
cache crcache.Cache
}

func newSecretsDelegatingClient(client clientcorev1.SecretInterface, namespace string, cache crcache.Cache) clientcorev1.SecretInterface {
return &secretsDelegatingClient{
SecretInterface: client,
namespace: namespace,
cache: cache,
}
}

func (s secretsDelegatingClient) Get(ctx context.Context, name string, opts metav1.GetOptions) (*corev1.Secret, error) {
var secret corev1.Secret
if err := s.cache.Get(ctx, client.ObjectKey{Namespace: s.namespace, Name: name}, &secret, &client.GetOptions{Raw: &opts}); err != nil {
return nil, err
}
return &secret, nil
}

func (s secretsDelegatingClient) List(ctx context.Context, opts metav1.ListOptions) (*corev1.SecretList, error) {
listOpts, err := metaOptionsToClientOptions(s.namespace, opts)
if err != nil {
return nil, err
}

var secrets corev1.SecretList
if err := s.cache.List(ctx, &secrets, listOpts); err != nil {
return nil, fmt.Errorf("error listing from cache: %w", err)
}
return &secrets, nil
}

func (s secretsDelegatingClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
panic("intentionally not implemented: watch is not intended to be called")
}

func metaOptionsToClientOptions(namespace string, opts metav1.ListOptions) (*client.ListOptions, error) {
clientListOptions := &client.ListOptions{
Namespace: namespace,
Limit: opts.Limit,
Continue: opts.Continue,
}

if opts.LabelSelector != "" {
labelSelector, err := k8slabels.Parse(opts.LabelSelector)
if err != nil {
return nil, err
}
clientListOptions.LabelSelector = labelSelector
}

if opts.FieldSelector != "" {
fieldSelector, err := fields.ParseSelector(opts.FieldSelector)
if err != nil {
return nil, err
}
clientListOptions.FieldSelector = fieldSelector
}

opts.LabelSelector = ""
opts.FieldSelector = ""
opts.Limit = 0
opts.Continue = ""
clientListOptions.Raw = &opts

return clientListOptions, nil
}

0 comments on commit 011246e

Please sign in to comment.