diff --git a/.bingo/Variables.mk b/.bingo/Variables.mk index 280913c46..fe814c517 100644 --- a/.bingo/Variables.mk +++ b/.bingo/Variables.mk @@ -95,3 +95,9 @@ $(SETUP_ENVTEST): $(BINGO_DIR)/setup-envtest.mod @echo "(re)installing $(GOBIN)/setup-envtest-v0.0.0-20250620151452-b9a9ca01fd37" @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=setup-envtest.mod -o=$(GOBIN)/setup-envtest-v0.0.0-20250620151452-b9a9ca01fd37 "sigs.k8s.io/controller-runtime/tools/setup-envtest" +YAMLFMT := $(GOBIN)/yamlfmt-v0.20.0 +$(YAMLFMT): $(BINGO_DIR)/yamlfmt.mod + @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. + @echo "(re)installing $(GOBIN)/yamlfmt-v0.20.0" + @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=yamlfmt.mod -o=$(GOBIN)/yamlfmt-v0.20.0 "github.com/google/yamlfmt/cmd/yamlfmt" + diff --git a/.bingo/variables.env b/.bingo/variables.env index b1e721562..64c92fc14 100644 --- a/.bingo/variables.env +++ b/.bingo/variables.env @@ -34,3 +34,5 @@ OPM="${GOBIN}/opm-v1.51.0" SETUP_ENVTEST="${GOBIN}/setup-envtest-v0.0.0-20250620151452-b9a9ca01fd37" +YAMLFMT="${GOBIN}/yamlfmt-v0.20.0" + diff --git a/.bingo/yamlfmt.mod b/.bingo/yamlfmt.mod new file mode 100644 index 000000000..152ea9ecb --- /dev/null +++ b/.bingo/yamlfmt.mod @@ -0,0 +1,5 @@ +module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT + +go 1.24.4 + +require github.com/google/yamlfmt v0.20.0 // cmd/yamlfmt diff --git a/.bingo/yamlfmt.sum b/.bingo/yamlfmt.sum new file mode 100644 index 000000000..e9450c191 --- /dev/null +++ b/.bingo/yamlfmt.sum @@ -0,0 +1,16 @@ +github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q= +github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/yamlfmt v0.20.0 h1:EfMuMFEZGnXPn2NY+KgJTH9sNs6P+am/Of6IwE2K6P8= +github.com/google/yamlfmt v0.20.0/go.mod h1:gs0UEklJOYkUJ+OOCG0hg9n+DzucKDPlJElTUasVNK8= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI= +github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/.gitignore b/.gitignore index c1b590a02..abd509daf 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,9 @@ vendor/ \#*\# .\#* +# AI temp files files +.claude/ + # documentation website asset folder site @@ -45,5 +48,5 @@ site .catalogd-tmp/ .vscode -# Tmporary files and directories +# Temporary files and directories /test/regression/convert/testdata/tmp/* diff --git a/Makefile b/Makefile index 08eb28630..e55a9a662 100644 --- a/Makefile +++ b/Makefile @@ -152,7 +152,7 @@ update-crds: # # Override HELM_SETTINGS on the command line to include additional Helm settings # e.g. make HELM_SETTINGS="options.openshift.enabled=true" manifests -# e.g. make HELM_SETTINGS="operatorControllerFeatures={WebhookProviderCertManager}" manifests +# e.g. make HELM_SETTINGS="options.operatorController.features.enabled={WebhookProviderCertManager}" manifests # MANIFESTS ?= $(STANDARD_MANIFEST) $(STANDARD_E2E_MANIFEST) $(EXPERIMENTAL_MANIFEST) $(EXPERIMENTAL_E2E_MANIFEST) $(STANDARD_MANIFEST) ?= helm/cert-manager.yaml @@ -186,8 +186,9 @@ fix-lint: $(GOLANGCI_LINT) #EXHELP Fix lint issues $(GOLANGCI_LINT) run --fix --build-tags $(GO_BUILD_TAGS) $(GOLANGCI_LINT_ARGS) .PHONY: fmt -fmt: #EXHELP Formats code +fmt: $(YAMLFMT) #EXHELP Formats code go fmt ./... + $(YAMLFMT) -gitignore_excludes testdata .PHONY: update-tls-profiles update-tls-profiles: $(GOJQ) #EXHELP Update TLS profiles from the Mozilla wiki diff --git a/commitchecker.yaml b/commitchecker.yaml index 752b7004b..89134d34d 100644 --- a/commitchecker.yaml +++ b/commitchecker.yaml @@ -1,4 +1,4 @@ -expectedMergeBase: 926d57e1e6559f7ac0ef69a47b3d50d96975ec38 +expectedMergeBase: f71870daded48936c5d2012211a885b339d1182f upstreamBranch: main upstreamOrg: operator-framework upstreamRepo: operator-controller diff --git a/docs/draft/howto/single-ownnamespace-install.md b/docs/draft/howto/single-ownnamespace-install.md index fc36de0e7..8e6f00fe4 100644 --- a/docs/draft/howto/single-ownnamespace-install.md +++ b/docs/draft/howto/single-ownnamespace-install.md @@ -56,11 +56,43 @@ kubectl rollout status -n olmv1-system deployment/operator-controller-controller ## Configuring the `ClusterExtension` A `ClusterExtension` can be configured to install bundle in `Single-` or `OwnNamespace` mode through the -`.spec.config.inline.watchNamespace` property. The *installMode* is inferred in the following way: - - - *AllNamespaces*: `watchNamespace` is empty, or not set - - *OwnNamespace*: `watchNamespace` is the install namespace (i.e. `.spec.namespace`) - - *SingleNamespace*: `watchNamespace` *not* the install namespace +`.spec.config.inline.watchNamespace` property which may or may not be present or required depending on the bundle's +install mode support, if the bundle: + + - only supports *AllNamespaces* mode => `watchNamespace` is not a configuration + - supports *AllNamespaces* and *SingleNamespace* and/or *OwnNamespace* => `watchNamespace` is optional + - bundle only supports *SingleNamespace* and/or *OwnNamespace* => `watchNamespace` is required + +The `watchNamespace` configuration can only be the install namespace if the bundle supports the *OwnNamespace* install mode, and +it can only be any other namespace if the bundle supports the *SingleNamespace* install mode. + +Examples: + +Bundle only supports *AllNamespaces*: +- `watchNamespace` is not a configuration +- bundle will be installed in *AllNamespaces* mode + +Bundle only supports *OwnNamespace*: +- `watchNamespace` is required +- `watchNamespace` must be the install namespace +- bundle will always be installed in *OwnNamespace* mode + +Bundle supports *AllNamespace* and *OwnNamespace*: +- `watchNamespace` is optional +- if `watchNamespace` = install namespace => bundle will be installed in *OwnNamespace* mode +- if `watchNamespace` is null or not set => bundle will be installed in *AllNamespaces* mode +- if `watchNamespace` != install namespace => error + +Bundle only supports *SingleNamespace*: +- `watchNamespace` is required +- `watchNamespace` must *NOT* be the install namespace +- bundle will always be installed in *SingleNamespace* mode + +Bundle supports *AllNamespaces*, *SingleNamespace*, and *OwnNamespace* install modes: +- `watchNamespace` can be optionally configured +- if `watchNamespace` = install namespace => bundle will be installed in *OwnNamespace* mode +- if `watchNamespace` != install namespace => bundle will be installed in *SingleNamespace* mode +- if `watchNamespace` is null or not set => bundle will be installed in *AllNamespaces* mode ### Examples diff --git a/helm/olmv1/templates/deployment-olmv1-system-catalogd-controller-manager.yml b/helm/olmv1/templates/deployment-olmv1-system-catalogd-controller-manager.yml index 5beb73826..b3df12139 100644 --- a/helm/olmv1/templates/deployment-olmv1-system-catalogd-controller-manager.yml +++ b/helm/olmv1/templates/deployment-olmv1-system-catalogd-controller-manager.yml @@ -45,9 +45,6 @@ spec: {{- end }} - --metrics-bind-address=:7443 - --external-address=catalogd-service.{{ .Values.namespaces.olmv1.name }}.svc - {{- range .Values.catalogdFeatures }} - - --feature-gates={{- . -}}=true - {{- end }} {{- range .Values.options.catalogd.features.enabled }} - --feature-gates={{- . -}}=true {{- end }} diff --git a/helm/olmv1/templates/deployment-olmv1-system-operator-controller-controller-manager.yml b/helm/olmv1/templates/deployment-olmv1-system-operator-controller-controller-manager.yml index a3bdea06f..9ec405a3e 100644 --- a/helm/olmv1/templates/deployment-olmv1-system-operator-controller-controller-manager.yml +++ b/helm/olmv1/templates/deployment-olmv1-system-operator-controller-controller-manager.yml @@ -44,9 +44,6 @@ spec: {{- if not .Values.options.tilt.enabled }} - --leader-elect {{- end }} - {{- range .Values.operatorControllerFeatures }} - - --feature-gates={{- . -}}=true - {{- end }} {{- range .Values.options.operatorController.features.enabled }} - --feature-gates={{- . -}}=true {{- end }} diff --git a/helm/olmv1/templates/rbac/clusterrolebinding-operator-controller-manager-rolebinding.yml b/helm/olmv1/templates/rbac/clusterrolebinding-operator-controller-manager-rolebinding.yml index 5c1c0847d..9817337df 100644 --- a/helm/olmv1/templates/rbac/clusterrolebinding-operator-controller-manager-rolebinding.yml +++ b/helm/olmv1/templates/rbac/clusterrolebinding-operator-controller-manager-rolebinding.yml @@ -8,7 +8,7 @@ metadata: labels: app.kubernetes.io/name: operator-controller {{- include "olmv1.labels" $ | nindent 4 }} -{{- if or (has "BoxcutterRuntime" .Values.options.operatorController.features.enabled) (has "BoxcutterRuntime" .Values.operatorControllerFeatures) }} +{{- if has "BoxcutterRuntime" .Values.options.operatorController.features.enabled }} name: operator-controller-manager-admin-rolebinding {{- else }} name: operator-controller-manager-rolebinding @@ -16,7 +16,7 @@ metadata: roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole -{{- if or (has "BoxcutterRuntime" .Values.options.operatorController.features.enabled) (has "BoxcutterRuntime" .Values.operatorControllerFeatures) }} +{{- if has "BoxcutterRuntime" .Values.options.operatorController.features.enabled }} name: cluster-admin {{- else }} name: operator-controller-manager-role diff --git a/helm/olmv1/values.yaml b/helm/olmv1/values.yaml index 7b6a2cb7e..0704f43ef 100644 --- a/helm/olmv1/values.yaml +++ b/helm/olmv1/values.yaml @@ -33,10 +33,6 @@ options: # This can be one of: standard or experimental featureSet: standard -# Deprecated: The list of features -operatorControllerFeatures: [] -catalogdFeatures: [] - # The set of namespaces namespaces: olmv1: diff --git a/internal/operator-controller/applier/provider.go b/internal/operator-controller/applier/provider.go index f425e7415..ffb5eb559 100644 --- a/internal/operator-controller/applier/provider.go +++ b/internal/operator-controller/applier/provider.go @@ -68,8 +68,14 @@ func (r *RegistryV1ManifestProvider) Get(bundleFS fs.FS, ext *ocv1.ClusterExtens render.WithCertificateProvider(r.CertificateProvider), } - if r.IsSingleOwnNamespaceEnabled && ext.Spec.Config != nil && ext.Spec.Config.ConfigType == ocv1.ClusterExtensionConfigTypeInline { - bundleConfig, err := bundle.UnmarshallConfig(ext.Spec.Config.Inline.Raw, rv1, ext.Spec.Namespace) + if r.IsSingleOwnNamespaceEnabled { + bundleConfigBytes := extensionConfigBytes(ext) + // treat no config as empty to properly validate the configuration + // e.g. ensure that validation catches missing required fields + if bundleConfigBytes == nil { + bundleConfigBytes = []byte(`{}`) + } + bundleConfig, err := bundle.UnmarshalConfig(bundleConfigBytes, rv1, ext.Spec.Namespace) if err != nil { return nil, fmt.Errorf("invalid bundle configuration: %w", err) } @@ -128,3 +134,17 @@ func (r *RegistryV1HelmChartProvider) Get(bundleFS fs.FS, ext *ocv1.ClusterExten return chrt, nil } + +// ExtensionConfigBytes returns the ClusterExtension configuration input by the user +// through .spec.config as a byte slice. +func extensionConfigBytes(ext *ocv1.ClusterExtension) []byte { + if ext.Spec.Config != nil { + switch ext.Spec.Config.ConfigType { + case ocv1.ClusterExtensionConfigTypeInline: + if ext.Spec.Config.Inline != nil { + return ext.Spec.Config.Inline.Raw + } + } + } + return nil +} diff --git a/internal/operator-controller/applier/provider_test.go b/internal/operator-controller/applier/provider_test.go index b1a6cd4f4..4ec20bead 100644 --- a/internal/operator-controller/applier/provider_test.go +++ b/internal/operator-controller/applier/provider_test.go @@ -244,15 +244,6 @@ func Test_RegistryV1ManifestProvider_SingleOwnNamespaceSupport(t *testing.T) { t.Run("rejects bundles without AllNamespaces install mode and with SingleNamespace support when Single/OwnNamespace install mode support is enabled", func(t *testing.T) { expectedWatchNamespace := "some-namespace" provider := applier.RegistryV1ManifestProvider{ - BundleRenderer: render.BundleRenderer{ - ResourceGenerators: []render.ResourceGenerator{ - func(rv1 *bundle.RegistryV1, opts render.Options) ([]client.Object, error) { - t.Log("ensure watch namespace is appropriately configured") - require.Equal(t, []string{expectedWatchNamespace}, opts.TargetNamespaces) - return nil, nil - }, - }, - }, IsSingleOwnNamespaceEnabled: false, } @@ -289,7 +280,7 @@ func Test_RegistryV1ManifestProvider_SingleOwnNamespaceSupport(t *testing.T) { require.Contains(t, err.Error(), "unsupported bundle") }) - t.Run("accepts bundles without AllNamespaces install mode and with SingleNamespace support when Single/OwnNamespace install mode support is enabled", func(t *testing.T) { + t.Run("accepts bundles with install modes {SingleNamespace} when the appropriate configuration is given", func(t *testing.T) { expectedWatchNamespace := "some-namespace" provider := applier.RegistryV1ManifestProvider{ BundleRenderer: render.BundleRenderer{ @@ -321,20 +312,89 @@ func Test_RegistryV1ManifestProvider_SingleOwnNamespaceSupport(t *testing.T) { require.NoError(t, err) }) - t.Run("accepts bundles without AllNamespaces install mode and with OwnNamespace support when Single/OwnNamespace install mode support is enabled", func(t *testing.T) { + t.Run("rejects bundles with {SingleNamespace} install modes when no configuration is given", func(t *testing.T) { provider := applier.RegistryV1ManifestProvider{ IsSingleOwnNamespaceEnabled: true, } + bundleFS := bundlefs.Builder().WithPackageName("test"). - WithCSV(clusterserviceversion.Builder().WithInstallModeSupportFor(v1alpha1.InstallModeTypeOwnNamespace).Build()).Build() + WithCSV(clusterserviceversion.Builder().WithInstallModeSupportFor(v1alpha1.InstallModeTypeSingleNamespace).Build()).Build() + _, err := provider.Get(bundleFS, &ocv1.ClusterExtension{ Spec: ocv1.ClusterExtensionSpec{ Namespace: "install-namespace", }, }) + require.Error(t, err) + require.Contains(t, err.Error(), "required field \"watchNamespace\" is missing") + }) + + t.Run("accepts bundles with {OwnNamespace} install modes when the appropriate configuration is given", func(t *testing.T) { + installNamespace := "some-namespace" + provider := applier.RegistryV1ManifestProvider{ + BundleRenderer: render.BundleRenderer{ + ResourceGenerators: []render.ResourceGenerator{ + func(rv1 *bundle.RegistryV1, opts render.Options) ([]client.Object, error) { + t.Log("ensure watch namespace is appropriately configured") + require.Equal(t, []string{installNamespace}, opts.TargetNamespaces) + return nil, nil + }, + }, + }, + IsSingleOwnNamespaceEnabled: true, + } + bundleFS := bundlefs.Builder().WithPackageName("test"). + WithCSV(clusterserviceversion.Builder().WithInstallModeSupportFor(v1alpha1.InstallModeTypeOwnNamespace).Build()).Build() + _, err := provider.Get(bundleFS, &ocv1.ClusterExtension{ + Spec: ocv1.ClusterExtensionSpec{ + Namespace: installNamespace, + Config: &ocv1.ClusterExtensionConfig{ + ConfigType: ocv1.ClusterExtensionConfigTypeInline, + Inline: &apiextensionsv1.JSON{ + Raw: []byte(`{"watchNamespace": "` + installNamespace + `"}`), + }, + }, + }, + }) require.NoError(t, err) }) + t.Run("rejects bundles with {OwnNamespace} install modes when no configuration is given", func(t *testing.T) { + provider := applier.RegistryV1ManifestProvider{ + IsSingleOwnNamespaceEnabled: true, + } + bundleFS := bundlefs.Builder().WithPackageName("test"). + WithCSV(clusterserviceversion.Builder().WithInstallModeSupportFor(v1alpha1.InstallModeTypeOwnNamespace).Build()).Build() + _, err := provider.Get(bundleFS, &ocv1.ClusterExtension{ + Spec: ocv1.ClusterExtensionSpec{ + Namespace: "install-namespace", + }, + }) + require.Error(t, err) + require.Contains(t, err.Error(), "required field \"watchNamespace\" is missing") + }) + + t.Run("rejects bundles with {OwnNamespace} install modes when watchNamespace is not install namespace", func(t *testing.T) { + provider := applier.RegistryV1ManifestProvider{ + IsSingleOwnNamespaceEnabled: true, + } + bundleFS := bundlefs.Builder().WithPackageName("test"). + WithCSV(clusterserviceversion.Builder().WithInstallModeSupportFor(v1alpha1.InstallModeTypeOwnNamespace).Build()).Build() + _, err := provider.Get(bundleFS, &ocv1.ClusterExtension{ + Spec: ocv1.ClusterExtensionSpec{ + Namespace: "install-namespace", + Config: &ocv1.ClusterExtensionConfig{ + ConfigType: ocv1.ClusterExtensionConfigTypeInline, + Inline: &apiextensionsv1.JSON{ + Raw: []byte(`{"watchNamespace": "not-install-namespace"}`), + }, + }, + }, + }) + require.Error(t, err) + require.Contains(t, err.Error(), "invalid 'watchNamespace' \"not-install-namespace\": must be install namespace (install-namespace)") + }) + t.Run("rejects bundles without AllNamespaces, SingleNamespace, or OwnNamespace install mode support when Single/OwnNamespace install mode support is enabled", func(t *testing.T) { provider := applier.RegistryV1ManifestProvider{ IsSingleOwnNamespaceEnabled: true, diff --git a/internal/operator-controller/rukpak/bundle/config.go b/internal/operator-controller/rukpak/bundle/config.go index c65a2311a..7ec4bfdf0 100644 --- a/internal/operator-controller/rukpak/bundle/config.go +++ b/internal/operator-controller/rukpak/bundle/config.go @@ -17,20 +17,20 @@ type Config struct { WatchNamespace *string `json:"watchNamespace"` } -// UnmarshallConfig returns a deserialized and validated *bundle.Config based on bytes and validated +// UnmarshalConfig returns a deserialized *bundle.Config based on bytes and validated // against rv1 and the desired install namespaces. It will error if: // - rv is nil // - bytes is not a valid YAML/JSON object // - bytes is a valid YAML/JSON object but does not follow the registry+v1 schema -// if bytes is nil a nil bundle.Config is returned -func UnmarshallConfig(bytes []byte, rv1 RegistryV1, installNamespace string) (*Config, error) { +// - if bytes is nil, a nil *bundle.Config is returned with no error +func UnmarshalConfig(bytes []byte, rv1 RegistryV1, installNamespace string) (*Config, error) { if bytes == nil { return nil, nil } bundleConfig := &Config{} if err := yaml.UnmarshalStrict(bytes, bundleConfig); err != nil { - return nil, fmt.Errorf("error unmarshalling registry+v1 configuration: %w", formatUnmarshallError(err)) + return nil, fmt.Errorf("error unmarshalling registry+v1 configuration: %w", formatUnmarshalError(err)) } // collect bundle install modes @@ -83,24 +83,23 @@ func validateConfig(config *Config, installNamespace string, bundleInstallModeSe } // isWatchNamespaceConfigSupported returns true when the bundle exposes a watchNamespace configuration. This happens when: -// - SingleNamespace install more is supported, or -// - OwnNamespace and AllNamespaces install modes are supported +// - SingleNamespace and/or OwnNamespace install modes are supported func isWatchNamespaceConfigSupported(bundleInstallModeSet sets.Set[v1alpha1.InstallMode]) bool { - return bundleInstallModeSet.Has(v1alpha1.InstallMode{Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: true}) || - bundleInstallModeSet.HasAll( - v1alpha1.InstallMode{Type: v1alpha1.InstallModeTypeOwnNamespace, Supported: true}, - v1alpha1.InstallMode{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true}) + return bundleInstallModeSet.HasAny( + v1alpha1.InstallMode{Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: true}, + v1alpha1.InstallMode{Type: v1alpha1.InstallModeTypeOwnNamespace, Supported: true}, + ) } // isWatchNamespaceConfigRequired returns true if the watchNamespace configuration is required. This happens when -// AllNamespaces install mode is not supported and SingleNamespace is supported +// AllNamespaces install mode is not supported and SingleNamespace and/or OwnNamespace is supported func isWatchNamespaceConfigRequired(bundleInstallModeSet sets.Set[v1alpha1.InstallMode]) bool { - return !bundleInstallModeSet.Has(v1alpha1.InstallMode{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true}) && - bundleInstallModeSet.Has(v1alpha1.InstallMode{Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: true}) + return isWatchNamespaceConfigSupported(bundleInstallModeSet) && + !bundleInstallModeSet.Has(v1alpha1.InstallMode{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true}) } -// formatUnmarshallError format JSON unmarshal errors to be more readable -func formatUnmarshallError(err error) error { +// formatUnmarshalError format JSON unmarshal errors to be more readable +func formatUnmarshalError(err error) error { var unmarshalErr *json.UnmarshalTypeError if errors.As(err, &unmarshalErr) { if unmarshalErr.Field == "" { diff --git a/internal/operator-controller/rukpak/bundle/config_test.go b/internal/operator-controller/rukpak/bundle/config_test.go index 10494f651..a6c4b394a 100644 --- a/internal/operator-controller/rukpak/bundle/config_test.go +++ b/internal/operator-controller/rukpak/bundle/config_test.go @@ -12,7 +12,7 @@ import ( "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util/testing/clusterserviceversion" ) -func Test_UnmarshallConfig(t *testing.T) { +func Test_UnmarshalConfig(t *testing.T) { for _, tc := range []struct { name string rawConfig []byte @@ -22,7 +22,7 @@ func Test_UnmarshallConfig(t *testing.T) { expectedConfig *bundle.Config }{ { - name: "accepts nil raw config", + name: "returns nil for nil config", rawConfig: nil, expectedConfig: nil, }, @@ -91,16 +91,28 @@ func Test_UnmarshallConfig(t *testing.T) { expectedErrMessage: "unknown field \"watchNamespace\"", }, { - name: "reject with unknown field when install modes {OwnNamespace}", + name: "reject with required field when install modes {OwnNamespace} and watchNamespace is null", supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeOwnNamespace}, - rawConfig: []byte(`{"watchNamespace": "some-namespace"}`), - expectedErrMessage: "unknown field \"watchNamespace\"", + rawConfig: []byte(`{"watchNamespace": null}`), + expectedErrMessage: "required field \"watchNamespace\" is missing", + }, + { + name: "reject with required field when install modes {OwnNamespace} and watchNamespace is missing", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeOwnNamespace}, + rawConfig: []byte(`{}`), + expectedErrMessage: "required field \"watchNamespace\" is missing", }, { - name: "reject with unknown field when install modes {MultiNamespace, OwnNamespace}", + name: "reject with required field when install modes {MultiNamespace, OwnNamespace} and watchNamespace is null", supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeMultiNamespace, v1alpha1.InstallModeTypeOwnNamespace}, - rawConfig: []byte(`{"watchNamespace": "some-namespace"}`), - expectedErrMessage: "unknown field \"watchNamespace\"", + rawConfig: []byte(`{"watchNamespace": null}`), + expectedErrMessage: "required field \"watchNamespace\" is missing", + }, + { + name: "reject with required field when install modes {MultiNamespace, OwnNamespace} and watchNamespace is missing", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeMultiNamespace, v1alpha1.InstallModeTypeOwnNamespace}, + rawConfig: []byte(`{}`), + expectedErrMessage: "required field \"watchNamespace\" is missing", }, { name: "accepts when install modes {SingleNamespace} and watchNamespace != install namespace", @@ -202,6 +214,27 @@ func Test_UnmarshallConfig(t *testing.T) { installNamespace: "not-some-namespace", expectedErrMessage: "required field \"watchNamespace\" is missing", }, + { + name: "rejects with required field error when install modes {SingleNamespace} and watchNamespace is missing", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeSingleNamespace}, + rawConfig: []byte(`{}`), + installNamespace: "not-some-namespace", + expectedErrMessage: "required field \"watchNamespace\" is missing", + }, + { + name: "rejects with required field error when install modes {SingleNamespace, OwnNamespace} and watchNamespace is missing", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeSingleNamespace, v1alpha1.InstallModeTypeOwnNamespace}, + rawConfig: []byte(`{}`), + installNamespace: "not-some-namespace", + expectedErrMessage: "required field \"watchNamespace\" is missing", + }, + { + name: "rejects with required field error when install modes {SingleNamespace, MultiNamespace} and watchNamespace is missing", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeSingleNamespace, v1alpha1.InstallModeTypeMultiNamespace}, + rawConfig: []byte(`{}`), + installNamespace: "not-some-namespace", + expectedErrMessage: "required field \"watchNamespace\" is missing", + }, { name: "rejects with required field error when install modes {SingleNamespace, OwnNamespace, MultiNamespace} and watchNamespace is nil", supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeSingleNamespace, v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeMultiNamespace}, @@ -227,6 +260,24 @@ func Test_UnmarshallConfig(t *testing.T) { WatchNamespace: nil, }, }, + { + name: "accepts no watchNamespace when install modes {AllNamespaces, OwnNamespace} and watchNamespace is nil", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeOwnNamespace}, + rawConfig: []byte(`{}`), + installNamespace: "not-some-namespace", + expectedConfig: &bundle.Config{ + WatchNamespace: nil, + }, + }, + { + name: "accepts no watchNamespace when install modes {AllNamespaces, OwnNamespace, MultiNamespace} and watchNamespace is nil", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeMultiNamespace}, + rawConfig: []byte(`{}`), + installNamespace: "not-some-namespace", + expectedConfig: &bundle.Config{ + WatchNamespace: nil, + }, + }, { name: "rejects with format error when install modes are {SingleNamespace, OwnNamespace} and watchNamespace is ''", supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeSingleNamespace, v1alpha1.InstallModeTypeOwnNamespace}, @@ -246,7 +297,7 @@ func Test_UnmarshallConfig(t *testing.T) { } } - config, err := bundle.UnmarshallConfig(tc.rawConfig, rv1, tc.installNamespace) + config, err := bundle.UnmarshalConfig(tc.rawConfig, rv1, tc.installNamespace) require.Equal(t, tc.expectedConfig, config) if tc.expectedErrMessage != "" { require.Error(t, err) diff --git a/internal/operator-controller/rukpak/render/render.go b/internal/operator-controller/rukpak/render/render.go index a279b5c1e..f7e419c78 100644 --- a/internal/operator-controller/rukpak/render/render.go +++ b/internal/operator-controller/rukpak/render/render.go @@ -124,7 +124,7 @@ func (r BundleRenderer) Render(rv1 bundle.RegistryV1, installNamespace string, o genOpts, errs := (&Options{ // default options InstallNamespace: installNamespace, - TargetNamespaces: defaultTargetNamespacesForBundle(&rv1, installNamespace), + TargetNamespaces: defaultTargetNamespacesForBundle(&rv1), UniqueNameGenerator: DefaultUniqueNameGenerator, CertificateProvider: nil, }).apply(opts...).validate(&rv1) @@ -160,10 +160,10 @@ func validateTargetNamespaces(rv1 *bundle.RegistryV1, installNamespace string, t // in case only the MultiNamespace install mode is supported by the bundle. // If AllNamespaces mode is supported, the default will be [""] -> watch all namespaces // If only OwnNamespace is supported, the default will be [install-namespace] -> only watch the install/own namespace - if supportedInstallModes.Len() == 1 && supportedInstallModes.Has(v1alpha1.InstallModeTypeSingleNamespace) { - return errors.New("exactly one target namespace must be specified") + if supportedInstallModes.Has(v1alpha1.InstallModeTypeMultiNamespace) { + return errors.New("at least one target namespace must be specified") } - return errors.New("at least one target namespace must be specified") + return errors.New("exactly one target namespace must be specified") case set.Len() == 1 && set.Has(""): if supportedInstallModes.Has(v1alpha1.InstallModeTypeAllNamespaces) { return nil @@ -190,17 +190,13 @@ func validateTargetNamespaces(rv1 *bundle.RegistryV1, installNamespace string, t return fmt.Errorf("supported install modes %v do not support target namespaces %v", sets.List[v1alpha1.InstallModeType](supportedInstallModes), targetNamespaces) } -func defaultTargetNamespacesForBundle(rv1 *bundle.RegistryV1, installNamespace string) []string { +func defaultTargetNamespacesForBundle(rv1 *bundle.RegistryV1) []string { supportedInstallModes := supportedBundleInstallModes(rv1) if supportedInstallModes.Has(v1alpha1.InstallModeTypeAllNamespaces) { return []string{corev1.NamespaceAll} } - if supportedInstallModes.Has(v1alpha1.InstallModeTypeOwnNamespace) { - return []string{installNamespace} - } - return nil } diff --git a/internal/operator-controller/rukpak/render/render_test.go b/internal/operator-controller/rukpak/render/render_test.go index 9483bd8cb..ca1459889 100644 --- a/internal/operator-controller/rukpak/render/render_test.go +++ b/internal/operator-controller/rukpak/render/render_test.go @@ -62,6 +62,113 @@ func Test_BundleRenderer_CreatesCorrectDefaultOptions(t *testing.T) { _, _ = renderer.Render(bundle.RegistryV1{}, expectedInstallNamespace) } +func Test_BundleRenderer_DefaultTargetNamespaces(t *testing.T) { + for _, tc := range []struct { + name string + supportedInstallModes []v1alpha1.InstallModeType + expectedTargetNamespaces []string + expectedErrMsg string + }{ + { + name: "Default to AllNamespaces when bundle install modes are {AllNamespaces}", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeAllNamespaces}, + expectedTargetNamespaces: []string{corev1.NamespaceAll}, + }, + { + name: "Default to AllNamespaces when bundle install modes are {AllNamespaces, OwnNamespace}", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeOwnNamespace}, + expectedTargetNamespaces: []string{corev1.NamespaceAll}, + }, + { + name: "Default to AllNamespaces when bundle install modes are {AllNamespaces, SingleNamespace}", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeSingleNamespace}, + expectedTargetNamespaces: []string{corev1.NamespaceAll}, + }, + { + name: "Default to AllNamespaces when bundle install modes are {AllNamespaces, MultiNamespace}", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeMultiNamespace}, + expectedTargetNamespaces: []string{corev1.NamespaceAll}, + }, + { + name: "Default to AllNamespaces when bundle install modes are {AllNamespaces, OwnNamespace, SingleNamespace}", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeSingleNamespace}, + expectedTargetNamespaces: []string{corev1.NamespaceAll}, + }, + { + name: "Default to AllNamespaces when bundle install modes are {AllNamespaces, OwnNamespace, MultiNamespace}", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeMultiNamespace}, + expectedTargetNamespaces: []string{corev1.NamespaceAll}, + }, + { + name: "Default to AllNamespaces when bundle install modes are {AllNamespaces, SingleNamespace, MultiNamespace}", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeSingleNamespace, v1alpha1.InstallModeTypeMultiNamespace}, + expectedTargetNamespaces: []string{corev1.NamespaceAll}, + }, + { + name: "Default to AllNamespaces when bundle install modes are {AllNamespaces, SingleNamespace, OwnNamespace, MultiNamespace}", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeSingleNamespace, v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeMultiNamespace}, + expectedTargetNamespaces: []string{corev1.NamespaceAll}, + }, + { + name: "No default when bundle install modes are {SingleNamespace}", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeSingleNamespace}, + expectedErrMsg: "exactly one target namespace must be specified", + }, + { + name: "No default when bundle install modes are {OwnNamespace}", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeOwnNamespace}, + expectedErrMsg: "exactly one target namespace must be specified", + }, + { + name: "No default when bundle install modes are {MultiNamespace}", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeMultiNamespace}, + expectedErrMsg: "at least one target namespace must be specified", + }, + { + name: "No default when bundle install modes are {SingleNamespace, OwnNamespace}", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeSingleNamespace, v1alpha1.InstallModeTypeOwnNamespace}, + expectedErrMsg: "exactly one target namespace must be specified", + }, + { + name: "No default when bundle install modes are {SingleNamespace, MultiNamespace}", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeSingleNamespace, v1alpha1.InstallModeTypeMultiNamespace}, + expectedErrMsg: "at least one target namespace must be specified", + }, + { + name: "No default when bundle install modes are {OwnNamespace, MultiNamespace}", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeMultiNamespace}, + expectedErrMsg: "at least one target namespace must be specified", + }, + { + name: "No default when bundle install modes are {SingleNamespace, OwnNamespace, MultiNamespace}", + supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeSingleNamespace, v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeMultiNamespace}, + expectedErrMsg: "at least one target namespace must be specified", + }, + } { + t.Run(tc.name, func(t *testing.T) { + renderer := render.BundleRenderer{ + ResourceGenerators: []render.ResourceGenerator{ + func(rv1 *bundle.RegistryV1, opts render.Options) ([]client.Object, error) { + require.Equal(t, tc.expectedTargetNamespaces, opts.TargetNamespaces) + return nil, nil + }, + }, + } + _, err := renderer.Render(bundle.RegistryV1{ + CSV: clusterserviceversion.Builder(). + WithName("test"). + WithInstallModeSupportFor(tc.supportedInstallModes...).Build(), + }, "some-namespace") + if tc.expectedErrMsg != "" { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expectedErrMsg) + } else { + require.NoError(t, err) + } + }) + } +} + func Test_BundleRenderer_ValidatesRenderOptions(t *testing.T) { for _, tc := range []struct { name string diff --git a/test/experimental-e2e/single_namespace_support_test.go b/test/experimental-e2e/single_namespace_support_test.go index d1ca13465..77a0cba42 100644 --- a/test/experimental-e2e/single_namespace_support_test.go +++ b/test/experimental-e2e/single_namespace_support_test.go @@ -55,14 +55,14 @@ func TestNoop(t *testing.T) { defer utils.CollectTestArtifacts(t, artifactName, c, cfg) } -func TestClusterExtensionConfigSupport(t *testing.T) { +func TestClusterExtensionSingleNamespaceSupport(t *testing.T) { t.Log("Test support for cluster extension config") defer utils.CollectTestArtifacts(t, artifactName, c, cfg) t.Log("By creating install namespace, watch namespace and necessary rbac resources") namespace := corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-operator", + Name: "single-namespace-operator", }, } require.NoError(t, c.Create(t.Context(), &namespace)) @@ -72,7 +72,7 @@ func TestClusterExtensionConfigSupport(t *testing.T) { watchNamespace := corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-operator-watch", + Name: "single-namespace-operator-target", }, } require.NoError(t, c.Create(t.Context(), &watchNamespace)) @@ -82,7 +82,7 @@ func TestClusterExtensionConfigSupport(t *testing.T) { serviceAccount := corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-operator-installer", + Name: "single-namespace-operator-installer", Namespace: namespace.GetName(), }, } @@ -93,7 +93,7 @@ func TestClusterExtensionConfigSupport(t *testing.T) { clusterRoleBinding := &rbacv1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-operator-installer", + Name: "single-namespace-operator-installer", }, Subjects: []rbacv1.Subject{ { @@ -114,10 +114,10 @@ func TestClusterExtensionConfigSupport(t *testing.T) { require.NoError(t, c.Delete(context.Background(), clusterRoleBinding)) }) - t.Log("By creating the test-operator ClusterCatalog") + t.Log("By creating the test-catalog ClusterCatalog") extensionCatalog := &ocv1.ClusterCatalog{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-operator-catalog", + Name: "test-catalog", }, Spec: ocv1.ClusterCatalogSpec{ Source: ocv1.CatalogSource{ @@ -143,16 +143,16 @@ func TestClusterExtensionConfigSupport(t *testing.T) { require.Equal(ct, ocv1.ReasonAvailable, cond.Reason) }, pollDuration, pollInterval) - t.Log("By installing the test-operator ClusterExtension configured in SingleNamespace mode") + t.Log("By attempting to install the single-namespace-operator ClusterExtension without any configuration") clusterExtension := &ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-operator-extension", + Name: "single-namespace-operator-extension", }, Spec: ocv1.ClusterExtensionSpec{ Source: ocv1.SourceConfig{ SourceType: "Catalog", Catalog: &ocv1.CatalogFilter{ - PackageName: "test", + PackageName: "single-namespace-operator", Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, }, @@ -162,12 +162,153 @@ func TestClusterExtensionConfigSupport(t *testing.T) { ServiceAccount: ocv1.ServiceAccountReference{ Name: serviceAccount.GetName(), }, - Config: &ocv1.ClusterExtensionConfig{ - ConfigType: ocv1.ClusterExtensionConfigTypeInline, - Inline: &apiextensionsv1.JSON{ - Raw: []byte(fmt.Sprintf(`{"watchNamespace": "%s"}`, watchNamespace.GetName())), + }, + } + require.NoError(t, c.Create(t.Context(), clusterExtension)) + t.Cleanup(func() { + require.NoError(t, c.Delete(context.Background(), clusterExtension)) + }) + + t.Log("By waiting for single-namespace-operator extension installation to fail") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + require.NoError(ct, c.Get(t.Context(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) + require.NotNil(ct, cond) + require.Equal(ct, metav1.ConditionTrue, cond.Status) + require.Equal(ct, ocv1.ReasonRetrying, cond.Reason) + require.Contains(ct, cond.Message, "required field \"watchNamespace\" is missing") + }, pollDuration, pollInterval) + + t.Log("By updating the ClusterExtension configuration with a watchNamespace") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + require.NoError(t, c.Get(t.Context(), types.NamespacedName{Name: clusterExtension.GetName()}, clusterExtension)) + clusterExtension.Spec.Config = &ocv1.ClusterExtensionConfig{ + ConfigType: ocv1.ClusterExtensionConfigTypeInline, + Inline: &apiextensionsv1.JSON{ + Raw: []byte(fmt.Sprintf(`{"watchNamespace": "%s"}`, watchNamespace.GetName())), + }, + } + require.NoError(t, c.Update(t.Context(), clusterExtension)) + }, pollDuration, pollInterval) + + t.Log("By waiting for single-namespace-operator extension to be installed successfully") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + require.NoError(ct, c.Get(t.Context(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled) + require.NotNil(ct, cond) + require.Equal(ct, metav1.ConditionTrue, cond.Status) + require.Equal(ct, ocv1.ReasonSucceeded, cond.Reason) + require.Contains(ct, cond.Message, "Installed bundle") + require.NotNil(ct, clusterExtension.Status.Install) + require.NotEmpty(ct, clusterExtension.Status.Install.Bundle) + }, pollDuration, pollInterval) + + t.Log("By ensuring the single-namespace-operator deployment is correctly configured to watch the watch namespace") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + deployment := &appsv1.Deployment{} + require.NoError(ct, c.Get(t.Context(), types.NamespacedName{Namespace: namespace.GetName(), Name: "single-namespace-operator"}, deployment)) + require.NotNil(ct, deployment.Spec.Template.GetAnnotations()) + require.Equal(ct, watchNamespace.GetName(), deployment.Spec.Template.GetAnnotations()["olm.targetNamespaces"]) + }, pollDuration, pollInterval) +} + +func TestClusterExtensionOwnNamespaceSupport(t *testing.T) { + t.Log("Test support for cluster extension with OwnNamespace install mode support") + defer utils.CollectTestArtifacts(t, artifactName, c, cfg) + + t.Log("By creating install namespace, watch namespace and necessary rbac resources") + namespace := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "own-namespace-operator", + }, + } + require.NoError(t, c.Create(t.Context(), &namespace)) + t.Cleanup(func() { + require.NoError(t, c.Delete(context.Background(), &namespace)) + }) + + serviceAccount := corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "own-namespace-operator-installer", + Namespace: namespace.GetName(), + }, + } + require.NoError(t, c.Create(t.Context(), &serviceAccount)) + t.Cleanup(func() { + require.NoError(t, c.Delete(context.Background(), &serviceAccount)) + }) + + clusterRoleBinding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "own-namespace-operator-installer", + }, + Subjects: []rbacv1.Subject{ + { + Kind: "ServiceAccount", + APIGroup: corev1.GroupName, + Name: serviceAccount.GetName(), + Namespace: serviceAccount.GetNamespace(), + }, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "ClusterRole", + Name: "cluster-admin", + }, + } + require.NoError(t, c.Create(t.Context(), clusterRoleBinding)) + t.Cleanup(func() { + require.NoError(t, c.Delete(context.Background(), clusterRoleBinding)) + }) + + t.Log("By creating the test-catalog ClusterCatalog") + extensionCatalog := &ocv1.ClusterCatalog{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-catalog", + }, + Spec: ocv1.ClusterCatalogSpec{ + Source: ocv1.CatalogSource{ + Type: ocv1.SourceTypeImage, + Image: &ocv1.ImageSource{ + Ref: fmt.Sprintf("%s/e2e/test-catalog:v1", os.Getenv("CLUSTER_REGISTRY_HOST")), + PollIntervalMinutes: ptr.To(1), + }, + }, + }, + } + require.NoError(t, c.Create(t.Context(), extensionCatalog)) + t.Cleanup(func() { + require.NoError(t, c.Delete(context.Background(), extensionCatalog)) + }) + + t.Log("By waiting for the catalog to serve its metadata") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: extensionCatalog.GetName()}, extensionCatalog)) + cond := apimeta.FindStatusCondition(extensionCatalog.Status.Conditions, ocv1.TypeServing) + require.NotNil(ct, cond) + require.Equal(ct, metav1.ConditionTrue, cond.Status) + require.Equal(ct, ocv1.ReasonAvailable, cond.Reason) + }, pollDuration, pollInterval) + + t.Log("By attempting to install the own-namespace-operator ClusterExtension without any configuration") + clusterExtension := &ocv1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{ + Name: "own-namespace-operator-extension", + }, + Spec: ocv1.ClusterExtensionSpec{ + Source: ocv1.SourceConfig{ + SourceType: "Catalog", + Catalog: &ocv1.CatalogFilter{ + PackageName: "own-namespace-operator", + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name}, + }, }, }, + Namespace: namespace.GetName(), + ServiceAccount: ocv1.ServiceAccountReference{ + Name: serviceAccount.GetName(), + }, }, } require.NoError(t, c.Create(t.Context(), clusterExtension)) @@ -175,7 +316,51 @@ func TestClusterExtensionConfigSupport(t *testing.T) { require.NoError(t, c.Delete(context.Background(), clusterExtension)) }) - t.Log("By waiting for test-operator extension to be installed successfully") + t.Log("By waiting for own-namespace-operator extension installation to fail") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + require.NoError(ct, c.Get(t.Context(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) + require.NotNil(ct, cond) + require.Equal(ct, metav1.ConditionTrue, cond.Status) + require.Equal(ct, ocv1.ReasonRetrying, cond.Reason) + require.Contains(ct, cond.Message, "required field \"watchNamespace\" is missing") + }, pollDuration, pollInterval) + + t.Log("By updating the ClusterExtension configuration with a watchNamespace other than the install namespace") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + require.NoError(t, c.Get(t.Context(), types.NamespacedName{Name: clusterExtension.GetName()}, clusterExtension)) + clusterExtension.Spec.Config = &ocv1.ClusterExtensionConfig{ + ConfigType: ocv1.ClusterExtensionConfigTypeInline, + Inline: &apiextensionsv1.JSON{ + Raw: []byte(`{"watchNamespace": "some-namespace"}`), + }, + } + require.NoError(t, c.Update(t.Context(), clusterExtension)) + }, pollDuration, pollInterval) + + t.Log("By waiting for own-namespace-operator extension installation to fail") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + require.NoError(ct, c.Get(t.Context(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) + cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing) + require.NotNil(ct, cond) + require.Equal(ct, metav1.ConditionTrue, cond.Status) + require.Equal(ct, ocv1.ReasonRetrying, cond.Reason) + require.Contains(ct, cond.Message, fmt.Sprintf("invalid 'watchNamespace' \"some-namespace\": must be install namespace (%s)", clusterExtension.Spec.Namespace)) + }, pollDuration, pollInterval) + + t.Log("By updating the ClusterExtension configuration with a watchNamespace = install namespace") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + require.NoError(t, c.Get(t.Context(), types.NamespacedName{Name: clusterExtension.GetName()}, clusterExtension)) + clusterExtension.Spec.Config = &ocv1.ClusterExtensionConfig{ + ConfigType: ocv1.ClusterExtensionConfigTypeInline, + Inline: &apiextensionsv1.JSON{ + Raw: []byte(fmt.Sprintf(`{"watchNamespace": "%s"}`, clusterExtension.Spec.Namespace)), + }, + } + require.NoError(t, c.Update(t.Context(), clusterExtension)) + }, pollDuration, pollInterval) + + t.Log("By waiting for own-namespace-operator extension to be installed successfully") require.EventuallyWithT(t, func(ct *assert.CollectT) { require.NoError(ct, c.Get(t.Context(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension)) cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled) @@ -187,12 +372,12 @@ func TestClusterExtensionConfigSupport(t *testing.T) { require.NotEmpty(ct, clusterExtension.Status.Install.Bundle) }, pollDuration, pollInterval) - t.Log("By ensuring the test-operator deployment is correctly configured to watch the watch namespace") + t.Log("By ensuring the own-namespace-operator deployment is correctly configured to watch the watch namespace") require.EventuallyWithT(t, func(ct *assert.CollectT) { deployment := &appsv1.Deployment{} - require.NoError(ct, c.Get(t.Context(), types.NamespacedName{Namespace: namespace.GetName(), Name: "test-operator"}, deployment)) + require.NoError(ct, c.Get(t.Context(), types.NamespacedName{Namespace: namespace.GetName(), Name: "own-namespace-operator"}, deployment)) require.NotNil(ct, deployment.Spec.Template.GetAnnotations()) - require.Equal(ct, watchNamespace.GetName(), deployment.Spec.Template.GetAnnotations()["olm.targetNamespaces"]) + require.Equal(ct, clusterExtension.Spec.Namespace, deployment.Spec.Template.GetAnnotations()["olm.targetNamespaces"]) }, pollDuration, pollInterval) } diff --git a/testdata/images/bundles/own-namespace-operator/v1.0.0/manifests/olm.operatorframework.com_ownnamespaces.yaml b/testdata/images/bundles/own-namespace-operator/v1.0.0/manifests/olm.operatorframework.com_ownnamespaces.yaml new file mode 100644 index 000000000..305e3c73e --- /dev/null +++ b/testdata/images/bundles/own-namespace-operator/v1.0.0/manifests/olm.operatorframework.com_ownnamespaces.yaml @@ -0,0 +1,27 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: ownnamespaces.olm.operatorframework.io +spec: + group: olm.operatorframework.io + names: + kind: OwnNamespace + listKind: OwnNamespaceList + plural: ownnamespaces + singular: ownnamespace + scope: Cluster + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + testField: + type: string diff --git a/testdata/images/bundles/own-namespace-operator/v1.0.0/manifests/ownnamespaceoperator.clusterserviceversion.yaml b/testdata/images/bundles/own-namespace-operator/v1.0.0/manifests/ownnamespaceoperator.clusterserviceversion.yaml new file mode 100644 index 000000000..d6c2a3255 --- /dev/null +++ b/testdata/images/bundles/own-namespace-operator/v1.0.0/manifests/ownnamespaceoperator.clusterserviceversion.yaml @@ -0,0 +1,151 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + alm-examples: |- + [ + { + "apiVersion": "ownnamespaces.olm.operatorframework.io/v1", + "kind": "OwnNamespace", + "metadata": { + "labels": { + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "test" + }, + "name": "test-sample" + }, + "spec": null + } + ] + capabilities: Basic Install + createdAt: "2024-10-24T19:21:40Z" + operators.operatorframework.io/builder: operator-sdk-v1.34.1 + operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 + name: ownnamespaceoperator.v1.0.0 + namespace: placeholder +spec: + apiservicedefinitions: {} + customresourcedefinitions: + owned: + - description: A dummy resource for an operator that only supports own namespace install mode + displayName: OwnNamespace + kind: OwnNamespace + name: ownnamespaces.olm.operatorframework.io + version: v1 + description: OLM OwnNamespace Testing Operator + displayName: test-operator + icon: + - base64data: "" + mediatype: "" + install: + spec: + deployments: + - label: + app.kubernetes.io/component: controller + app.kubernetes.io/name: own-namespace-operator + app.kubernetes.io/version: 1.0.0 + name: own-namespace-operator + spec: + replicas: 1 + selector: + matchLabels: + app: ownnamespacetest + template: + metadata: + labels: + app: ownnamespacetest + spec: + terminationGracePeriodSeconds: 0 + containers: + - name: busybox + image: busybox:1.36 + command: + - 'sleep' + - '1000' + securityContext: + runAsUser: 1000 + runAsNonRoot: true + serviceAccountName: simple-bundle-manager + clusterPermissions: + - rules: + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create + serviceAccountName: simple-bundle-manager + permissions: + - rules: + - apiGroups: + - "" + resources: + - configmaps + - serviceaccounts + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - networking.k8s.io + resources: + - networkpolicies + verbs: + - get + - list + - create + - update + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + serviceAccountName: simple-bundle-manager + strategy: deployment + installModes: + - supported: true + type: OwnNamespace + - supported: false + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: false + type: AllNamespaces + keywords: + - registry + links: + - name: simple-bundle + url: https://simple-bundle.domain + maintainers: + - email: main#simple-bundle.domain + name: Simple Bundle + maturity: beta + provider: + name: Simple Bundle + url: https://simple-bundle.domain + version: 1.0.0 diff --git a/testdata/images/bundles/own-namespace-operator/v1.0.0/metadata/annotations.yaml b/testdata/images/bundles/own-namespace-operator/v1.0.0/metadata/annotations.yaml new file mode 100644 index 000000000..24cf5213a --- /dev/null +++ b/testdata/images/bundles/own-namespace-operator/v1.0.0/metadata/annotations.yaml @@ -0,0 +1,10 @@ +annotations: + # Core bundle annotations. + operators.operatorframework.io.bundle.mediatype.v1: registry+v1 + operators.operatorframework.io.bundle.manifests.v1: manifests/ + operators.operatorframework.io.bundle.metadata.v1: metadata/ + operators.operatorframework.io.bundle.package.v1: own-namespace-operator + operators.operatorframework.io.bundle.channels.v1: alpha + operators.operatorframework.io.metrics.builder: operator-sdk-v1.28.0 + operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 + operators.operatorframework.io.metrics.project_layout: unknown diff --git a/testdata/images/bundles/single-namespace-operator/v1.0.0/manifests/olm.operatorframework.com_singlenamespaces.yaml b/testdata/images/bundles/single-namespace-operator/v1.0.0/manifests/olm.operatorframework.com_singlenamespaces.yaml new file mode 100644 index 000000000..5f8d30543 --- /dev/null +++ b/testdata/images/bundles/single-namespace-operator/v1.0.0/manifests/olm.operatorframework.com_singlenamespaces.yaml @@ -0,0 +1,27 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: singlenamespaces.olm.operatorframework.io +spec: + group: olm.operatorframework.io + names: + kind: SingleNamespace + listKind: SingleNamespaceList + plural: singlenamespaces + singular: singlenamespace + scope: Cluster + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + testField: + type: string diff --git a/testdata/images/bundles/single-namespace-operator/v1.0.0/manifests/singlenamespaceoperator.clusterserviceversion.yaml b/testdata/images/bundles/single-namespace-operator/v1.0.0/manifests/singlenamespaceoperator.clusterserviceversion.yaml new file mode 100644 index 000000000..dea4c7f09 --- /dev/null +++ b/testdata/images/bundles/single-namespace-operator/v1.0.0/manifests/singlenamespaceoperator.clusterserviceversion.yaml @@ -0,0 +1,151 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + alm-examples: |- + [ + { + "apiVersion": "singlenamespaces.olm.operatorframework.io/v1", + "kind": "SingleNamespace", + "metadata": { + "labels": { + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "test" + }, + "name": "test-sample" + }, + "spec": null + } + ] + capabilities: Basic Install + createdAt: "2024-10-24T19:21:40Z" + operators.operatorframework.io/builder: operator-sdk-v1.34.1 + operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 + name: singlenamespaceoperator.v1.0.0 + namespace: placeholder +spec: + apiservicedefinitions: {} + customresourcedefinitions: + owned: + - description: A dummy resource for an operator that only supports single namespace install mode + displayName: SingleNamespace + kind: SingleNamespace + name: singlenamespaces.olm.operatorframework.io + version: v1 + description: OLM SingleNamespace Testing Operator + displayName: test-operator + icon: + - base64data: "" + mediatype: "" + install: + spec: + deployments: + - label: + app.kubernetes.io/component: controller + app.kubernetes.io/name: single-namespace-operator + app.kubernetes.io/version: 1.0.0 + name: single-namespace-operator + spec: + replicas: 1 + selector: + matchLabels: + app: singlenamespacetest + template: + metadata: + labels: + app: singlenamespacetest + spec: + terminationGracePeriodSeconds: 0 + containers: + - name: busybox + image: busybox:1.36 + command: + - 'sleep' + - '1000' + securityContext: + runAsUser: 1000 + runAsNonRoot: true + serviceAccountName: simple-bundle-manager + clusterPermissions: + - rules: + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create + serviceAccountName: simple-bundle-manager + permissions: + - rules: + - apiGroups: + - "" + resources: + - configmaps + - serviceaccounts + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - networking.k8s.io + resources: + - networkpolicies + verbs: + - get + - list + - create + - update + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + serviceAccountName: simple-bundle-manager + strategy: deployment + installModes: + - supported: false + type: OwnNamespace + - supported: true + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: false + type: AllNamespaces + keywords: + - registry + links: + - name: simple-bundle + url: https://simple-bundle.domain + maintainers: + - email: main#simple-bundle.domain + name: Simple Bundle + maturity: beta + provider: + name: Simple Bundle + url: https://simple-bundle.domain + version: 1.0.0 diff --git a/testdata/images/bundles/single-namespace-operator/v1.0.0/metadata/annotations.yaml b/testdata/images/bundles/single-namespace-operator/v1.0.0/metadata/annotations.yaml new file mode 100644 index 000000000..b7d60e6df --- /dev/null +++ b/testdata/images/bundles/single-namespace-operator/v1.0.0/metadata/annotations.yaml @@ -0,0 +1,10 @@ +annotations: + # Core bundle annotations. + operators.operatorframework.io.bundle.mediatype.v1: registry+v1 + operators.operatorframework.io.bundle.manifests.v1: manifests/ + operators.operatorframework.io.bundle.metadata.v1: metadata/ + operators.operatorframework.io.bundle.package.v1: single-namespace-operator + operators.operatorframework.io.bundle.channels.v1: alpha + operators.operatorframework.io.metrics.builder: operator-sdk-v1.28.0 + operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 + operators.operatorframework.io.metrics.project_layout: unknown diff --git a/testdata/images/bundles/test-operator/v1.0.0/manifests/bundle.configmap.yaml b/testdata/images/bundles/test-operator/v1.0.0/manifests/bundle.configmap.yaml index 567b7588d..74a3623ee 100644 --- a/testdata/images/bundles/test-operator/v1.0.0/manifests/bundle.configmap.yaml +++ b/testdata/images/bundles/test-operator/v1.0.0/manifests/bundle.configmap.yaml @@ -4,9 +4,8 @@ metadata: name: test-configmap annotations: shouldNotTemplate: > - The namespace is {{ $labels.namespace }}. The templated - $labels.namespace is NOT expected to be processed by OLM's - rendering engine for registry+v1 bundles. + The namespace is {{ $labels.namespace }}. The templated $labels.namespace is NOT expected to be processed by OLM's rendering engine for registry+v1 bundles. + data: version: "v1.0.0" name: "test-configmap" diff --git a/testdata/images/bundles/test-operator/v1.0.0/manifests/olm.operatorframework.com_olme2etest.yaml b/testdata/images/bundles/test-operator/v1.0.0/manifests/olm.operatorframework.com_olme2etest.yaml index fcfd4aeaf..44e64cef7 100644 --- a/testdata/images/bundles/test-operator/v1.0.0/manifests/olm.operatorframework.com_olme2etest.yaml +++ b/testdata/images/bundles/test-operator/v1.0.0/manifests/olm.operatorframework.com_olme2etest.yaml @@ -1,4 +1,3 @@ ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/testdata/images/bundles/test-operator/v1.0.0/manifests/testoperator.clusterserviceversion.yaml b/testdata/images/bundles/test-operator/v1.0.0/manifests/testoperator.clusterserviceversion.yaml index 54924492b..4cb49d5fe 100644 --- a/testdata/images/bundles/test-operator/v1.0.0/manifests/testoperator.clusterserviceversion.yaml +++ b/testdata/images/bundles/test-operator/v1.0.0/manifests/testoperator.clusterserviceversion.yaml @@ -35,115 +35,115 @@ spec: description: OLM E2E Testing Operator displayName: test-operator icon: - - base64data: "" - mediatype: "" + - base64data: "" + mediatype: "" install: spec: deployments: - - label: - app.kubernetes.io/component: controller - app.kubernetes.io/name: test-operator - app.kubernetes.io/version: 1.0.0 - name: test-operator - spec: - replicas: 1 - selector: - matchLabels: - app: olme2etest - template: - metadata: - labels: + - label: + app.kubernetes.io/component: controller + app.kubernetes.io/name: test-operator + app.kubernetes.io/version: 1.0.0 + name: test-operator + spec: + replicas: 1 + selector: + matchLabels: app: olme2etest - spec: - terminationGracePeriodSeconds: 0 - containers: - - name: busybox - image: busybox:1.36 - command: - - 'sleep' - - '1000' - securityContext: - runAsUser: 1000 - runAsNonRoot: true - serviceAccountName: simple-bundle-manager + template: + metadata: + labels: + app: olme2etest + spec: + terminationGracePeriodSeconds: 0 + containers: + - name: busybox + image: busybox:1.36 + command: + - 'sleep' + - '1000' + securityContext: + runAsUser: 1000 + runAsNonRoot: true + serviceAccountName: simple-bundle-manager clusterPermissions: - - rules: - - apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create - - apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create - serviceAccountName: simple-bundle-manager + - rules: + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create + serviceAccountName: simple-bundle-manager permissions: - - rules: - - apiGroups: - - "" - resources: - - configmaps - - serviceaccounts - verbs: - - get - - list - - watch - - create - - update - - patch - - delete - - apiGroups: - - networking.k8s.io - resources: - - networkpolicies - verbs: - - get - - list - - create - - update - - delete - - apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete - - apiGroups: - - "" - resources: - - events - verbs: - - create - - patch - serviceAccountName: simple-bundle-manager + - rules: + - apiGroups: + - "" + resources: + - configmaps + - serviceaccounts + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - networking.k8s.io + resources: + - networkpolicies + verbs: + - get + - list + - create + - update + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + serviceAccountName: simple-bundle-manager strategy: deployment installModes: - - supported: false - type: OwnNamespace - - supported: true - type: SingleNamespace - - supported: false - type: MultiNamespace - - supported: true - type: AllNamespaces + - supported: false + type: OwnNamespace + - supported: true + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces keywords: - - registry + - registry links: - - name: simple-bundle - url: https://simple-bundle.domain + - name: simple-bundle + url: https://simple-bundle.domain maintainers: - - email: main#simple-bundle.domain - name: Simple Bundle + - email: main#simple-bundle.domain + name: Simple Bundle maturity: beta provider: name: Simple Bundle diff --git a/testdata/images/bundles/test-operator/v1.0.0/manifests/testoperator.networkpolicy.yaml b/testdata/images/bundles/test-operator/v1.0.0/manifests/testoperator.networkpolicy.yaml index d87648e6f..20a5ea834 100644 --- a/testdata/images/bundles/test-operator/v1.0.0/manifests/testoperator.networkpolicy.yaml +++ b/testdata/images/bundles/test-operator/v1.0.0/manifests/testoperator.networkpolicy.yaml @@ -5,4 +5,4 @@ metadata: spec: podSelector: {} policyTypes: - - Ingress + - Ingress diff --git a/testdata/images/bundles/test-operator/v1.2.0/manifests/bundle.configmap.yaml b/testdata/images/bundles/test-operator/v1.2.0/manifests/bundle.configmap.yaml index 0d696a6d4..22ee50090 100644 --- a/testdata/images/bundles/test-operator/v1.2.0/manifests/bundle.configmap.yaml +++ b/testdata/images/bundles/test-operator/v1.2.0/manifests/bundle.configmap.yaml @@ -4,9 +4,8 @@ metadata: name: test-configmap annotations: shouldNotTemplate: > - The namespace is {{ $labels.namespace }}. The templated - $labels.namespace is NOT expected to be processed by OLM's - rendering engine for registry+v1 bundles. + The namespace is {{ $labels.namespace }}. The templated $labels.namespace is NOT expected to be processed by OLM's rendering engine for registry+v1 bundles. + data: version: "v1.2.0" name: "test-configmap" diff --git a/testdata/images/bundles/test-operator/v1.2.0/manifests/olm.operatorframework.com_olme2etest.yaml b/testdata/images/bundles/test-operator/v1.2.0/manifests/olm.operatorframework.com_olme2etest.yaml index fcfd4aeaf..44e64cef7 100644 --- a/testdata/images/bundles/test-operator/v1.2.0/manifests/olm.operatorframework.com_olme2etest.yaml +++ b/testdata/images/bundles/test-operator/v1.2.0/manifests/olm.operatorframework.com_olme2etest.yaml @@ -1,4 +1,3 @@ ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/testdata/images/bundles/test-operator/v1.2.0/manifests/testoperator.clusterserviceversion.yaml b/testdata/images/bundles/test-operator/v1.2.0/manifests/testoperator.clusterserviceversion.yaml index db7cdb635..90621bc6d 100644 --- a/testdata/images/bundles/test-operator/v1.2.0/manifests/testoperator.clusterserviceversion.yaml +++ b/testdata/images/bundles/test-operator/v1.2.0/manifests/testoperator.clusterserviceversion.yaml @@ -35,115 +35,115 @@ spec: description: OLM E2E Testing Operator displayName: test-operator icon: - - base64data: "" - mediatype: "" + - base64data: "" + mediatype: "" install: spec: deployments: - - label: - app.kubernetes.io/component: controller - app.kubernetes.io/name: test-operator - app.kubernetes.io/version: 1.2.0 - name: test-operator - spec: - replicas: 1 - selector: - matchLabels: - app: olme2etest - template: - metadata: - labels: + - label: + app.kubernetes.io/component: controller + app.kubernetes.io/name: test-operator + app.kubernetes.io/version: 1.2.0 + name: test-operator + spec: + replicas: 1 + selector: + matchLabels: app: olme2etest - spec: - terminationGracePeriodSeconds: 0 - containers: - - name: busybox - image: busybox:1.37 - command: - - 'sleep' - - '1000' - securityContext: - runAsUser: 1000 - runAsNonRoot: true - serviceAccountName: simple-bundle-manager + template: + metadata: + labels: + app: olme2etest + spec: + terminationGracePeriodSeconds: 0 + containers: + - name: busybox + image: busybox:1.37 + command: + - 'sleep' + - '1000' + securityContext: + runAsUser: 1000 + runAsNonRoot: true + serviceAccountName: simple-bundle-manager clusterPermissions: - - rules: - - apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create - - apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create - serviceAccountName: simple-bundle-manager + - rules: + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create + serviceAccountName: simple-bundle-manager permissions: - - rules: - - apiGroups: - - "" - resources: - - configmaps - - serviceaccounts - verbs: - - get - - list - - watch - - create - - update - - patch - - delete - - apiGroups: - - networking.k8s.io - resources: - - networkpolicies - verbs: - - get - - list - - create - - update - - delete - - apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete - - apiGroups: - - "" - resources: - - events - verbs: - - create - - patch - serviceAccountName: simple-bundle-manager + - rules: + - apiGroups: + - "" + resources: + - configmaps + - serviceaccounts + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - networking.k8s.io + resources: + - networkpolicies + verbs: + - get + - list + - create + - update + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + serviceAccountName: simple-bundle-manager strategy: deployment installModes: - - supported: false - type: OwnNamespace - - supported: true - type: SingleNamespace - - supported: false - type: MultiNamespace - - supported: true - type: AllNamespaces + - supported: false + type: OwnNamespace + - supported: true + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces keywords: - - registry + - registry links: - - name: simple-bundle - url: https://simple-bundle.domain + - name: simple-bundle + url: https://simple-bundle.domain maintainers: - - email: main#simple-bundle.domain - name: Simple Bundle + - email: main#simple-bundle.domain + name: Simple Bundle maturity: beta provider: name: Simple Bundle diff --git a/testdata/images/bundles/test-operator/v1.2.0/manifests/testoperator.networkpolicy.yaml b/testdata/images/bundles/test-operator/v1.2.0/manifests/testoperator.networkpolicy.yaml index d87648e6f..20a5ea834 100644 --- a/testdata/images/bundles/test-operator/v1.2.0/manifests/testoperator.networkpolicy.yaml +++ b/testdata/images/bundles/test-operator/v1.2.0/manifests/testoperator.networkpolicy.yaml @@ -5,4 +5,4 @@ metadata: spec: podSelector: {} policyTypes: - - Ingress + - Ingress diff --git a/testdata/images/bundles/webhook-operator/v0.0.1/manifests/webhook-operator-metrics-reader_rbac.authorization.k8s.io_v1beta1_clusterrole.yaml b/testdata/images/bundles/webhook-operator/v0.0.1/manifests/webhook-operator-metrics-reader_rbac.authorization.k8s.io_v1beta1_clusterrole.yaml index 20f88a159..2394392b6 100644 --- a/testdata/images/bundles/webhook-operator/v0.0.1/manifests/webhook-operator-metrics-reader_rbac.authorization.k8s.io_v1beta1_clusterrole.yaml +++ b/testdata/images/bundles/webhook-operator/v0.0.1/manifests/webhook-operator-metrics-reader_rbac.authorization.k8s.io_v1beta1_clusterrole.yaml @@ -4,7 +4,7 @@ metadata: creationTimestamp: null name: webhook-operator-metrics-reader rules: -- nonResourceURLs: - - /metrics - verbs: - - get + - nonResourceURLs: + - /metrics + verbs: + - get diff --git a/testdata/images/bundles/webhook-operator/v0.0.1/manifests/webhook.operators.coreos.io_webhooktests.yaml b/testdata/images/bundles/webhook-operator/v0.0.1/manifests/webhook.operators.coreos.io_webhooktests.yaml index 0f936d962..6d82f2c96 100644 --- a/testdata/images/bundles/webhook-operator/v0.0.1/manifests/webhook.operators.coreos.io_webhooktests.yaml +++ b/testdata/images/bundles/webhook-operator/v0.0.1/manifests/webhook.operators.coreos.io_webhooktests.yaml @@ -50,12 +50,10 @@ spec: description: spec defines the desired state of WebhookTest properties: mutate: - description: Mutate is a field that will be set to true by the mutating - webhook. + description: Mutate is a field that will be set to true by the mutating webhook. type: boolean valid: - description: Valid must be set to true or the validation webhook will - reject the resource. + description: Valid must be set to true or the validation webhook will reject the resource. type: boolean required: - valid @@ -67,16 +65,15 @@ spec: description: |- conditions represent the current state of the WebhookTest resource. Each condition has a unique type and reflects the status of a specific aspect of the resource. - + Standard condition types include: - "Available": the resource is fully functional - "Progressing": the resource is being created or updated - "Degraded": the resource failed to reach or maintain its desired state - + The status of each condition is one of True, False, or Unknown. items: - description: Condition contains details for one aspect of the current - state of this API Resource. + description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- @@ -166,16 +163,13 @@ spec: description: spec defines the desired state of WebhookTest properties: conversion: - description: Conversion is an example field of WebhookTest. Edit WebhookTest_types.go - to remove/update + description: Conversion is an example field of WebhookTest. Edit WebhookTest_types.go to remove/update properties: mutate: - description: Mutate is a field that will be set to true by the - mutating webhook. + description: Mutate is a field that will be set to true by the mutating webhook. type: boolean valid: - description: Valid must be set to true or the validation webhook - will reject the resource. + description: Valid must be set to true or the validation webhook will reject the resource. type: boolean required: - valid @@ -190,16 +184,15 @@ spec: description: |- conditions represent the current state of the WebhookTest resource. Each condition has a unique type and reflects the status of a specific aspect of the resource. - + Standard condition types include: - "Available": the resource is fully functional - "Progressing": the resource is being created or updated - "Degraded": the resource failed to reach or maintain its desired state - + The status of each condition is one of True, False, or Unknown. items: - description: Condition contains details for one aspect of the current - state of this API Resource. + description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- diff --git a/testdata/images/catalogs/test-catalog/v1/configs/catalog.yaml b/testdata/images/catalogs/test-catalog/v1/configs/catalog.yaml index 2fead8261..437175a4e 100644 --- a/testdata/images/catalogs/test-catalog/v1/configs/catalog.yaml +++ b/testdata/images/catalogs/test-catalog/v1/configs/catalog.yaml @@ -1,4 +1,3 @@ ---- schema: olm.package name: test defaultChannel: beta @@ -48,7 +47,6 @@ properties: value: packageName: test version: 1.2.0 - --- schema: olm.package name: test-mirrored @@ -109,3 +107,43 @@ properties: value: packageName: webhook-operator version: 0.0.1 +--- +schema: olm.package +name: own-namespace-operator +defaultChannel: alpha +--- +schema: olm.channel +name: alpha +package: own-namespace-operator +entries: + - name: own-namespace-operator.1.0.0 +--- +schema: olm.bundle +name: own-namespace-operator.1.0.0 +package: own-namespace-operator +image: docker-registry.operator-controller-e2e.svc.cluster.local:5000/bundles/registry-v1/own-namespace-operator:v1.0.0 +properties: + - type: olm.package + value: + packageName: own-namespace-operator + version: 1.0.0 +--- +schema: olm.package +name: single-namespace-operator +defaultChannel: alpha +--- +schema: olm.channel +name: alpha +package: single-namespace-operator +entries: + - name: single-namespace-operator.1.0.0 +--- +schema: olm.bundle +name: single-namespace-operator.1.0.0 +package: single-namespace-operator +image: docker-registry.operator-controller-e2e.svc.cluster.local:5000/bundles/registry-v1/single-namespace-operator:v1.0.0 +properties: + - type: olm.package + value: + packageName: single-namespace-operator + version: 1.0.0 diff --git a/testdata/images/catalogs/test-catalog/v2/configs/catalog.yaml b/testdata/images/catalogs/test-catalog/v2/configs/catalog.yaml index e40cb3c56..2e82b1229 100644 --- a/testdata/images/catalogs/test-catalog/v2/configs/catalog.yaml +++ b/testdata/images/catalogs/test-catalog/v2/configs/catalog.yaml @@ -1,4 +1,3 @@ ---- schema: olm.package name: test defaultChannel: beta