diff --git a/README.md b/README.md index e68410642..02839f298 100644 --- a/README.md +++ b/README.md @@ -15,13 +15,13 @@ Search OperatorHub for "pattern" and accept all the defaults ## Create the Multi-Cloud GitOps pattern ``` -kubectl create -f config/samples/gitops_v1alpha1_pattern.yaml +oc create -f config/samples/gitops_v1alpha1_pattern.yaml ``` ### Check the status ``` -kubectl get -f config/samples/gitops_v1alpha1_pattern.yaml -o yaml +oc get -f config/samples/gitops_v1alpha1_pattern.yaml -o yaml oc get applications -A -w ``` @@ -36,21 +36,27 @@ secret and then add the secrets via the UI (this approach is a bit more work) ### Delete the pattern ``` -kubectl delete -f config/samples/gitops_v1alpha1_pattern.yaml +oc delete -f config/samples/gitops_v1alpha1_pattern.yaml ``` -This will only remove the top-level application. -The subscription and anything created by Argo will not be removed and canmust be removed manually. -Removing the top-level application ensures that Argo won't try to put back anything you delete. +This action removes the `Pattern` instance only. -## Watch the logs - -When installing via UI the namespace will be `patterns-operator` (recommended) +If you annotate the Pattern instance with `patterns.gitops.hybrid-cloud-patterns.io/prune: "true"`: ``` -oc logs -n patterns-operator `oc get -n patterns-operator pods -o name --field-selector status.phase=Running | grep patterns` -c manager -f +oc annotate -f config/samples/gitops_v1alpha1_pattern.yaml patterns.gitops.hybrid-cloud-patterns.io/prune='true' ``` +Once the `Pattern` instance is deleted, the following resources will also be removed: + +- The top-level application of the hub cluster. +- The child applications of the hub cluster. +- The top-level application of the spoke clusters. +- The child applications of the spoke clusters. +- The `ManagedCluster` instances (excluding the `local-cluster`). + +**NOTE:** The GitOps Operator `Subscription` and the main `ArgoCD` instance will not be removed and must be removed manually. + ## Development ### Test your changes locally against a remote cluster diff --git a/cmd/main.go b/cmd/main.go index 1ac5f8978..d5b69740e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -29,12 +29,10 @@ import ( // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" - "k8s.io/apimachinery/pkg/api/errors" k8sruntime "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" - crclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/manager" @@ -46,9 +44,6 @@ import ( "github.com/hybrid-cloud-patterns/patterns-operator/version" consolev1 "github.com/openshift/api/console/v1" operatorv1 "github.com/openshift/api/operator/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" //+kubebuilder:scaffold:imports ) @@ -85,12 +80,6 @@ func main() { setupLog.Info("detected operator namespace", "namespace", controllers.DetectOperatorNamespace()) - // Create initial config map for gitops - err := createPatternsOperatorConfigMap() - if err != nil { - setupLog.Error(err, "unable to create config map") - } - mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, HealthProbeBindAddress: probeAddr, @@ -120,7 +109,7 @@ func main() { setupLog.Error(err, "unable to add console plugin runnable") } - analyticsEnabled := areAnalyticsEnabled(mgr.GetAPIReader()) + analyticsEnabled := strings.ToLower(os.Getenv("ANALYTICS")) != "false" setupLog.Info("analytics enabled", "enabled", analyticsEnabled) if err = (&controllers.PatternReconciler{ Client: mgr.GetClient(), @@ -162,44 +151,6 @@ func printVersion() { setupLog.Info(fmt.Sprintf("Build Date: %s", version.BuildDate)) } -// Creates the patterns operator configmap -// This will include configuration parameters that -// will allow operator configuration -func createPatternsOperatorConfigMap() error { - config, err := ctrl.GetConfig() - if err != nil { - return fmt.Errorf("failed to get config: %s", err) - } - clientset, err := kubernetes.NewForConfig(config) - if err != nil { - return fmt.Errorf("failed to call NewForConfig: %s", err) - } - - configMap := corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{ - Kind: "ConfigMap", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: controllers.OperatorConfigMap, - Namespace: controllers.DetectOperatorNamespace(), - }, - } - - _, err = clientset.CoreV1().ConfigMaps(controllers.DetectOperatorNamespace()).Get(context.Background(), controllers.OperatorConfigMap, metav1.GetOptions{}) - if err != nil && errors.IsNotFound(err) { - // if the configmap does not exist we create an empty one - _, err = clientset.CoreV1().ConfigMaps(controllers.DetectOperatorNamespace()).Create(context.Background(), &configMap, metav1.CreateOptions{}) - if err != nil { - return err - } - } else { - // if we had an error that is not IsNotFound we need to return it - return err - } - return nil -} - func registerComponentOrExit(mgr manager.Manager, f func(*k8sruntime.Scheme) error) { // Setup Scheme for all resources if err := f(mgr.GetScheme()); err != nil { @@ -208,22 +159,3 @@ func registerComponentOrExit(mgr manager.Manager, f func(*k8sruntime.Scheme) err } setupLog.Info(fmt.Sprintf("Component registered: %v", reflect.ValueOf(f))) } - -// areAnalyticsEnabled determines whether analytics are enabled. -// Precedence: Operator ConfigMap key "analytics.enabled" (true/false) > ENV ANALYTICS (false means disabled) -func areAnalyticsEnabled(reader crclient.Reader) bool { - enabled := strings.ToLower(os.Getenv("ANALYTICS")) != "false" - - var cm corev1.ConfigMap - err := reader.Get(context.Background(), crclient.ObjectKey{Namespace: controllers.DetectOperatorNamespace(), Name: controllers.OperatorConfigMap}, &cm) - if err != nil { - setupLog.Error(err, "error reading operator configmap for analytics setting") - return enabled - } - - if v, ok := cm.Data["analytics.enabled"]; ok { - return strings.EqualFold(v, "true") - } - - return enabled -} diff --git a/cmd/main_test.go b/cmd/main_test.go deleted file mode 100644 index d88937be4..000000000 --- a/cmd/main_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package main - -import ( - "os" - "testing" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - crclient "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - - controllers "github.com/hybrid-cloud-patterns/patterns-operator/internal/controller" -) - -func newFakeReader(objs ...crclient.Object) crclient.Reader { - scheme := runtime.NewScheme() - _ = corev1.AddToScheme(scheme) - builder := fake.NewClientBuilder().WithScheme(scheme) - if len(objs) > 0 { - builder = builder.WithObjects(objs...) - } - return builder.Build() -} - -func newOperatorConfigMap(analyticsEnabled string) *corev1.ConfigMap { - return &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: controllers.DetectOperatorNamespace(), - Name: controllers.OperatorConfigMap, - }, - Data: map[string]string{ - "analytics.enabled": analyticsEnabled, - }, - } -} - -func TestAreAnalyticsEnabledWithReader_ConfigMapTrue(t *testing.T) { - cm := newOperatorConfigMap("true") - reader := newFakeReader(cm) - - if got := areAnalyticsEnabled(reader); got != true { - t.Fatalf("expected true when configmap sets analytics.enabled=true, got %v", got) - } -} - -func TestAreAnalyticsEnabledWithReader_ConfigMapFalse(t *testing.T) { - cm := newOperatorConfigMap("false") - reader := newFakeReader(cm) - - if got := areAnalyticsEnabled(reader); got != false { - t.Fatalf("expected false when configmap sets analytics.enabled=false, got %v", got) - } -} - -func TestAreAnalyticsEnabledWithReader_NoConfigMap_EnvFalseDisables(t *testing.T) { - _ = os.Setenv("ANALYTICS", "false") - t.Cleanup(func() { _ = os.Unsetenv("ANALYTICS") }) - - reader := newFakeReader() - if got := areAnalyticsEnabled(reader); got != false { - t.Fatalf("expected false when no configmap and ANALYTICS=false, got %v", got) - } -} - -func TestAreAnalyticsEnabledWithReader_NoConfigMap_EnvTrueEnables(t *testing.T) { - _ = os.Setenv("ANALYTICS", "true") - t.Cleanup(func() { _ = os.Unsetenv("ANALYTICS") }) - - reader := newFakeReader() - if got := areAnalyticsEnabled(reader); got != true { - t.Fatalf("expected true when no configmap and ANALYTICS=true, got %v", got) - } -} diff --git a/internal/controller/argo.go b/internal/controller/argo.go index 45bbecc52..7b6021d72 100644 --- a/internal/controller/argo.go +++ b/internal/controller/argo.go @@ -62,7 +62,7 @@ func newArgoCD(name, namespace string, patternsOperatorConfig PatternsOperatorCo "g, cluster-admins, role:admin", "g, admin, role:admin", } - for argoAdmin := range strings.SplitSeq(patternsOperatorConfig.getValueWithDefault("gitops.additionalArgoAdmins"), ",") { + for argoAdmin := range strings.SplitSeq(patternsOperatorConfig.getStringValue("gitops.additionalArgoAdmins"), ",") { argoAdmin = strings.TrimSpace(argoAdmin) if argoAdmin != "" { argoPolicies = append(argoPolicies, "g, "+argoAdmin+", role:admin") @@ -160,7 +160,7 @@ health_status.message = "An install plan for a subscription is pending installat return health_status`, }, } - if strings.EqualFold(patternsOperatorConfig.getValueWithDefault("gitops.applicationHealthCheckEnabled"), "true") { + if patternsOperatorConfig.getBoolValue("gitops.applicationHealthCheckEnabled") { // As of ArgoCD 1.8 the Application health check was dropped (see https://github.com/argoproj/argo-cd/issues/3781), // but in app-of-apps pattern this is needed in order to implement children apps dependencies via sync-waves resourceHealthChecks = append(resourceHealthChecks, argooperator.ResourceHealthCheck{ @@ -978,9 +978,9 @@ func newArgoGiteaApplication(p *api.Pattern, patternsOperatorConfig PatternsOper }, Project: "default", Source: &argoapi.ApplicationSource{ - RepoURL: patternsOperatorConfig.getValueWithDefault("gitea.helmRepoUrl"), - TargetRevision: patternsOperatorConfig.getValueWithDefault("gitea.chartVersion"), - Chart: patternsOperatorConfig.getValueWithDefault("gitea.chartName"), + RepoURL: patternsOperatorConfig.getStringValue("gitea.helmRepoUrl"), + TargetRevision: patternsOperatorConfig.getStringValue("gitea.chartVersion"), + Chart: patternsOperatorConfig.getStringValue("gitea.chartName"), Helm: &argoapi.ApplicationSourceHelm{ Parameters: parameters, }, diff --git a/internal/controller/defaults.go b/internal/controller/defaults.go index b10f079da..caee5eb16 100644 --- a/internal/controller/defaults.go +++ b/internal/controller/defaults.go @@ -85,30 +85,3 @@ const ( // Experimental Capabilities that can be enabled // Currently none - -var DefaultPatternsOperatorConfig = map[string]string{ - "gitops.catalogSource": GitOpsDefaultCatalogSource, - "gitops.channel": GitOpsDefaultChannel, - "gitops.sourceNamespace": GitOpsDefaultCatalogSourceNamespace, - "gitops.installApprovalPlan": GitOpsDefaultApprovalPlan, - "gitops.csv": GitOpsDefaultCSV, - "gitops.additionalArgoAdmins": "", - "gitops.applicationHealthCheckEnabled": "false", - "gitea.chartName": GiteaChartName, - "gitea.helmRepoUrl": GiteaHelmRepoUrl, - "gitea.chartVersion": GiteaDefaultChartVersion, - "analytics.enabled": "true", - "catalog.image": "", -} - -type PatternsOperatorConfig map[string]string - -func (g PatternsOperatorConfig) getValueWithDefault(k string) string { - if v, present := g[k]; present { - return v - } - if defaultValue, present := DefaultPatternsOperatorConfig[k]; present { - return defaultValue - } - return "" -} diff --git a/internal/controller/pattern_controller.go b/internal/controller/pattern_controller.go index 8ba0f993b..dba817819 100644 --- a/internal/controller/pattern_controller.go +++ b/internal/controller/pattern_controller.go @@ -34,6 +34,7 @@ import ( "github.com/go-logr/logr" "github.com/hybrid-cloud-patterns/patterns-operator/internal/controller/console" + corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -67,6 +68,7 @@ import ( ) const ReconcileLoopRequeueTime = 180 * time.Second +const PruneAnnotation = "patterns.gitops.hybrid-cloud-patterns.io/prune" // PatternReconciler reconciles a Pattern object type PatternReconciler struct { @@ -149,14 +151,21 @@ func (r *PatternReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct return reconcile.Result{}, err } - patternsOperatorConfig := DefaultPatternsOperatorConfig + var patternsOperatorConfig PatternsOperatorConfig - configCM, err := GetPatternsOperatorConfigMap() - // If we hit an error that is not related to the configmap not existing bubble it up - if err != nil && !kerrors.IsNotFound(err) { + // We try to get the configuration ConfigMap. The method getPatternsOperatorConfigMap returns nil, nil if the ConfigMap doesn't exist + configCM, err := r.getPatternsOperatorConfigMap() + if err != nil { return r.actionPerformed(instance, "failed to get the configuration ConfigMap", err) } - if configCM != nil { + if configCM == nil { // If the ConfigMap doesn't exist, we create it + if err = r.createPatternsOperatorConfigMap(instance); err != nil { + return r.actionPerformed(instance, "failed to create the configuration ConfigMap", err) + } + } else { // If the ConfigMap exists, we set the ownership and get the configuration from Data + if err = r.setPatternsOperatorConfigMapOwnership(configCM, instance); err != nil { + return r.actionPerformed(instance, "failed to set ownership of configuration ConfigMap", err) + } patternsOperatorConfig = configCM.Data } @@ -398,8 +407,8 @@ func (r *PatternReconciler) reconcileGitOpsSubscription(qualifiedInstance *api.P if err := controllerutil.RemoveOwnerReference(qualifiedInstance, currentSub, r.Scheme); err == nil { changed = true } - operatorConfigMap, cmErr := GetPatternsOperatorConfigMap() - if cmErr == nil { + operatorConfigMap, cmErr := r.getPatternsOperatorConfigMap() + if cmErr == nil && operatorConfigMap != nil { if err := controllerutil.RemoveOwnerReference(operatorConfigMap, currentSub, r.Scheme); err == nil { changed = true } @@ -740,11 +749,11 @@ func (r *PatternReconciler) deleteHubApps(targetApp, app *argoapi.Application, n } func (r *PatternReconciler) finalizeObject(instance *api.Pattern) error { - // Add finalizer when object is created log.Printf("Finalizing pattern object") - // The object is being deleted - if controllerutil.ContainsFinalizer(instance, api.PatternFinalizer) || controllerutil.ContainsFinalizer(instance, metav1.FinalizerOrphanDependents) { + // The object is being deleted and, if prune is enabled, we want to delete all the dependent objects in cascade + if strings.EqualFold(instance.Annotations[PruneAnnotation], "true") && + (controllerutil.ContainsFinalizer(instance, api.PatternFinalizer) || controllerutil.ContainsFinalizer(instance, metav1.FinalizerOrphanDependents)) { // Prepare the app for cascaded deletion qualifiedInstance, err := r.applyDefaults(instance) if err != nil { @@ -907,6 +916,7 @@ func (r *PatternReconciler) SetupWithManager(mgr ctrl.Manager) error { var ctrlErr error r.ctrl, ctrlErr = ctrl.NewControllerManagedBy(mgr). For(&api.Pattern{}). + Owns(&corev1.ConfigMap{}). Build(r) return ctrlErr } @@ -1325,6 +1335,53 @@ func (r *PatternReconciler) getLocalGit(p *api.Pattern) (string, error) { return "", nil } +func (r *PatternReconciler) createPatternsOperatorConfigMap(p *api.Pattern) error { + configMap := corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: OperatorConfigMap, + Namespace: DetectOperatorNamespace(), + }, + } + if err := controllerutil.SetControllerReference(p, &configMap, r.Scheme); err != nil { + return err + } + if _, err := r.fullClient.CoreV1().ConfigMaps(DetectOperatorNamespace()).Create(context.Background(), &configMap, metav1.CreateOptions{}); err != nil { + return err + } + return nil +} + +func (r *PatternReconciler) getPatternsOperatorConfigMap() (*corev1.ConfigMap, error) { + cm, err := r.fullClient.CoreV1().ConfigMaps(DetectOperatorNamespace()).Get(context.Background(), OperatorConfigMap, metav1.GetOptions{}) + if err != nil { + if kerrors.IsNotFound(err) { + return nil, nil + } else { + return nil, err + } + } + return cm, nil +} + +func (r *PatternReconciler) setPatternsOperatorConfigMapOwnership(cm *corev1.ConfigMap, p *api.Pattern) error { + controllerRef := metav1.GetControllerOf(cm) + if controllerRef != nil && controllerRef.UID == p.GetUID() && controllerRef.Kind == p.Kind && controllerRef.APIVersion == p.APIVersion { + return nil + } + if err := controllerutil.SetControllerReference(p, cm, r.Scheme); err != nil { + return err + } + _, err := r.fullClient.CoreV1().ConfigMaps(DetectOperatorNamespace()).Update(context.Background(), cm, metav1.UpdateOptions{}) + if err != nil { + return err + } + return nil +} + func DropLocalGitPaths() error { // If there is a completely new local folder, let's remove the old one // User changed the target repo diff --git a/internal/controller/patterns_operator_config.go b/internal/controller/patterns_operator_config.go new file mode 100644 index 000000000..2adf05202 --- /dev/null +++ b/internal/controller/patterns_operator_config.go @@ -0,0 +1,37 @@ +package controllers + +import ( + "strings" +) + +type PatternsOperatorConfig map[string]string + +var DefaultPatternsOperatorConfig = PatternsOperatorConfig{ + "gitops.catalogSource": GitOpsDefaultCatalogSource, + "gitops.channel": GitOpsDefaultChannel, + "gitops.sourceNamespace": GitOpsDefaultCatalogSourceNamespace, + "gitops.installApprovalPlan": GitOpsDefaultApprovalPlan, + "gitops.csv": GitOpsDefaultCSV, + "gitops.additionalArgoAdmins": "", + "gitops.applicationHealthCheckEnabled": "false", + "gitea.chartName": GiteaChartName, + "gitea.helmRepoUrl": GiteaHelmRepoUrl, + "gitea.chartVersion": GiteaDefaultChartVersion, + "catalog.image": "", +} + +func (g PatternsOperatorConfig) getStringValue(k string) string { + if v, present := g[k]; present { + return v + } else { + return DefaultPatternsOperatorConfig[k] + } +} + +func (g PatternsOperatorConfig) getBoolValue(k string) bool { + if v, present := g[k]; present { + return strings.EqualFold(v, "true") + } else { + return strings.EqualFold(DefaultPatternsOperatorConfig[k], "true") + } +} diff --git a/internal/controller/defaults_test.go b/internal/controller/patterns_operator_config_test.go similarity index 53% rename from internal/controller/defaults_test.go rename to internal/controller/patterns_operator_config_test.go index d55ce8d42..3cb66c43f 100644 --- a/internal/controller/defaults_test.go +++ b/internal/controller/patterns_operator_config_test.go @@ -5,67 +5,81 @@ import ( . "github.com/onsi/gomega" ) -var _ = Describe("PatternsOperatorConfig getValueWithDefault", func() { +var _ = Describe("PatternsOperatorConfig get values", func() { Context("when the key exists in the config", func() { It("should return the config value", func() { config := PatternsOperatorConfig{ "gitops.channel": "custom-channel", } - Expect(config.getValueWithDefault("gitops.channel")).To(Equal("custom-channel")) + Expect(config.getStringValue("gitops.channel")).To(Equal("custom-channel")) }) }) Context("when the key does not exist in config but exists in defaults", func() { It("should return the default value for gitops.channel", func() { config := PatternsOperatorConfig{} - Expect(config.getValueWithDefault("gitops.channel")).To(Equal(GitOpsDefaultChannel)) + Expect(config.getStringValue("gitops.channel")).To(Equal(GitOpsDefaultChannel)) }) It("should return the default value for gitops.catalogSource", func() { config := PatternsOperatorConfig{} - Expect(config.getValueWithDefault("gitops.catalogSource")).To(Equal(GitOpsDefaultCatalogSource)) + Expect(config.getStringValue("gitops.catalogSource")).To(Equal(GitOpsDefaultCatalogSource)) }) It("should return the default value for gitops.sourceNamespace", func() { config := PatternsOperatorConfig{} - Expect(config.getValueWithDefault("gitops.sourceNamespace")).To(Equal(GitOpsDefaultCatalogSourceNamespace)) + Expect(config.getStringValue("gitops.sourceNamespace")).To(Equal(GitOpsDefaultCatalogSourceNamespace)) }) It("should return the default value for gitops.installApprovalPlan", func() { config := PatternsOperatorConfig{} - Expect(config.getValueWithDefault("gitops.installApprovalPlan")).To(Equal(GitOpsDefaultApprovalPlan)) + Expect(config.getStringValue("gitops.installApprovalPlan")).To(Equal(GitOpsDefaultApprovalPlan)) + }) + + It("should return the default value for gitops.csv", func() { + config := PatternsOperatorConfig{} + Expect(config.getStringValue("gitops.csv")).To(Equal("")) }) It("should return the default value for gitops.additionalArgoAdmins", func() { config := PatternsOperatorConfig{} - Expect(config.getValueWithDefault("gitops.additionalArgoAdmins")).To(Equal("")) + Expect(config.getStringValue("gitops.additionalArgoAdmins")).To(Equal("")) + }) + + It("should return the default value for gitops.applicationHealthCheckEnabled", func() { + config := PatternsOperatorConfig{} + Expect(config.getBoolValue("gitops.applicationHealthCheckEnabled")).To(BeFalse()) }) It("should return the default value for gitea.chartName", func() { config := PatternsOperatorConfig{} - Expect(config.getValueWithDefault("gitea.chartName")).To(Equal(GiteaChartName)) + Expect(config.getStringValue("gitea.chartName")).To(Equal(GiteaChartName)) }) It("should return the default value for gitea.helmRepoUrl", func() { config := PatternsOperatorConfig{} - Expect(config.getValueWithDefault("gitea.helmRepoUrl")).To(Equal(GiteaHelmRepoUrl)) + Expect(config.getStringValue("gitea.helmRepoUrl")).To(Equal(GiteaHelmRepoUrl)) }) It("should return the default value for gitea.chartVersion", func() { config := PatternsOperatorConfig{} - Expect(config.getValueWithDefault("gitea.chartVersion")).To(Equal(GiteaDefaultChartVersion)) + Expect(config.getStringValue("gitea.chartVersion")).To(Equal(GiteaDefaultChartVersion)) }) - It("should return the default value for analytics.enabled", func() { + It("should return the default value for catalog.image", func() { config := PatternsOperatorConfig{} - Expect(config.getValueWithDefault("analytics.enabled")).To(Equal("true")) + Expect(config.getStringValue("catalog.image")).To(Equal("")) }) }) Context("when the key does not exist in config or defaults", func() { - It("should return an empty string", func() { + It("should return an empty string for string parameters", func() { + config := PatternsOperatorConfig{} + Expect(config.getStringValue("nonexistent.key")).To(Equal("")) + }) + It("should return false for boolean parameters", func() { config := PatternsOperatorConfig{} - Expect(config.getValueWithDefault("nonexistent.key")).To(Equal("")) + Expect(config.getBoolValue("nonexistent.key")).To(BeFalse()) }) }) @@ -74,14 +88,14 @@ var _ = Describe("PatternsOperatorConfig getValueWithDefault", func() { config := PatternsOperatorConfig{ "gitops.channel": "gitops-1.99", } - Expect(config.getValueWithDefault("gitops.channel")).To(Equal("gitops-1.99")) + Expect(config.getStringValue("gitops.channel")).To(Equal("gitops-1.99")) }) }) Context("when config is nil", func() { It("should return the default value", func() { var config PatternsOperatorConfig - Expect(config.getValueWithDefault("gitops.channel")).To(Equal(GitOpsDefaultChannel)) + Expect(config.getStringValue("gitops.channel")).To(Equal(GitOpsDefaultChannel)) }) }) }) @@ -93,20 +107,16 @@ var _ = Describe("DefaultPatternsOperatorConfig", func() { "gitops.channel", "gitops.sourceNamespace", "gitops.installApprovalPlan", + "gitops.csv", + "gitops.additionalArgoAdmins", + "gitops.applicationHealthCheckEnabled", "gitea.chartName", "gitea.helmRepoUrl", "gitea.chartVersion", - "analytics.enabled", + "catalog.image", } for _, key := range expectedKeys { Expect(DefaultPatternsOperatorConfig).To(HaveKey(key)) } }) - - It("should have correct default values", func() { - Expect(DefaultPatternsOperatorConfig["gitops.catalogSource"]).To(Equal("redhat-operators")) - Expect(DefaultPatternsOperatorConfig["gitops.sourceNamespace"]).To(Equal("openshift-marketplace")) - Expect(DefaultPatternsOperatorConfig["gitops.installApprovalPlan"]).To(Equal("Automatic")) - Expect(DefaultPatternsOperatorConfig["analytics.enabled"]).To(Equal("true")) - }) }) diff --git a/internal/controller/subscription.go b/internal/controller/subscription.go index ab5244a8f..96382a5f9 100644 --- a/internal/controller/subscription.go +++ b/internal/controller/subscription.go @@ -34,18 +34,18 @@ func newSubscription(patternsOperatorConfig PatternsOperatorConfig, disableDefau var installPlanApproval operatorv1alpha1.Approval - if patternsOperatorConfig.getValueWithDefault("gitops.installApprovalPlan") == "Manual" { + if patternsOperatorConfig.getStringValue("gitops.installApprovalPlan") == "Manual" { installPlanApproval = operatorv1alpha1.ApprovalManual } else { installPlanApproval = operatorv1alpha1.ApprovalAutomatic } spec := &operatorv1alpha1.SubscriptionSpec{ - CatalogSource: patternsOperatorConfig.getValueWithDefault("gitops.catalogSource"), - CatalogSourceNamespace: patternsOperatorConfig.getValueWithDefault("gitops.sourceNamespace"), + CatalogSource: patternsOperatorConfig.getStringValue("gitops.catalogSource"), + CatalogSourceNamespace: patternsOperatorConfig.getStringValue("gitops.sourceNamespace"), Package: GitOpsDefaultPackageName, - Channel: patternsOperatorConfig.getValueWithDefault("gitops.channel"), - StartingCSV: patternsOperatorConfig.getValueWithDefault("gitops.csv"), + Channel: patternsOperatorConfig.getStringValue("gitops.channel"), + StartingCSV: patternsOperatorConfig.getStringValue("gitops.csv"), InstallPlanApproval: installPlanApproval, Config: &operatorv1alpha1.SubscriptionConfig{ Env: newSubscriptionEnvVars(disableDefaultInstance), diff --git a/internal/controller/utils.go b/internal/controller/utils.go index 6aef79a68..34c0fad71 100644 --- a/internal/controller/utils.go +++ b/internal/controller/utils.go @@ -41,7 +41,6 @@ import ( kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" - ctrl "sigs.k8s.io/controller-runtime" configv1 "github.com/openshift/api/config/v1" ) @@ -449,17 +448,3 @@ func IntOrZero(secret map[string][]byte, key string) (int64, error) { return strconv.ParseInt(string(val), 10, 64) } - -// Gets the configmap for the Patterns Operator. (Used as an owner reference for the operator itself.) -func GetPatternsOperatorConfigMap() (*corev1.ConfigMap, error) { - config, err := ctrl.GetConfig() - if err != nil { - return nil, fmt.Errorf("failed to get config: %s", err) - } - clientset, err := kubernetes.NewForConfig(config) - if err != nil { - return nil, fmt.Errorf("failed to call NewForConfig: %s", err) - } - - return clientset.CoreV1().ConfigMaps(DetectOperatorNamespace()).Get(context.Background(), OperatorConfigMap, metav1.GetOptions{}) -} diff --git a/internal/controller/values_test.go b/internal/controller/values_test.go index d5e35003f..f029a293d 100644 --- a/internal/controller/values_test.go +++ b/internal/controller/values_test.go @@ -405,40 +405,3 @@ var _ = Describe("CountApplicationsAndSets", func() { }) }) }) - -var _ = Describe("PatternsOperatorConfig getValueWithDefault", func() { - Context("when value exists in config", func() { - It("should return the configured value", func() { - config := PatternsOperatorConfig{"key1": "value1"} - Expect(config.getValueWithDefault("key1")).To(Equal("value1")) - }) - }) - - Context("when value does not exist in config but has a default", func() { - It("should return the default value", func() { - config := PatternsOperatorConfig{} - Expect(config.getValueWithDefault("gitops.channel")).To(Equal(GitOpsDefaultChannel)) - }) - }) - - Context("when value does not exist anywhere", func() { - It("should return empty string", func() { - config := PatternsOperatorConfig{} - Expect(config.getValueWithDefault("nonexistent.key")).To(Equal("")) - }) - }) - - Context("when config overrides a default", func() { - It("should return the config value not the default", func() { - config := PatternsOperatorConfig{"gitops.channel": "custom-channel"} - Expect(config.getValueWithDefault("gitops.channel")).To(Equal("custom-channel")) - }) - }) - - Context("when config is nil", func() { - It("should return the default value", func() { - var config PatternsOperatorConfig - Expect(config.getValueWithDefault("gitops.channel")).To(Equal(GitOpsDefaultChannel)) - }) - }) -})