diff --git a/docs/draft/howto/single-ownnamespace-install.md b/docs/draft/howto/single-ownnamespace-install.md index 866bf1da5..fc36de0e7 100644 --- a/docs/draft/howto/single-ownnamespace-install.md +++ b/docs/draft/howto/single-ownnamespace-install.md @@ -91,8 +91,6 @@ apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: argocd - annotations: - olm.operatorframework.io/watch-namespace: argocd spec: namespace: argocd serviceAccount: diff --git a/hack/demo/own-namespace-demo-script.sh b/hack/demo/own-namespace-demo-script.sh index 6b867fbad..611c6dfb0 100755 --- a/hack/demo/own-namespace-demo-script.sh +++ b/hack/demo/own-namespace-demo-script.sh @@ -3,7 +3,14 @@ # # Welcome to the OwnNamespace install mode demo # -trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT +set -e +trap 'echo "Demo ran into error"; trap - SIGTERM && kill -- -$$; exit 1' ERR SIGINT SIGTERM EXIT + +# install experimental CRDs with config field support +kubectl apply -f "$(dirname "${BASH_SOURCE[0]}")/../../manifests/experimental.yaml" + +# wait for experimental CRDs to be available +kubectl wait --for condition=established --timeout=60s crd/clusterextensions.olm.operatorframework.io # enable 'SingleOwnNamespaceInstallSupport' feature gate kubectl patch deployment -n olmv1-system operator-controller-controller-manager --type='json' -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--feature-gates=SingleOwnNamespaceInstallSupport=true"}]' @@ -41,3 +48,27 @@ kubectl get deployments -n argocd-system argocd-operator-controller-manager -o j # check service account for role binding is the same as controller service-account rolebinding=$(kubectl get rolebindings -n argocd-system -o name | grep 'argocd-operator' | head -n 1) kubectl get -n argocd-system $rolebinding -o jsonpath='{.subjects}' | jq .[0] + +echo "Demo completed successfully!" + +# cleanup resources +echo "Cleaning up demo resources..." +kubectl delete clusterextension argocd-operator --ignore-not-found=true +kubectl delete namespace argocd-system --ignore-not-found=true +kubectl delete clusterrolebinding argocd-installer-crb --ignore-not-found=true + +# remove feature gate from deployment +echo "Removing feature gate from operator-controller..." +kubectl patch deployment -n olmv1-system operator-controller-controller-manager --type='json' -p='[{"op": "remove", "path": "/spec/template/spec/containers/0/args", "value": "--feature-gates=SingleOwnNamespaceInstallSupport=true"}]' || true + +# restore standard CRDs +echo "Restoring standard CRDs..." +kubectl apply -f "$(dirname "${BASH_SOURCE[0]}")/../../manifests/base.yaml" + +# wait for standard CRDs to be available +kubectl wait --for condition=established --timeout=60s crd/clusterextensions.olm.operatorframework.io + +# wait for operator-controller to become available with standard config +kubectl rollout status -n olmv1-system deployment/operator-controller-controller-manager + +echo "Demo cleanup completed!" diff --git a/hack/demo/resources/own-namespace-demo.yaml b/hack/demo/resources/own-namespace-demo.yaml index f1d6da927..b22db7aa0 100644 --- a/hack/demo/resources/own-namespace-demo.yaml +++ b/hack/demo/resources/own-namespace-demo.yaml @@ -2,9 +2,6 @@ apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: argocd-operator - annotations: - # watch namespace is the same as intall namespace - olm.operatorframework.io/watch-namespace: argocd-system spec: namespace: argocd-system serviceAccount: diff --git a/hack/demo/resources/single-namespace-demo.yaml b/hack/demo/resources/single-namespace-demo.yaml index 2403bc6a8..9c1ac17f9 100644 --- a/hack/demo/resources/single-namespace-demo.yaml +++ b/hack/demo/resources/single-namespace-demo.yaml @@ -2,13 +2,14 @@ apiVersion: olm.operatorframework.io/v1 kind: ClusterExtension metadata: name: argocd-operator - annotations: - # watch-namespace is different from install namespace - olm.operatorframework.io/watch-namespace: argocd spec: namespace: argocd-system serviceAccount: name: argocd-installer + config: + configType: Inline + inline: + watchNamespace: argocd source: sourceType: Catalog catalog: diff --git a/hack/demo/single-own-namespace-demo-script.sh b/hack/demo/single-namespace-demo-script.sh similarity index 57% rename from hack/demo/single-own-namespace-demo-script.sh rename to hack/demo/single-namespace-demo-script.sh index 4e243f9df..970268415 100755 --- a/hack/demo/single-own-namespace-demo-script.sh +++ b/hack/demo/single-namespace-demo-script.sh @@ -3,7 +3,14 @@ # # Welcome to the SingleNamespace install mode demo # -trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT +set -e +trap 'echo "Demo ran into error"; trap - SIGTERM && kill -- -$$; exit 1' ERR SIGINT SIGTERM EXIT + +# install experimental CRDs with config field support +kubectl apply -f "$(dirname "${BASH_SOURCE[0]}")/../../manifests/experimental.yaml" + +# wait for experimental CRDs to be available +kubectl wait --for condition=established --timeout=60s crd/clusterextensions.olm.operatorframework.io # enable 'SingleOwnNamespaceInstallSupport' feature gate kubectl patch deployment -n olmv1-system operator-controller-controller-manager --type='json' -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--feature-gates=SingleOwnNamespaceInstallSupport=true"}]' @@ -44,3 +51,27 @@ kubectl get deployments -n argocd-system argocd-operator-controller-manager -o j # check service account for role binding is the controller deployment service account rolebinding=$(kubectl get rolebindings -n argocd -o name | grep 'argocd-operator' | head -n 1) kubectl get -n argocd $rolebinding -o jsonpath='{.subjects}' | jq .[0] + +echo "Demo completed successfully!" + +# cleanup resources +echo "Cleaning up demo resources..." +kubectl delete clusterextension argocd-operator --ignore-not-found=true +kubectl delete namespace argocd-system argocd --ignore-not-found=true +kubectl delete clusterrolebinding argocd-installer-crb --ignore-not-found=true + +# remove feature gate from deployment +echo "Removing feature gate from operator-controller..." +kubectl patch deployment -n olmv1-system operator-controller-controller-manager --type='json' -p='[{"op": "remove", "path": "/spec/template/spec/containers/0/args", "value": "--feature-gates=SingleOwnNamespaceInstallSupport=true"}]' || true + +# restore standard CRDs +echo "Restoring standard CRDs..." +kubectl apply -f "$(dirname "${BASH_SOURCE[0]}")/../../manifests/base.yaml" + +# wait for standard CRDs to be available +kubectl wait --for condition=established --timeout=60s crd/clusterextensions.olm.operatorframework.io + +# wait for operator-controller to become available with standard config +kubectl rollout status -n olmv1-system deployment/operator-controller-controller-manager + +echo "Demo cleanup completed!" diff --git a/internal/operator-controller/applier/watchnamespace.go b/internal/operator-controller/applier/watchnamespace.go index a89c95d29..4ef2b2267 100644 --- a/internal/operator-controller/applier/watchnamespace.go +++ b/internal/operator-controller/applier/watchnamespace.go @@ -10,10 +10,6 @@ import ( "github.com/operator-framework/operator-controller/internal/operator-controller/features" ) -const ( - AnnotationClusterExtensionWatchNamespace = "olm.operatorframework.io/watch-namespace" -) - // GetWatchNamespace determines the watch namespace the ClusterExtension should use // Note: this is a temporary artifice to enable gated use of single/own namespace install modes // for registry+v1 bundles. This will go away once the ClusterExtension API is updated to include @@ -32,8 +28,6 @@ func GetWatchNamespace(ext *ocv1.ClusterExtension) (string, error) { return "", fmt.Errorf("invalid bundle configuration: %w", err) } watchNamespace = cfg.WatchNamespace - } else if _, ok := ext.Annotations[AnnotationClusterExtensionWatchNamespace]; ok { - watchNamespace = ext.Annotations[AnnotationClusterExtensionWatchNamespace] } else { return "", nil } diff --git a/internal/operator-controller/applier/watchnamespace_test.go b/internal/operator-controller/applier/watchnamespace_test.go index 4745f3dc5..274ee7212 100644 --- a/internal/operator-controller/applier/watchnamespace_test.go +++ b/internal/operator-controller/applier/watchnamespace_test.go @@ -39,13 +39,13 @@ func TestGetWatchNamespace(t *testing.T) { for _, tt := range []struct { name string want string - csv *ocv1.ClusterExtension + ce *ocv1.ClusterExtension expectError bool }{ { - name: "cluster extension does not configure a watch namespace", + name: "no watch namespace is configured in a ClusterExtension CR", want: corev1.NamespaceAll, - csv: &ocv1.ClusterExtension{ + ce: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: "extension", Annotations: nil, @@ -54,9 +54,9 @@ func TestGetWatchNamespace(t *testing.T) { }, expectError: false, }, { - name: "cluster extension configures a watch namespace", + name: "a watch namespace is configured in a ClusterExtension CR", want: "watch-namespace", - csv: &ocv1.ClusterExtension{ + ce: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: "extension", }, @@ -71,63 +71,41 @@ func TestGetWatchNamespace(t *testing.T) { }, expectError: false, }, { - name: "cluster extension configures a watch namespace through annotation", - want: "watch-namespace", - csv: &ocv1.ClusterExtension{ + name: "a watch namespace is configured in a ClusterExtension CR but with invalid namespace", + ce: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: "extension", - Annotations: map[string]string{ - "olm.operatorframework.io/watch-namespace": "watch-namespace", - }, }, - }, - expectError: false, - }, { - name: "cluster extension configures a watch namespace through annotation with invalid ns", - csv: &ocv1.ClusterExtension{ - ObjectMeta: metav1.ObjectMeta{ - Name: "extension", - Annotations: map[string]string{ - "olm.operatorframework.io/watch-namespace": "watch-namespace-", - }, - }, - }, - expectError: true, - }, { - name: "cluster extension configures a watch namespace through annotation with empty ns", - csv: &ocv1.ClusterExtension{ - ObjectMeta: metav1.ObjectMeta{ - Name: "extension", - Annotations: map[string]string{ - "olm.operatorframework.io/watch-namespace": "", + Spec: ocv1.ClusterExtensionSpec{ + Config: &ocv1.ClusterExtensionConfig{ + ConfigType: ocv1.ClusterExtensionConfigTypeInline, + Inline: &apiextensionsv1.JSON{ + Raw: []byte(`{"watchNamespace":"watch-namespace-"}`), + }, }, }, }, expectError: true, }, { - name: "cluster extension configures a watch namespace through annotation and config (take config)", - want: "watch-namespace", - csv: &ocv1.ClusterExtension{ + name: "a watch namespace is configured in a ClusterExtension CR with an empty string as the namespace", + ce: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: "extension", - Annotations: map[string]string{ - "olm.operatorframework.io/watch-namespace": "dont-use-this-watch-namespace", - }, }, Spec: ocv1.ClusterExtensionSpec{ Config: &ocv1.ClusterExtensionConfig{ ConfigType: ocv1.ClusterExtensionConfigTypeInline, Inline: &apiextensionsv1.JSON{ - Raw: []byte(`{"watchNamespace":"watch-namespace"}`), + Raw: []byte(`{"watchNamespace":""}`), }, }, }, }, - expectError: false, + expectError: true, }, { - name: "cluster extension configures an invalid watchNamespace: multiple watch namespaces", + name: "an invalid watchNamespace value is configured in a ClusterExtension CR: multiple watch namespaces", want: "", - csv: &ocv1.ClusterExtension{ + ce: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: "extension", }, @@ -142,9 +120,9 @@ func TestGetWatchNamespace(t *testing.T) { }, expectError: true, }, { - name: "cluster extension configures an invalid watchNamespace: invalid name", + name: "an invalid watchNamespace value is configured in a ClusterExtension CR: invalid name", want: "", - csv: &ocv1.ClusterExtension{ + ce: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: "extension", }, @@ -159,9 +137,9 @@ func TestGetWatchNamespace(t *testing.T) { }, expectError: true, }, { - name: "cluster extension configures an invalid watchNamespace: invalid json", + name: "an invalid watchNamespace value is configured in a ClusterExtension CR: invalid json", want: "", - csv: &ocv1.ClusterExtension{ + ce: &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: "extension", }, @@ -178,7 +156,7 @@ func TestGetWatchNamespace(t *testing.T) { }, } { t.Run(tt.name, func(t *testing.T) { - got, err := applier.GetWatchNamespace(tt.csv) + got, err := applier.GetWatchNamespace(tt.ce) require.Equal(t, tt.want, got) require.Equal(t, tt.expectError, err != nil) })