From 1207faba62de49fbbdcbc687c63ffdfc5b2779a7 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 19 May 2026 15:01:25 +0200 Subject: [PATCH 1/2] Update golangci-lint to 2.12.2 --- .github/workflows/lint.yaml | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 936bfe644..c05ebd5fb 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -29,7 +29,7 @@ jobs: - name: Run golangci-lint uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0 with: - version: v2.11.3 + version: v2.12.2 args: --timeout 5m -v #--exclude SA5011 diff --git a/Makefile b/Makefile index f8904a15f..ed2f6074e 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ GOFLAGS=-mod=vendor REGISTRY ?= localhost UPLOADREGISTRY ?= quay.io/validatedpatterns GOLANGCI_IMG ?= docker.io/golangci/golangci-lint -GOLANGCI_VERSION ?= 2.11.3 +GOLANGCI_VERSION ?= 2.12.2 # CI uses a non-writable home dir, make sure .cache is writable From 4e7484d2df6285c5794c1d88c762c8d52ad0d534 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Tue, 19 May 2026 15:58:16 +0200 Subject: [PATCH 2/2] Fix all the goconst errors --- internal/controller/acm.go | 29 +++-- internal/controller/analytics.go | 4 +- internal/controller/argo.go | 102 ++++++++++++------ internal/controller/checkout.go | 15 +-- internal/controller/pattern_controller.go | 54 ++++++---- .../controller/patterns_operator_config.go | 43 +++++--- internal/controller/subscription.go | 12 +-- internal/controller/utils.go | 9 +- internal/controller/values.go | 12 ++- 9 files changed, 177 insertions(+), 103 deletions(-) diff --git a/internal/controller/acm.go b/internal/controller/acm.go index 5a7afcd4d..303faafec 100644 --- a/internal/controller/acm.go +++ b/internal/controller/acm.go @@ -28,10 +28,17 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" ) +const ( + apiVersionV1 = "v1" + acmGroup = "cluster.open-cluster-management.io" + acmManagedClusterRes = "managedclusters" + acmLocalCluster = "local-cluster" +) + func haveACMHub(r *PatternReconciler) bool { - gvrMCH := schema.GroupVersionResource{Group: "operator.open-cluster-management.io", Version: "v1", Resource: "multiclusterhubs"} + gvrMCH := schema.GroupVersionResource{Group: "operator.open-cluster-management.io", Version: apiVersionV1, Resource: "multiclusterhubs"} - mch, err := r.dynamicClient.Resource(gvrMCH).Namespace("open-cluster-management").Get(context.Background(), "multiclusterhub", metav1.GetOptions{}) + mch, err := r.dynamicClient.Resource(gvrMCH).Namespace(acmNamespace).Get(context.Background(), "multiclusterhub", metav1.GetOptions{}) if err != nil { log.Printf("Error obtaining hub: %s\n", err) return false @@ -39,7 +46,7 @@ func haveACMHub(r *PatternReconciler) bool { return strings.EqualFold( mch.GetAnnotations()["patterns.gitops.validatedpatterns.io/managed"], - "true", + boolTrue, ) } @@ -47,9 +54,9 @@ func haveACMHub(r *PatternReconciler) bool { // Returns a list of cluster names and an error func (r *PatternReconciler) listManagedClusters(ctx context.Context) ([]string, error) { gvrMC := schema.GroupVersionResource{ - Group: "cluster.open-cluster-management.io", - Version: "v1", - Resource: "managedclusters", + Group: acmGroup, + Version: apiVersionV1, + Resource: acmManagedClusterRes, } // ManagedCluster is a cluster-scoped resource, so no namespace needed @@ -62,7 +69,7 @@ func (r *PatternReconciler) listManagedClusters(ctx context.Context) ([]string, for _, item := range mcList.Items { name := item.GetName() // Exclude local-cluster (hub cluster) - if name != "local-cluster" { + if name != acmLocalCluster { clusterNames = append(clusterNames, name) } } @@ -74,9 +81,9 @@ func (r *PatternReconciler) listManagedClusters(ctx context.Context) ([]string, // Returns the number of clusters deleted and an error func (r *PatternReconciler) deleteManagedClusters(ctx context.Context) (int, error) { gvrMC := schema.GroupVersionResource{ - Group: "cluster.open-cluster-management.io", - Version: "v1", - Resource: "managedclusters", + Group: acmGroup, + Version: apiVersionV1, + Resource: acmManagedClusterRes, } // ManagedCluster is a cluster-scoped resource, so no namespace needed @@ -89,7 +96,7 @@ func (r *PatternReconciler) deleteManagedClusters(ctx context.Context) (int, err for _, item := range mcList.Items { name := item.GetName() // Exclude local-cluster (hub cluster) - if name == "local-cluster" { + if name == acmLocalCluster { continue } diff --git a/internal/controller/analytics.go b/internal/controller/analytics.go index cf79fdee8..8110b85e2 100644 --- a/internal/controller/analytics.go +++ b/internal/controller/analytics.go @@ -17,6 +17,8 @@ import ( "github.com/hybrid-cloud-patterns/patterns-operator/version" ) +const analyticsPattern = "Pattern" + type VpAnalyticsInterface interface { SendPatternInstallationInfo(p *api.Pattern) bool SendPatternStartEventInfo(p *api.Pattern) bool @@ -98,7 +100,7 @@ func getAnalyticsContext(p *api.Pattern) *analytics.Context { ctx := &analytics.Context{ Extra: map[string]any{ - "Pattern": p.Name, + analyticsPattern: p.Name, "Domain": getSimpleDomain(p), "OperatorVersion": version.Version, "RepoBaseName": getBaseGitRepo(p), diff --git a/internal/controller/argo.go b/internal/controller/argo.go index 68e422093..0fcc81974 100644 --- a/internal/controller/argo.go +++ b/internal/controller/argo.go @@ -44,9 +44,43 @@ import ( // Which ArgoCD objects we're creating const ( - ArgoCDGroup = "argoproj.io" - ArgoCDVersion = "v1beta1" - ArgoCDResource = "argocds" + ArgoCDGroup = "argoproj.io" + ArgoCDVersion = "v1beta1" + ArgoCDResource = "argocds" + ArgoCDKind = "ArgoCD" + ArgoCDAPIVersion = ArgoCDGroup + "/" + ArgoCDVersion + + ApplicationKind = "Application" + + DefaultProject = "default" + InClusterDestination = "in-cluster" + PatternRef = "patternref" + SyncOptionForce = "Force=true" +) + +// Unstructured map keys +const ( + FieldAPIVersion = "apiVersion" + FieldKind = "kind" + FieldMetadata = "metadata" + FieldName = "name" +) + +// Volume/ConfigMap names for CA bundle handling +const ( + KubeRootCACM = "kube-root-ca.crt" + CaBundlesVol = "ca-bundles" +) + +// Helm parameter names +const ( + ParamMultiSourceSupport = "global.multiSourceSupport" + ParamMultiSourceRepoUrl = "global.multiSourceRepoUrl" + ParamExperimentalCapabilities = "global.experimentalCapabilities" + ParamGitOpsSubNamespace = "global.gitOpsSubNamespace" + ParamVpArgoNamespace = "global.vpArgoNamespace" + ParamMultiSourceTargetRevision = "global.multiSourceTargetRevision" + ParamDeletePattern = "global.deletePattern" ) // ConsoleLink constants @@ -62,7 +96,7 @@ func newArgoCD(name, namespace string, patternsOperatorConfig PatternsOperatorCo "g, cluster-admins, role:admin", "g, admin, role:admin", } - for argoAdmin := range strings.SplitSeq(patternsOperatorConfig.getStringValue("gitops.additionalArgoAdmins"), ",") { + for argoAdmin := range strings.SplitSeq(patternsOperatorConfig.getStringValue(configKeyAdditionalAdmins), ",") { argoAdmin = strings.TrimSpace(argoAdmin) if argoAdmin != "" { argoPolicies = append(argoPolicies, "g, "+argoAdmin+", role:admin") @@ -160,12 +194,12 @@ health_status.message = "An install plan for a subscription is pending installat return health_status`, }, } - if patternsOperatorConfig.getBoolValue("gitops.applicationHealthCheckEnabled") { + if patternsOperatorConfig.getBoolValue(configKeyHealthCheck) { // 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{ - Group: "argoproj.io", - Kind: "Application", + Group: ArgoCDGroup, + Kind: ApplicationKind, Check: `local health_status = {} health_status.status = "Progressing" health_status.message = "" @@ -199,24 +233,24 @@ return health_status`, VolumeSource: v1.VolumeSource{ ConfigMap: &v1.ConfigMapVolumeSource{ LocalObjectReference: v1.LocalObjectReference{ - Name: "kube-root-ca.crt", + Name: KubeRootCACM, }, }, }, }, { - Name: "trusted-ca-bundle", + Name: trustedBundleCM, VolumeSource: v1.VolumeSource{ ConfigMap: &v1.ConfigMapVolumeSource{ LocalObjectReference: v1.LocalObjectReference{ - Name: "trusted-ca-bundle", + Name: trustedBundleCM, }, Optional: &trueBool, }, }, }, { - Name: "ca-bundles", + Name: CaBundlesVol, VolumeSource: v1.VolumeSource{ EmptyDir: &v1.EmptyDirVolumeSource{}, }, @@ -224,7 +258,7 @@ return health_status`, } initVolumeMounts := []v1.VolumeMount{ { - Name: "ca-bundles", + Name: CaBundlesVol, MountPath: "/etc/pki/tls/certs", }, } @@ -239,11 +273,11 @@ return health_status`, MountPath: "/var/run/kube-root-ca", // ca.crt field }, { - Name: "trusted-ca-bundle", + Name: trustedBundleCM, MountPath: "/var/run/trusted-ca", // ca-bundle.crt field }, { - Name: "ca-bundles", + Name: CaBundlesVol, MountPath: "/tmp/ca-bundles", }, }, @@ -257,8 +291,8 @@ return health_status`, s := argooperator.ArgoCD{ TypeMeta: metav1.TypeMeta{ - Kind: "ArgoCD", - APIVersion: "argoproj.io/v1beta1", + Kind: ArgoCDKind, + APIVersion: ArgoCDAPIVersion, }, ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -509,10 +543,10 @@ func createOrUpdateConsoleLink(client dynamic.Interface, argoName, argoNamespace consoleLinkObj := &unstructured.Unstructured{ Object: map[string]any{ - "apiVersion": ConsoleLinkGroup + "/" + ConsoleLinkVersion, - "kind": "ConsoleLink", - "metadata": map[string]any{ - "name": linkName, + FieldAPIVersion: ConsoleLinkGroup + "/" + ConsoleLinkVersion, + FieldKind: "ConsoleLink", + FieldMetadata: map[string]any{ + FieldName: linkName, }, "spec": map[string]any{ "applicationMenu": map[string]any{ @@ -614,28 +648,28 @@ func newApplicationParameters(p *api.Pattern) []argoapi.HelmParameter { Value: strconv.FormatBool(p.Spec.GitConfig.TokenSecret != ""), }, { - Name: "global.multiSourceSupport", + Name: ParamMultiSourceSupport, Value: strconv.FormatBool(*p.Spec.MultiSourceConfig.Enabled), }, { - Name: "global.multiSourceRepoUrl", + Name: ParamMultiSourceRepoUrl, Value: p.Spec.MultiSourceConfig.HelmRepoUrl, }, { - Name: "global.experimentalCapabilities", + Name: ParamExperimentalCapabilities, Value: p.Spec.ExperimentalCapabilities, }, } _, gitOpsSubNamespace := DetectGitOpsSubscription() parameters = append(parameters, argoapi.HelmParameter{ - Name: "global.gitOpsSubNamespace", + Name: ParamGitOpsSubNamespace, Value: gitOpsSubNamespace, }, argoapi.HelmParameter{ - Name: "global.vpArgoNamespace", + Name: ParamVpArgoNamespace, Value: getClusterWideArgoNamespace(), }, argoapi.HelmParameter{ - Name: "global.multiSourceTargetRevision", + Name: ParamMultiSourceTargetRevision, Value: getClusterGroupChartVersion(p), }) for _, extra := range p.Spec.ExtraParameters { @@ -662,7 +696,7 @@ func newApplicationParameters(p *api.Pattern) []argoapi.HelmParameter { deletePatternValue = "DeleteChildApps" } parameters = append(parameters, argoapi.HelmParameter{ - Name: "global.deletePattern", + Name: ParamDeletePattern, Value: string(deletePatternValue), ForceString: true, }) @@ -807,12 +841,12 @@ func commonSyncPolicy(p *api.Pattern) *argoapi.SyncPolicy { func commonApplicationSpec(p *api.Pattern, sources []argoapi.ApplicationSource) *argoapi.ApplicationSpec { spec := &argoapi.ApplicationSpec{ Destination: argoapi.ApplicationDestination{ - Name: "in-cluster", + Name: InClusterDestination, Namespace: p.Namespace, }, // Project is a reference to the project this application belongs to. // The empty string means that application belongs to the 'default' project. - Project: "default", + Project: DefaultProject, // IgnoreDifferences is a list of resources and their fields which should be ignored during comparison // IgnoreDifferences []ResourceIgnoreDifferences `json:"ignoreDifferences,omitempty" protobuf:"bytes,5,name=ignoreDifferences"` @@ -909,7 +943,7 @@ func newMultiSourceApplication(p *api.Pattern) *argoapi.Application { valuesSource := &argoapi.ApplicationSource{ RepoURL: p.Spec.GitConfig.TargetRepo, TargetRevision: p.Spec.GitConfig.TargetRevision, - Ref: "patternref", + Ref: PatternRef, } sources = append(sources, *valuesSource) @@ -984,10 +1018,10 @@ func newArgoGiteaApplication(p *api.Pattern, patternsOperatorConfig PatternsOper } spec := &argoapi.ApplicationSpec{ Destination: argoapi.ApplicationDestination{ - Name: "in-cluster", + Name: InClusterDestination, Namespace: GiteaNamespace, }, - Project: "default", + Project: DefaultProject, Source: &argoapi.ApplicationSource{ RepoURL: patternsOperatorConfig.getStringValue("gitea.helmRepoUrl"), TargetRevision: patternsOperatorConfig.getStringValue("gitea.chartVersion"), @@ -1296,14 +1330,14 @@ func updateHelmParameter(goal api.PatternParameter, actual []argoapi.HelmParamet // syncApplication syncs the application with prune and force options if such a sync is not already in progress. // Returns nil if a sync is already in progress, error otherwise func syncApplication(client argoclient.Interface, app *argoapi.Application, withPrune bool) error { - if app.Operation != nil && app.Operation.Sync != nil && app.Operation.Sync.Prune == withPrune && slices.Contains(app.Operation.Sync.SyncOptions, "Force=true") { + if app.Operation != nil && app.Operation.Sync != nil && app.Operation.Sync.Prune == withPrune && slices.Contains(app.Operation.Sync.SyncOptions, SyncOptionForce) { return nil } app.Operation = &argoapi.Operation{ Sync: &argoapi.SyncOperation{ Prune: withPrune, - SyncOptions: []string{"Force=true"}, + SyncOptions: []string{SyncOptionForce}, }, } diff --git a/internal/controller/checkout.go b/internal/controller/checkout.go index b14110cc6..b0370b647 100644 --- a/internal/controller/checkout.go +++ b/internal/controller/checkout.go @@ -51,6 +51,7 @@ const ( GitAuthGitHubApp GitAuthenticationBackend = 3 ) +const gitRemoteOrigin = "origin" const ContextTimeout = 15 * time.Second const GitCustomCAFile = "/tmp/vp-git-cas.pem" const GitHEAD = "HEAD" @@ -162,7 +163,7 @@ func getCommitFromTarget(repo *git.Repository, name string) (plumbing.Hash, erro if h, err := getHashFromReference(repo, plumbing.NewRemoteHEADReferenceName(name)); err == nil { return h, nil } - if h, err := getHashFromReference(repo, plumbing.NewRemoteReferenceName("origin", name)); err == nil { + if h, err := getHashFromReference(repo, plumbing.NewRemoteReferenceName(gitRemoteOrigin, name)); err == nil { return h, nil } @@ -267,7 +268,7 @@ func cloneRepo(fullClient kubernetes.Interface, gitOps GitOperations, url, direc func getFetchOptions(fullClient kubernetes.Interface, url string, secret map[string][]byte) (*git.FetchOptions, error) { var foptions = &git.FetchOptions{ - RemoteName: "origin", + RemoteName: gitRemoteOrigin, Force: true, InsecureSkipTLS: true, Tags: git.AllTags, @@ -297,7 +298,7 @@ func getCloneOptions(fullClient kubernetes.Interface, url string, secret map[str // Clone the given repository to the given directory var options = &git.CloneOptions{ URL: url, - RemoteName: "origin", + RemoteName: gitRemoteOrigin, Progress: os.Stdout, Depth: 0, SingleBranch: false, @@ -330,8 +331,8 @@ func getHttpAuth(secret map[string][]byte) *http.BasicAuth { // because access tokens can easily be revoked. // https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/ auth := &http.BasicAuth{ - Username: string(getField(secret, "username")), - Password: string(getField(secret, "password")), + Username: string(getField(secret, secretFieldUsername)), + Password: string(getField(secret, secretFieldPassword)), } return auth @@ -444,8 +445,8 @@ func detectGitAuthType(secret map[string][]byte) GitAuthenticationBackend { } // Username + Password - _, hasUser := secret["username"] - _, hasPassword := secret["password"] + _, hasUser := secret[secretFieldUsername] + _, hasPassword := secret[secretFieldPassword] if hasUser && hasPassword { return GitAuthPassword } diff --git a/internal/controller/pattern_controller.go b/internal/controller/pattern_controller.go index ff3ded620..8b954e03e 100644 --- a/internal/controller/pattern_controller.go +++ b/internal/controller/pattern_controller.go @@ -71,6 +71,16 @@ import ( const ReconcileLoopRequeueTime = 180 * time.Second +const ( + secretFieldUsername = "username" + secretFieldPassword = "password" + acmNamespace = "open-cluster-management" + searchFilterProperty = "property" + searchFilterCluster = "cluster" + searchFilterNamespace = "namespace" + searchFilterValues = "values" +) + // PatternReconciler reconciles a Pattern object type PatternReconciler struct { client.Client @@ -457,8 +467,8 @@ func (r *PatternReconciler) createGiteaInstance(input *api.Pattern, patternsOper } secretData := map[string][]byte{ - "username": []byte(GiteaAdminUser), - "password": []byte(giteaAdminPassword), + secretFieldUsername: []byte(GiteaAdminUser), + secretFieldPassword: []byte(giteaAdminPassword), } giteaAdminSecret := newSecret(GiteaAdminSecretName, GiteaNamespace, secretData, nil) err = r.Create(context.Background(), giteaAdminSecret) @@ -512,8 +522,8 @@ func (r *PatternReconciler) createGiteaInstance(input *api.Pattern, patternsOper } // Let's attempt to migrate the repo to Gitea - _, _, err = r.giteaOperations.MigrateGiteaRepo(r.fullClient, string(secret.Data["username"]), - string(secret.Data["password"]), + _, _, err = r.giteaOperations.MigrateGiteaRepo(r.fullClient, string(secret.Data[secretFieldUsername]), + string(secret.Data[secretFieldPassword]), input.Spec.GitConfig.OriginRepo, giteaRouteURL) if err != nil { @@ -565,7 +575,7 @@ func (r *PatternReconciler) applyDefaults(input *api.Pattern) (*api.Pattern, err // Cluster platform // oc get Infrastructure.config.openshift.io/cluster -o jsonpath='{.spec.platformSpec.type}' - clusterInfra, err := r.configClient.ConfigV1().Infrastructures().Get(context.Background(), "cluster", metav1.GetOptions{}) + clusterInfra, err := r.configClient.ConfigV1().Infrastructures().Get(context.Background(), searchFilterCluster, metav1.GetOptions{}) if err != nil { return output, err } else { @@ -600,7 +610,7 @@ func (r *PatternReconciler) applyDefaults(input *api.Pattern) (*api.Pattern, err // Derive cluster and domain names // oc get Ingress.config.openshift.io/cluster -o jsonpath='{.spec.domain}' - clusterIngress, err := r.configClient.ConfigV1().Ingresses().Get(context.Background(), "cluster", metav1.GetOptions{}) + clusterIngress, err := r.configClient.ConfigV1().Ingresses().Get(context.Background(), searchFilterCluster, metav1.GetOptions{}) if err != nil { return output, err } @@ -640,7 +650,7 @@ func (r *PatternReconciler) applyDefaults(input *api.Pattern) (*api.Pattern, err output.Spec.ClusterGroupName = "default" //nolint:goconst } if output.Spec.MultiSourceConfig.HelmRepoUrl == "" { - output.Spec.MultiSourceConfig.HelmRepoUrl = "https://charts.validatedpatterns.io/" + output.Spec.MultiSourceConfig.HelmRepoUrl = GiteaHelmRepoUrl } localCheckoutPath := getLocalGitPath(output.Spec.GitConfig.TargetRepo) @@ -759,7 +769,7 @@ func (r *PatternReconciler) finalizeObject(instance *api.Pattern) error { log.Printf("Finalizing pattern object") // 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[api.PruneAnnotation], "true") && + if strings.EqualFold(instance.Annotations[api.PruneAnnotation], boolTrue) && (controllerutil.ContainsFinalizer(instance, api.PatternFinalizer) || controllerutil.ContainsFinalizer(instance, metav1.FinalizerOrphanDependents)) { // Prepare the app for cascaded deletion qualifiedInstance, err := r.applyDefaults(instance) @@ -984,7 +994,7 @@ func (r *PatternReconciler) startArgoCDWatch() { argoCD.SetGroupVersionKind(schema.GroupVersionKind{ Group: ArgoCDGroup, Version: ArgoCDVersion, - Kind: "ArgoCD", + Kind: ArgoCDKind, }) err := r.ctrl.Watch(source.Kind(r.mgr.GetCache(), argoCD, @@ -1153,7 +1163,7 @@ func (r *PatternReconciler) checkSpokeApplicationsGone(appOfApps bool) (bool, er // User should run: kubectl port-forward -n open-cluster-management svc/search-search-api 4010:4010 searchURL := os.Getenv("ACM_SEARCH_API_URL") if searchURL == "" { - searchNamespace := "open-cluster-management" // Default namespace for ACM + searchNamespace := acmNamespace searchURL = fmt.Sprintf("https://search-search-api.%s.svc.cluster.local:4010/searchapi/graphql", searchNamespace) } @@ -1194,23 +1204,23 @@ func (r *PatternReconciler) checkSpokeApplicationsGone(appOfApps bool) (bool, er { "filters": []map[string]any{ { - "property": "apigroup", - "values": []string{"argoproj.io"}, + searchFilterProperty: "apigroup", + searchFilterValues: []string{ArgoCDGroup}, }, { - "property": "kind", - "values": []string{"Application"}, + searchFilterProperty: FieldKind, + searchFilterValues: []string{ApplicationKind}, }, { - "property": "cluster", - "values": []string{"!local-cluster"}, + searchFilterProperty: searchFilterCluster, + searchFilterValues: []string{"!local-cluster"}, }, { - "property": "namespace", - "values": ns, + searchFilterProperty: searchFilterNamespace, + searchFilterValues: ns, }, }, - "relatedKinds": []string{"Application"}, + "relatedKinds": []string{ApplicationKind}, "limit": 20000, }, }, @@ -1338,10 +1348,10 @@ func (r *PatternReconciler) getLocalGit(p *api.Pattern) (string, error) { // Here we dump all the CAs in kube-root-ca.crt and in openshift-config-managed/trusted-ca-bundle to a file // and then we call git config --global http.sslCAInfo /path/to/your/cacert.pem // This makes us trust our self-signed CAs or any custom CAs a customer might have. We try and ignore any errors here - if err = writeConfigMapKeyToFile(r.fullClient, "openshift-config-managed", "kube-root-ca.crt", "ca.crt", GitCustomCAFile, false); err != nil { + if err = writeConfigMapKeyToFile(r.fullClient, "openshift-config-managed", KubeRootCACM, "ca.crt", GitCustomCAFile, false); err != nil { fmt.Printf("Error while writing kube-root-ca.crt configmap to file: %v", err) } - if err = writeConfigMapKeyToFile(r.fullClient, "openshift-config-managed", "trusted-ca-bundle", "ca-bundle.crt", GitCustomCAFile, true); err != nil { + if err = writeConfigMapKeyToFile(r.fullClient, "openshift-config-managed", trustedBundleCM, "ca-bundle.crt", GitCustomCAFile, true); err != nil { fmt.Printf("Error while appending trusted-ca-bundle configmap to file: %v", err) } @@ -1352,7 +1362,7 @@ func (r *PatternReconciler) getLocalGit(p *api.Pattern) (string, error) { return "cloning pattern repo", err } } else { // If the cloned repository directory already existed we check if the URL changed - localURL, err := getGitRemoteURL(gitDir, "origin") + localURL, err := getGitRemoteURL(gitDir, gitRemoteOrigin) if err != nil { return "getting remote URL pattern repo", err } diff --git a/internal/controller/patterns_operator_config.go b/internal/controller/patterns_operator_config.go index 09f1be048..bb211397c 100644 --- a/internal/controller/patterns_operator_config.go +++ b/internal/controller/patterns_operator_config.go @@ -12,18 +12,31 @@ import ( type PatternsOperatorConfig map[string]string +const ( + configKeyCatalogSource = "gitops.catalogSource" + configKeyChannel = "gitops.channel" + configKeySourceNamespace = "gitops.sourceNamespace" + configKeyApprovalPlan = "gitops.installApprovalPlan" + configKeyCSV = "gitops.csv" + configKeyAdditionalAdmins = "gitops.additionalArgoAdmins" + configKeyHealthCheck = "gitops.applicationHealthCheckEnabled" + configMapKind = "ConfigMap" + boolTrue = "true" + boolFalse = "false" +) + 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": "", + configKeyCatalogSource: GitOpsDefaultCatalogSource, + configKeyChannel: GitOpsDefaultChannel, + configKeySourceNamespace: GitOpsDefaultCatalogSourceNamespace, + configKeyApprovalPlan: GitOpsDefaultApprovalPlan, + configKeyCSV: GitOpsDefaultCSV, + configKeyAdditionalAdmins: "", + configKeyHealthCheck: boolFalse, + "gitea.chartName": GiteaChartName, + "gitea.helmRepoUrl": GiteaHelmRepoUrl, + "gitea.chartVersion": GiteaDefaultChartVersion, + "catalog.image": "", } func (g PatternsOperatorConfig) getStringValue(k string) string { @@ -36,9 +49,9 @@ func (g PatternsOperatorConfig) getStringValue(k string) string { func (g PatternsOperatorConfig) getBoolValue(k string) bool { if v, present := g[k]; present { - return strings.EqualFold(v, "true") + return strings.EqualFold(v, boolTrue) } else { - return strings.EqualFold(DefaultPatternsOperatorConfig[k], "true") + return strings.EqualFold(DefaultPatternsOperatorConfig[k], boolTrue) } } @@ -48,8 +61,8 @@ func (g PatternsOperatorConfig) getBoolValue(k string) bool { func CreatePatternsOperatorConfigMap(ctx context.Context, cl client.Client) (*corev1.ConfigMap, error) { configMap := corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{ - Kind: "ConfigMap", - APIVersion: "v1", + Kind: configMapKind, + APIVersion: apiVersionV1, }, ObjectMeta: metav1.ObjectMeta{ Name: OperatorConfigMap, diff --git a/internal/controller/subscription.go b/internal/controller/subscription.go index 96382a5f9..c8d62fc0a 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.getStringValue("gitops.installApprovalPlan") == "Manual" { + if patternsOperatorConfig.getStringValue(configKeyApprovalPlan) == "Manual" { installPlanApproval = operatorv1alpha1.ApprovalManual } else { installPlanApproval = operatorv1alpha1.ApprovalAutomatic } spec := &operatorv1alpha1.SubscriptionSpec{ - CatalogSource: patternsOperatorConfig.getStringValue("gitops.catalogSource"), - CatalogSourceNamespace: patternsOperatorConfig.getStringValue("gitops.sourceNamespace"), + CatalogSource: patternsOperatorConfig.getStringValue(configKeyCatalogSource), + CatalogSourceNamespace: patternsOperatorConfig.getStringValue(configKeySourceNamespace), Package: GitOpsDefaultPackageName, - Channel: patternsOperatorConfig.getStringValue("gitops.channel"), - StartingCSV: patternsOperatorConfig.getStringValue("gitops.csv"), + Channel: patternsOperatorConfig.getStringValue(configKeyChannel), + StartingCSV: patternsOperatorConfig.getStringValue(configKeyCSV), InstallPlanApproval: installPlanApproval, Config: &operatorv1alpha1.SubscriptionConfig{ Env: newSubscriptionEnvVars(disableDefaultInstance), @@ -79,7 +79,7 @@ func newSubscriptionEnvVars(disableDefaultInstance bool) []corev1.EnvVar { if disableDefaultInstance { envVars = append(envVars, corev1.EnvVar{ Name: "DISABLE_DEFAULT_ARGOCD_INSTANCE", - Value: "true", + Value: boolTrue, }) } return envVars diff --git a/internal/controller/utils.go b/internal/controller/utils.go index 34c0fad71..c7107fa5c 100644 --- a/internal/controller/utils.go +++ b/internal/controller/utils.go @@ -50,6 +50,7 @@ var ( ) const trustedBundleCM = "trusted-ca-bundle" +const clusterVersionCompleted = "Completed" func logOnce(message string) { if _, ok := logKeys[message]; ok { @@ -110,7 +111,7 @@ func getPatternConditionByType(conditions []api.PatternCondition, conditionType func getCurrentClusterVersion(clusterversion *configv1.ClusterVersion) (*semver.Version, error) { // First, check the history for completed versions for _, v := range clusterversion.Status.History { - if v.State == "Completed" { + if v.State == clusterVersionCompleted { return parseAndReturnVersion(v.Version) } } @@ -242,7 +243,7 @@ func createTrustedBundleCM(fullClient kubernetes.Interface, namespace string) er Name: trustedBundleCM, Namespace: namespace, Labels: map[string]string{ - "config.openshift.io/inject-trusted-cabundle": "true", + "config.openshift.io/inject-trusted-cabundle": boolTrue, }, }, } @@ -374,12 +375,12 @@ func getHTTPSTransport(fullClient kubernetes.Interface) *nethttp.Transport { var trustedcabundle = "" if fullClient != nil { - kuberoot, err = getConfigMapKey(fullClient, "openshift-config-managed", "kube-root-ca.crt", "ca.crt") + kuberoot, err = getConfigMapKey(fullClient, "openshift-config-managed", KubeRootCACM, "ca.crt") if err != nil { fmt.Printf("Could not get kube-root-ca.crt configmap: %v\n", err) } - trustedcabundle, err = getConfigMapKey(fullClient, "openshift-config-managed", "trusted-ca-bundle", "ca-bundle.crt") + trustedcabundle, err = getConfigMapKey(fullClient, "openshift-config-managed", trustedBundleCM, "ca-bundle.crt") if err != nil { fmt.Printf("Could not get trusted-ca-bundle configmap: %v\n", err) } diff --git a/internal/controller/values.go b/internal/controller/values.go index 4350112f9..8fc576159 100644 --- a/internal/controller/values.go +++ b/internal/controller/values.go @@ -11,6 +11,12 @@ import ( "helm.sh/helm/v3/pkg/engine" ) +const ( + appSetKeyDestServer = "destinationServer" + appSetKeyGenerators = "generators" + helmChartAPIVersion = "v2" +) + func mergeHelmValues(files ...string) (map[string]any, error) { mergedValues := make(map[string]any) @@ -50,7 +56,7 @@ func helmTpl(templateString string, valueFiles []string, values map[string]any) // Create a fake chart with the template. fakeChart := &chart.Chart{ Metadata: &chart.Metadata{ - APIVersion: "v2", + APIVersion: helmChartAPIVersion, Name: "fake", Version: "0.1.0", }, @@ -87,7 +93,7 @@ func helmTpl(templateString string, valueFiles []string, values map[string]any) // Render the template. options := chartutil.ReleaseOptions{ Name: "fake-release", - Namespace: "default", + Namespace: DefaultProject, IsInstall: true, IsUpgrade: false, } @@ -113,7 +119,7 @@ func helmTpl(templateString string, valueFiles []string, values map[string]any) func countApplicationsAndSets(a any) (appCount, appSetsCount int) { applicationCount := 0 applicationSetsCount := 0 - applicationSetsKeys := []string{"generators", "generatorFile", "useGeneratorValues", "destinationServer", "destinationNamespace"} + applicationSetsKeys := []string{appSetKeyGenerators, "generatorFile", "useGeneratorValues", appSetKeyDestServer, "destinationNamespace"} m, ok := a.(map[string]any) if !ok {