From c7e5542731a65e4ae8020fa7f201c24bfe3bc18b Mon Sep 17 00:00:00 2001 From: Sid Kattoju Date: Mon, 3 Jun 2024 16:08:44 -0400 Subject: [PATCH] Use SA from spec --- Makefile | 7 +- api/v1alpha1/clusterextension_types.go | 6 ++ cmd/manager/main.go | 43 +++++++- ...peratorframework.io_clusterextensions.yaml | 8 ++ .../olm_v1alpha1_clusterextension.yaml | 1 + internal/authentication/tokengetter.go | 97 +++++++++++++++++++ .../clusterextension_admission_test.go | 60 ++++++------ .../clusterextension_controller_test.go | 92 +++++++++++------- ...terextension_registryv1_validation_test.go | 6 +- test/e2e/cluster_extension_install_test.go | 31 +++--- ...r_extension_registryV1_limitations_test.go | 7 +- .../extension_developer_test.go | 5 +- test/extension-developer-e2e/setup.sh | 1 + testdata/rbac/test-rbac.yaml | 29 ++++++ 14 files changed, 303 insertions(+), 90 deletions(-) create mode 100644 internal/authentication/tokengetter.go create mode 100644 testdata/rbac/test-rbac.yaml diff --git a/Makefile b/Makefile index e4fc640af..6c411e3e7 100644 --- a/Makefile +++ b/Makefile @@ -188,7 +188,7 @@ kind-clean: $(KIND) #EXHELP Delete the kind cluster. .PHONY: kind-load-test-artifacts kind-load-test-artifacts: $(KIND) #EXHELP Load the e2e testdata container images into a kind cluster. - $(CONTAINER_RUNTIME) build testdata/bundles/registry-v1/prometheus-operator.v1.0.0 -t localhost/testdata/bundles/registry-v1/prometheus-operator:v1.0.0 + $(CONTAINER_RUNTIME) build testdata/bundles/registry-v1/prometheus-operator.v1.0.0 -t localhost/testdata/bundles/registry-v1/prometheus-operator:v1.0.0 --load $(CONTAINER_RUNTIME) tag localhost/testdata/bundles/registry-v1/prometheus-operator:v1.0.0 localhost/testdata/bundles/registry-v1/prometheus-operator:v1.0.1 $(CONTAINER_RUNTIME) tag localhost/testdata/bundles/registry-v1/prometheus-operator:v1.0.0 localhost/testdata/bundles/registry-v1/prometheus-operator:v1.2.0 $(CONTAINER_RUNTIME) tag localhost/testdata/bundles/registry-v1/prometheus-operator:v1.0.0 localhost/testdata/bundles/registry-v1/prometheus-operator:v2.0.0 @@ -196,8 +196,9 @@ kind-load-test-artifacts: $(KIND) #EXHELP Load the e2e testdata container images $(KIND) load docker-image localhost/testdata/bundles/registry-v1/prometheus-operator:v1.0.1 --name $(KIND_CLUSTER_NAME) $(KIND) load docker-image localhost/testdata/bundles/registry-v1/prometheus-operator:v1.2.0 --name $(KIND_CLUSTER_NAME) $(KIND) load docker-image localhost/testdata/bundles/registry-v1/prometheus-operator:v2.0.0 --name $(KIND_CLUSTER_NAME) - $(CONTAINER_RUNTIME) build testdata/bundles/registry-v1/package-with-webhooks.v1.0.0 -t localhost/testdata/bundles/registry-v1/package-with-webhooks:v1.0.0 + $(CONTAINER_RUNTIME) build testdata/bundles/registry-v1/package-with-webhooks.v1.0.0 -t localhost/testdata/bundles/registry-v1/package-with-webhooks:v1.0.0 --load $(KIND) load docker-image localhost/testdata/bundles/registry-v1/package-with-webhooks:v1.0.0 --name $(KIND_CLUSTER_NAME) + kubectl apply -f testdata/rbac/test-rbac.yaml -n default #SECTION Build @@ -245,7 +246,7 @@ run: docker-build kind-cluster kind-load kind-deploy #HELP Build the operator-co .PHONY: docker-build docker-build: build-linux #EXHELP Build docker image for operator-controller with GOOS=linux and local GOARCH. - $(CONTAINER_RUNTIME) build -t $(IMG) -f Dockerfile ./bin/linux + $(CONTAINER_RUNTIME) build -t $(IMG) -f Dockerfile ./bin/linux --load #SECTION Release ifeq ($(origin ENABLE_RELEASE_PIPELINE), undefined) diff --git a/api/v1alpha1/clusterextension_types.go b/api/v1alpha1/clusterextension_types.go index 557c0570d..df2895598 100644 --- a/api/v1alpha1/clusterextension_types.go +++ b/api/v1alpha1/clusterextension_types.go @@ -78,6 +78,12 @@ type ClusterExtensionSpec struct { // the bundle may contain resources that are cluster-scoped or that are // installed in a different namespace. This namespace is expected to exist. InstallNamespace string `json:"installNamespace"` + + //+kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9.]*[a-z0-9])?$ + //+kubebuilder:validation:MaxLength:=253 + // ServiceAccountName is the name of the ServiceAccount to use to manage the resources in the bundle. + // The service account is expected to exist in the InstallNamespace. + ServiceAccountName string `json:"serviceAccountName"` } const ( diff --git a/cmd/manager/main.go b/cmd/manager/main.go index ab677dc38..ae0c84670 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -17,6 +17,7 @@ limitations under the License. package main import ( + "context" "crypto/x509" "flag" "fmt" @@ -29,7 +30,10 @@ import ( "go.uber.org/zap/zapcore" k8slabels "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/selection" + "k8s.io/apimachinery/pkg/types" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" _ "k8s.io/client-go/plugin/pkg/client/auth" + "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" crcache "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" @@ -42,6 +46,7 @@ import ( "github.com/operator-framework/rukpak/pkg/storage" "github.com/operator-framework/operator-controller/api/v1alpha1" + "github.com/operator-framework/operator-controller/internal/authentication" "github.com/operator-framework/operator-controller/internal/catalogmetadata/cache" catalogclient "github.com/operator-framework/operator-controller/internal/catalogmetadata/client" "github.com/operator-framework/operator-controller/internal/controllers" @@ -155,14 +160,44 @@ func main() { cl := mgr.GetClient() catalogClient := catalogclient.New(cl, cache.NewFilesystemCache(cachePath, &http.Client{Timeout: 10 * time.Second})) - cfgGetter, err := helmclient.NewActionConfigGetter(mgr.GetConfig(), mgr.GetRESTMapper(), helmclient.StorageNamespaceMapper(func(o client.Object) (string, error) { - return systemNamespace, nil - })) + saGetter, err := corev1client.NewForConfig(ctrl.GetConfigOrDie()) if err != nil { - setupLog.Error(err, "unable to config for creating helm client") + setupLog.Error(err, "unable to create service account client") os.Exit(1) } + tg := authentication.NewTokenGetter(saGetter, 3600) + nsMapper := func(obj client.Object) (string, error) { + bd, ok := obj.(*v1alpha1.ClusterExtension) + if !ok { + return "", fmt.Errorf("cannot derive namespace from object of type %T", obj) + } + return bd.Spec.InstallNamespace, nil + } + + rcm := func(ctx context.Context, obj client.Object, baseRestConfig *rest.Config) (*rest.Config, error) { + cfg := rest.AnonymousClientConfig(rest.CopyConfig(baseRestConfig)) + ext, ok := obj.(*v1alpha1.ClusterExtension) + if !ok { + return cfg, nil + } + token, err := tg.Get(ctx, types.NamespacedName{Namespace: ext.Spec.InstallNamespace, Name: ext.Spec.ServiceAccountName}) + if err != nil { + return nil, err + } + cfg.BearerToken = token + return cfg, nil + } + + cfgGetter, err := helmclient.NewActionConfigGetter(mgr.GetConfig(), mgr.GetRESTMapper(), + helmclient.ClientNamespaceMapper(nsMapper), + helmclient.StorageNamespaceMapper(nsMapper), + helmclient.RestConfigMapper(rcm), + ) + if err != nil { + setupLog.Error(err, "unable to config for creating helm client") + os.Exit(1) + } acg, err := helmclient.NewActionClientGetter(cfgGetter) if err != nil { setupLog.Error(err, "unable to create helm client") diff --git a/config/crd/bases/olm.operatorframework.io_clusterextensions.yaml b/config/crd/bases/olm.operatorframework.io_clusterextensions.yaml index 9c41127f5..0c4947b2a 100644 --- a/config/crd/bases/olm.operatorframework.io_clusterextensions.yaml +++ b/config/crd/bases/olm.operatorframework.io_clusterextensions.yaml @@ -56,6 +56,13 @@ spec: maxLength: 48 pattern: ^[a-z0-9]+(-[a-z0-9]+)*$ type: string + serviceAccountName: + description: |- + ServiceAccountName is the name of the ServiceAccount to use to manage the resources in the bundle. + The service account is expected to exist in the InstallNamespace. + maxLength: 253 + pattern: ^[a-z0-9]([-a-z0-9.]*[a-z0-9])?$ + type: string upgradeConstraintPolicy: default: Enforce description: Defines the policy for how to handle upgrade constraints @@ -77,6 +84,7 @@ spec: required: - installNamespace - packageName + - serviceAccountName type: object status: description: ClusterExtensionStatus defines the observed state of ClusterExtension diff --git a/config/samples/olm_v1alpha1_clusterextension.yaml b/config/samples/olm_v1alpha1_clusterextension.yaml index 483ef34f1..c50316890 100644 --- a/config/samples/olm_v1alpha1_clusterextension.yaml +++ b/config/samples/olm_v1alpha1_clusterextension.yaml @@ -4,5 +4,6 @@ metadata: name: clusterextension-sample spec: installNamespace: default + serviceAccountName: default packageName: argocd-operator version: 0.6.0 diff --git a/internal/authentication/tokengetter.go b/internal/authentication/tokengetter.go new file mode 100644 index 000000000..e2eda7769 --- /dev/null +++ b/internal/authentication/tokengetter.go @@ -0,0 +1,97 @@ +package authentication + +import ( + "context" + "sync" + "time" + + authenticationv1 "k8s.io/api/authentication/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/utils/ptr" +) + +type TokenGetter struct { + client corev1client.ServiceAccountsGetter + expirationSeconds int64 + tokens map[types.NamespacedName]*authenticationv1.TokenRequestStatus + tokenLocks keyLock[types.NamespacedName] + mu sync.RWMutex +} + +func NewTokenGetter(client corev1client.ServiceAccountsGetter, expirationSeconds int64) *TokenGetter { + return &TokenGetter{ + client: client, + expirationSeconds: expirationSeconds, + tokenLocks: newKeyLock[types.NamespacedName](), + tokens: map[types.NamespacedName]*authenticationv1.TokenRequestStatus{}, + } +} + +type keyLock[K comparable] struct { + locks map[K]*sync.Mutex + mu sync.Mutex +} + +func newKeyLock[K comparable]() keyLock[K] { + return keyLock[K]{locks: map[K]*sync.Mutex{}} +} + +func (k *keyLock[K]) Lock(key K) { + k.getLock(key).Lock() +} + +func (k *keyLock[K]) Unlock(key K) { + k.getLock(key).Unlock() +} + +func (k *keyLock[K]) getLock(key K) *sync.Mutex { + k.mu.Lock() + defer k.mu.Unlock() + + lock, ok := k.locks[key] + if !ok { + lock = &sync.Mutex{} + k.locks[key] = lock + } + return lock +} + +func (t *TokenGetter) Get(ctx context.Context, key types.NamespacedName) (string, error) { + t.tokenLocks.Lock(key) + defer t.tokenLocks.Unlock(key) + + t.mu.RLock() + token, ok := t.tokens[key] + t.mu.RUnlock() + + expireTime := time.Time{} + if ok { + expireTime = token.ExpirationTimestamp.Time + } + + fiveMinutesAfterNow := metav1.Now().Add(5 * time.Minute) + if expireTime.Before(fiveMinutesAfterNow) { + var err error + token, err = t.getToken(ctx, key) + if err != nil { + return "", err + } + t.mu.Lock() + t.tokens[key] = token + t.mu.Unlock() + } + + return token.Token, nil +} + +func (t *TokenGetter) getToken(ctx context.Context, key types.NamespacedName) (*authenticationv1.TokenRequestStatus, error) { + req, err := t.client.ServiceAccounts(key.Namespace).CreateToken(ctx, key.Name, &authenticationv1.TokenRequest{Spec: authenticationv1.TokenRequestSpec{ + ExpirationSeconds: ptr.To[int64](3600), + }}, metav1.CreateOptions{}) + if err != nil { + return nil, err + } + return &req.Status, nil +} diff --git a/internal/controllers/clusterextension_admission_test.go b/internal/controllers/clusterextension_admission_test.go index 16629c410..fc21a7fd7 100644 --- a/internal/controllers/clusterextension_admission_test.go +++ b/internal/controllers/clusterextension_admission_test.go @@ -43,8 +43,9 @@ func TestClusterExtensionAdmissionPackageName(t *testing.T) { t.Parallel() cl := newClient(t) err := cl.Create(context.Background(), buildClusterExtension(ocv1alpha1.ClusterExtensionSpec{ - PackageName: tc.pkgName, - InstallNamespace: "default", + PackageName: tc.pkgName, + InstallNamespace: "default", + ServiceAccountName: "default", })) if tc.errMsg == "" { require.NoError(t, err, "unexpected error for package name %q: %w", tc.pkgName, err) @@ -131,9 +132,10 @@ func TestClusterExtensionAdmissionVersion(t *testing.T) { t.Parallel() cl := newClient(t) err := cl.Create(context.Background(), buildClusterExtension(ocv1alpha1.ClusterExtensionSpec{ - PackageName: "package", - Version: tc.version, - InstallNamespace: "default", + PackageName: "package", + Version: tc.version, + InstallNamespace: "default", + ServiceAccountName: "default", })) if tc.errMsg == "" { require.NoError(t, err, "unexpected error for version %q: %w", tc.version, err) @@ -176,9 +178,10 @@ func TestClusterExtensionAdmissionChannel(t *testing.T) { t.Parallel() cl := newClient(t) err := cl.Create(context.Background(), buildClusterExtension(ocv1alpha1.ClusterExtensionSpec{ - PackageName: "package", - Channel: tc.channelName, - InstallNamespace: "default", + PackageName: "package", + Channel: tc.channelName, + InstallNamespace: "default", + ServiceAccountName: "default", })) if tc.errMsg == "" { require.NoError(t, err, "unexpected error for channel %q: %w", tc.channelName, err) @@ -195,24 +198,25 @@ func TestClusterExtensionAdmissionInstallNamespace(t *testing.T) { regexMismatchError := "spec.installNamespace in body should match" testCases := []struct { - name string - installNamespace string - errMsg string + name string + installNamespace string + serviceAccountName string + errMsg string }{ - {"just alphanumeric", "justalphanumberic1", ""}, - {"hypen-separated", "hyphenated-name", ""}, - {"no install namespace", "", regexMismatchError}, - {"dot-separated", "dotted.name", regexMismatchError}, - {"longest valid install namespace", strings.Repeat("x", 63), ""}, - {"too long install namespace name", strings.Repeat("x", 64), tooLongError}, - {"spaces", "spaces spaces", regexMismatchError}, - {"capitalized", "Capitalized", regexMismatchError}, - {"camel case", "camelCase", regexMismatchError}, - {"invalid characters", "many/invalid$characters+in_name", regexMismatchError}, - {"starts with hyphen", "-start-with-hyphen", regexMismatchError}, - {"ends with hyphen", "end-with-hyphen-", regexMismatchError}, - {"starts with period", ".start-with-period", regexMismatchError}, - {"ends with period", "end-with-period.", regexMismatchError}, + {"just alphanumeric", "justalphanumberic1", "justalphanumberic1", ""}, + {"hypen-separated", "hyphenated-name", "hyphenated-name", ""}, + {"no install namespace", "", "", regexMismatchError}, + {"dot-separated", "dotted.name", "dotted.name", regexMismatchError}, + {"longest valid install namespace", strings.Repeat("x", 63), strings.Repeat("x", 63), ""}, + {"too long install namespace name", strings.Repeat("x", 64), strings.Repeat("x", 64), tooLongError}, + {"spaces", "spaces spaces", "spaces spaces", regexMismatchError}, + {"capitalized", "Capitalized", "Capitalized", regexMismatchError}, + {"camel case", "camelCase", "camelCase", regexMismatchError}, + {"invalid characters", "many/invalid$characters+in_name", "many/invalid$characters+in_name", regexMismatchError}, + {"starts with hyphen", "-start-with-hyphen", "-start-with-hyphen", regexMismatchError}, + {"ends with hyphen", "end-with-hyphen-", "end-with-hyphen-", regexMismatchError}, + {"starts with period", ".start-with-period", ".start-with-period", regexMismatchError}, + {"ends with period", "end-with-period.", "end-with-period.", regexMismatchError}, } t.Parallel() @@ -222,11 +226,13 @@ func TestClusterExtensionAdmissionInstallNamespace(t *testing.T) { t.Parallel() cl := newClient(t) err := cl.Create(context.Background(), buildClusterExtension(ocv1alpha1.ClusterExtensionSpec{ - PackageName: "package", - InstallNamespace: tc.installNamespace, + PackageName: "package", + InstallNamespace: tc.installNamespace, + ServiceAccountName: tc.serviceAccountName, })) if tc.errMsg == "" { require.NoError(t, err, "unexpected error for installNamespace %q: %w", tc.installNamespace, err) + require.NoError(t, err, "unexpected error for serviceAccountName %q: %w", tc.serviceAccountName, err) } else { require.Error(t, err) require.Contains(t, err.Error(), tc.errMsg) diff --git a/internal/controllers/clusterextension_controller_test.go b/internal/controllers/clusterextension_controller_test.go index efa1191c8..31acd4a07 100644 --- a/internal/controllers/clusterextension_controller_test.go +++ b/internal/controllers/clusterextension_controller_test.go @@ -53,8 +53,9 @@ func TestClusterExtensionNonExistentPackage(t *testing.T) { clusterExtension := &ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, Spec: ocv1alpha1.ClusterExtensionSpec{ - PackageName: pkgName, - InstallNamespace: "default", + PackageName: pkgName, + InstallNamespace: "default", + ServiceAccountName: "default", }, } require.NoError(t, cl.Create(ctx, clusterExtension)) @@ -94,9 +95,10 @@ func TestClusterExtensionNonExistentVersion(t *testing.T) { clusterExtension := &ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, Spec: ocv1alpha1.ClusterExtensionSpec{ - PackageName: pkgName, - Version: "0.50.0", // this version of the package does not exist - InstallNamespace: "default", + PackageName: pkgName, + Version: "0.50.0", // this version of the package does not exist + InstallNamespace: "default", + ServiceAccountName: "default", }, } require.NoError(t, cl.Create(ctx, clusterExtension)) @@ -146,14 +148,16 @@ func TestClusterExtensionChannelVersionExists(t *testing.T) { pkgVer := "1.0.0" pkgChan := "beta" installNamespace := fmt.Sprintf("test-ns-%s", rand.String(8)) + serviceAccountName := fmt.Sprintf("test-sa-%s", rand.String(8)) clusterExtension := &ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, Spec: ocv1alpha1.ClusterExtensionSpec{ - PackageName: pkgName, - Version: pkgVer, - Channel: pkgChan, - InstallNamespace: installNamespace, + PackageName: pkgName, + Version: pkgVer, + Channel: pkgChan, + InstallNamespace: installNamespace, + ServiceAccountName: serviceAccountName, }, } err := cl.Create(ctx, clusterExtension) @@ -205,13 +209,16 @@ func TestClusterExtensionChannelExistsNoVersion(t *testing.T) { pkgVer := "" pkgChan := "beta" installNamespace := fmt.Sprintf("test-ns-%s", rand.String(8)) + serviceAccountName := fmt.Sprintf("test-sa-%s", rand.String(8)) + clusterExtension := &ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, Spec: ocv1alpha1.ClusterExtensionSpec{ - PackageName: pkgName, - Version: pkgVer, - Channel: pkgChan, - InstallNamespace: installNamespace, + PackageName: pkgName, + Version: pkgVer, + Channel: pkgChan, + InstallNamespace: installNamespace, + ServiceAccountName: serviceAccountName, }, } err := cl.Create(ctx, clusterExtension) @@ -260,10 +267,11 @@ func TestClusterExtensionVersionNoChannel(t *testing.T) { clusterExtension := &ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, Spec: ocv1alpha1.ClusterExtensionSpec{ - PackageName: pkgName, - Version: pkgVer, - Channel: pkgChan, - InstallNamespace: "default", + PackageName: pkgName, + Version: pkgVer, + Channel: pkgChan, + InstallNamespace: "default", + ServiceAccountName: "default", }, } require.NoError(t, cl.Create(ctx, clusterExtension)) @@ -309,9 +317,10 @@ func TestClusterExtensionNoChannel(t *testing.T) { clusterExtension := &ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, Spec: ocv1alpha1.ClusterExtensionSpec{ - PackageName: pkgName, - Channel: pkgChan, - InstallNamespace: "default", + PackageName: pkgName, + Channel: pkgChan, + InstallNamespace: "default", + ServiceAccountName: "default", }, } require.NoError(t, cl.Create(ctx, clusterExtension)) @@ -358,10 +367,11 @@ func TestClusterExtensionNoVersion(t *testing.T) { clusterExtension := &ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, Spec: ocv1alpha1.ClusterExtensionSpec{ - PackageName: pkgName, - Version: pkgVer, - Channel: pkgChan, - InstallNamespace: "default", + PackageName: pkgName, + Version: pkgVer, + Channel: pkgChan, + InstallNamespace: "default", + ServiceAccountName: "default", }, } require.NoError(t, cl.Create(ctx, clusterExtension)) @@ -451,14 +461,16 @@ func TestClusterExtensionUpgrade(t *testing.T) { pkgVer := "1.0.0" pkgChan := "beta" installNamespace := fmt.Sprintf("test-ns-%s", rand.String(8)) + serviceAccountName := fmt.Sprintf("test-sa-%s", rand.String(8)) extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} clusterExtension := &ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, Spec: ocv1alpha1.ClusterExtensionSpec{ - PackageName: pkgName, - Version: pkgVer, - Channel: pkgChan, - InstallNamespace: installNamespace, + PackageName: pkgName, + Version: pkgVer, + Channel: pkgChan, + InstallNamespace: installNamespace, + ServiceAccountName: serviceAccountName, }, } // Create a cluster extension @@ -559,14 +571,16 @@ func TestClusterExtensionUpgrade(t *testing.T) { pkgVer := "1.0.0" pkgChan := "beta" installNamespace := fmt.Sprintf("test-ns-%s", rand.String(8)) + serviceAccountName := fmt.Sprintf("test-sa-%s", rand.String(8)) extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} clusterExtension := &ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, Spec: ocv1alpha1.ClusterExtensionSpec{ - PackageName: pkgName, - Version: pkgVer, - Channel: pkgChan, - InstallNamespace: installNamespace, + PackageName: pkgName, + Version: pkgVer, + Channel: pkgChan, + InstallNamespace: installNamespace, + ServiceAccountName: serviceAccountName, }, } // Create a cluster extension @@ -679,6 +693,7 @@ func TestClusterExtensionUpgrade(t *testing.T) { }() installNamespace := fmt.Sprintf("test-ns-%s", rand.String(8)) + serviceAccountName := fmt.Sprintf("test-sa-%s", rand.String(8)) extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} clusterExtension := &ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, @@ -688,6 +703,7 @@ func TestClusterExtensionUpgrade(t *testing.T) { Channel: "beta", UpgradeConstraintPolicy: ocv1alpha1.UpgradeConstraintPolicyIgnore, InstallNamespace: installNamespace, + ServiceAccountName: serviceAccountName, }, } // Create a cluster extension @@ -789,14 +805,16 @@ func TestClusterExtensionDowngrade(t *testing.T) { }() installNamespace := fmt.Sprintf("test-ns-%s", rand.String(8)) + serviceAccountName := fmt.Sprintf("test-sa-%s", rand.String(8)) extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} clusterExtension := &ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, Spec: ocv1alpha1.ClusterExtensionSpec{ - PackageName: "prometheus", - Version: "1.0.1", - Channel: "beta", - InstallNamespace: installNamespace, + PackageName: "prometheus", + Version: "1.0.1", + Channel: "beta", + InstallNamespace: installNamespace, + ServiceAccountName: serviceAccountName, }, } // Create a cluster extension @@ -887,6 +905,7 @@ func TestClusterExtensionDowngrade(t *testing.T) { }() installNamespace := fmt.Sprintf("test-ns-%s", rand.String(8)) + serviceAccountName := fmt.Sprintf("test-sa-%s", rand.String(8)) extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} clusterExtension := &ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, @@ -896,6 +915,7 @@ func TestClusterExtensionDowngrade(t *testing.T) { Channel: "beta", UpgradeConstraintPolicy: ocv1alpha1.UpgradeConstraintPolicyIgnore, InstallNamespace: installNamespace, + ServiceAccountName: serviceAccountName, }, } // Create a cluster extension diff --git a/internal/controllers/clusterextension_registryv1_validation_test.go b/internal/controllers/clusterextension_registryv1_validation_test.go index a828a3e05..fa6b983be 100644 --- a/internal/controllers/clusterextension_registryv1_validation_test.go +++ b/internal/controllers/clusterextension_registryv1_validation_test.go @@ -120,12 +120,14 @@ func TestClusterExtensionRegistryV1DisallowDependencies(t *testing.T) { } installNamespace := fmt.Sprintf("test-ns-%s", rand.String(8)) + serviceAccountName := fmt.Sprintf("test-sa-%s", rand.String(8)) extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} clusterExtension := &ocv1alpha1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{Name: extKey.Name}, Spec: ocv1alpha1.ClusterExtensionSpec{ - PackageName: tt.bundle.Package, - InstallNamespace: installNamespace, + PackageName: tt.bundle.Package, + InstallNamespace: installNamespace, + ServiceAccountName: serviceAccountName, }, } require.NoError(t, cl.Create(ctx, clusterExtension)) diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go index 5eca7747c..753838a6d 100644 --- a/test/e2e/cluster_extension_install_test.go +++ b/test/e2e/cluster_extension_install_test.go @@ -72,8 +72,9 @@ func TestClusterExtensionInstallRegistry(t *testing.T) { defer getArtifactsOutput(t) clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ - PackageName: "prometheus", - InstallNamespace: "default", + PackageName: "prometheus", + InstallNamespace: "default", + ServiceAccountName: "default", } t.Log("It resolves the specified package with correct bundle path") t.Log("By creating the ClusterExtension resource") @@ -129,8 +130,9 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) { pkgName := "prometheus" clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ - PackageName: pkgName, - InstallNamespace: "default", + PackageName: pkgName, + InstallNamespace: "default", + ServiceAccountName: "default", } t.Log("By deleting the catalog first") @@ -191,9 +193,10 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) { t.Log("By creating an ClusterExtension at a specified version") clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ - PackageName: "prometheus", - Version: "1.0.0", - InstallNamespace: "default", + PackageName: "prometheus", + Version: "1.0.0", + InstallNamespace: "default", + ServiceAccountName: "default", } require.NoError(t, c.Create(context.Background(), clusterExtension)) t.Log("By eventually reporting a successful installation") @@ -237,9 +240,10 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) { t.Log("By creating an ClusterExtension at a specified version") clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ - PackageName: "prometheus", - Version: "1.0.0", - InstallNamespace: "default", + PackageName: "prometheus", + Version: "1.0.0", + InstallNamespace: "default", + ServiceAccountName: "default", } require.NoError(t, c.Create(context.Background(), clusterExtension)) t.Log("By eventually reporting a successful resolution") @@ -282,9 +286,10 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) { t.Log("By creating an ClusterExtension at a specified version") clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ - PackageName: "prometheus", - Version: "1.0.0", - InstallNamespace: "default", + PackageName: "prometheus", + Version: "1.0.0", + InstallNamespace: "default", + ServiceAccountName: "default", } require.NoError(t, c.Create(context.Background(), clusterExtension)) t.Log("By eventually reporting a successful resolution") diff --git a/test/e2e/cluster_extension_registryV1_limitations_test.go b/test/e2e/cluster_extension_registryV1_limitations_test.go index 824b1f686..c6317c6c7 100644 --- a/test/e2e/cluster_extension_registryV1_limitations_test.go +++ b/test/e2e/cluster_extension_registryV1_limitations_test.go @@ -20,9 +20,10 @@ func TestClusterExtensionPackagesWithWebhooksAreNotAllowed(t *testing.T) { defer getArtifactsOutput(t) clusterExtension.Spec = ocv1alpha1.ClusterExtensionSpec{ - PackageName: "package-with-webhooks", - Version: "1.0.0", - InstallNamespace: "default", + PackageName: "package-with-webhooks", + Version: "1.0.0", + InstallNamespace: "default", + ServiceAccountName: "default", } require.NoError(t, c.Create(ctx, clusterExtension)) require.EventuallyWithT(t, func(ct *assert.CollectT) { diff --git a/test/extension-developer-e2e/extension_developer_test.go b/test/extension-developer-e2e/extension_developer_test.go index 4b4bbe219..dded3e6cf 100644 --- a/test/extension-developer-e2e/extension_developer_test.go +++ b/test/extension-developer-e2e/extension_developer_test.go @@ -37,8 +37,9 @@ func TestExtensionDeveloper(t *testing.T) { Name: "registryv1", }, Spec: ocv1alpha1.ClusterExtensionSpec{ - PackageName: os.Getenv("REG_PKG_NAME"), - InstallNamespace: "default", + PackageName: os.Getenv("REG_PKG_NAME"), + InstallNamespace: "default", + ServiceAccountName: "default", }, }, } diff --git a/test/extension-developer-e2e/setup.sh b/test/extension-developer-e2e/setup.sh index 776770459..1252897a3 100755 --- a/test/extension-developer-e2e/setup.sh +++ b/test/extension-developer-e2e/setup.sh @@ -154,6 +154,7 @@ EOF kubectl create configmap -n "${namespace}" --from-file="${TMP_ROOT}"/catalog.Dockerfile extension-dev-e2e.dockerfile kubectl create configmap -n "${namespace}" --from-file="${TMP_ROOT}"/catalog extension-dev-e2e.build-contents +kubectl apply -f testdata/rbac/test-rbac.yaml -n default kubectl apply -f - << EOF apiVersion: batch/v1 diff --git a/testdata/rbac/test-rbac.yaml b/testdata/rbac/test-rbac.yaml new file mode 100644 index 000000000..6e0ece311 --- /dev/null +++ b/testdata/rbac/test-rbac.yaml @@ -0,0 +1,29 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: default + namespace: default +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + namespace: default + name: test-clusterrole +rules: +- apiGroups: ["*"] + resources: ["*"] + verbs: ["*"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: test-clusterrolebinding + namespace: default +subjects: +- kind: ServiceAccount + name: default + namespace: default +roleRef: + kind: ClusterRole + name: test-clusterrole + apiGroup: rbac.authorization.k8s.io