diff --git a/cmd/main.go b/cmd/main.go index 8fd068306..9a73f6bf7 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -43,6 +43,7 @@ import ( gitopsv1alpha1 "github.com/hybrid-cloud-patterns/patterns-operator/api/v1alpha1" controllers "github.com/hybrid-cloud-patterns/patterns-operator/internal/controller" "github.com/hybrid-cloud-patterns/patterns-operator/version" + configv1 "github.com/openshift/api/config/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -58,6 +59,7 @@ func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(gitopsv1alpha1.AddToScheme(scheme)) + utilruntime.Must(configv1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 0b6b5a20c..8805f62aa 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -13,4 +13,4 @@ kind: Kustomization images: - name: controller newName: quay.io/validatedpatterns/patterns-operator - newTag: 0.0.63 + newTag: 0.0.64 diff --git a/config/samples/test-pattern.yaml b/config/samples/test-pattern.yaml new file mode 100644 index 000000000..5e752c0ab --- /dev/null +++ b/config/samples/test-pattern.yaml @@ -0,0 +1,13 @@ +apiVersion: gitops.hybrid-cloud-patterns.io/v1alpha1 +kind: Pattern +metadata: + name: test-pattern-infra-params + namespace: default +spec: + clusterGroupName: test-group + gitSpec: + targetRepo: "https://github.com/validatedpatterns/multicloud-gitops" + targetRevision: "main" + multiSourceConfig: + enabled: false + helmRepoUrl: "https://charts.validatedpatterns.io/" diff --git a/internal/controller/argo.go b/internal/controller/argo.go index a25fa0e20..848aa7511 100644 --- a/internal/controller/argo.go +++ b/internal/controller/argo.go @@ -24,6 +24,7 @@ import ( "strconv" "strings" + configv1 "github.com/openshift/api/config/v1" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -347,7 +348,7 @@ func getArgoCD(client dynamic.Interface, name, namespace string) (*argooperator. return argo, err } -func newApplicationParameters(p *api.Pattern) []argoapi.HelmParameter { +func newApplicationParameters(p *api.Pattern, infra *configv1.Infrastructure) []argoapi.HelmParameter { parameters := []argoapi.HelmParameter{ { Name: "global.pattern", @@ -411,6 +412,20 @@ func newApplicationParameters(p *api.Pattern) []argoapi.HelmParameter { Value: p.Spec.ExperimentalCapabilities, }, } + if infra != nil { + log.Printf("Adding infrastructure parameters: clusterAPIServerURL=%s, controlPlaneTopology=%s", infra.Status.APIServerURL, infra.Status.ControlPlaneTopology) + parameters = append(parameters, argoapi.HelmParameter{ + Name: "global.clusterAPIServerURL", + Value: infra.Status.APIServerURL, + }, + argoapi.HelmParameter{ + Name: "global.controlPlaneTopology", + Value: string(infra.Status.ControlPlaneTopology), + }, + ) + } else { + log.Printf("Warning: infra is nil, skipping infrastructure parameters (global.clusterAPIServerURL and global.controlPlaneTopology)") + } parameters = append(parameters, argoapi.HelmParameter{ Name: "global.multiSourceTargetRevision", Value: getClusterGroupChartVersion(p), @@ -491,7 +506,7 @@ func newApplicationValues(p *api.Pattern) string { // libraries. E.g. a string '/overrides/values-{{ $.Values.global.clusterPlatform }}.yaml' // will be converted to '/overrides/values-AWS.yaml' // 4. We return the list of templated strings back as an array -func getSharedValueFiles(p *api.Pattern, prefix string) ([]string, error) { +func getSharedValueFiles(p *api.Pattern, prefix string, infra *configv1.Infrastructure) ([]string, error) { gitDir := p.Status.LocalCheckoutPath if _, err := os.Stat(gitDir); err != nil { return nil, fmt.Errorf("%s path does not exist", gitDir) @@ -521,7 +536,7 @@ func getSharedValueFiles(p *api.Pattern, prefix string) ([]string, error) { if !ok { return nil, fmt.Errorf("type assertion failed at index %d: Not a string", i) } - valueMap := convertArgoHelmParametersToMap(newApplicationParameters(p)) + valueMap := convertArgoHelmParametersToMap(newApplicationParameters(p, infra)) templatedString, err := helmTpl(str, valueFiles, valueMap) // we only log an error, but try to keep going @@ -595,9 +610,9 @@ func commonApplicationSpec(p *api.Pattern, sources []argoapi.ApplicationSource) return spec } -func commonApplicationSourceHelm(p *api.Pattern, prefix string) *argoapi.ApplicationSourceHelm { +func commonApplicationSourceHelm(p *api.Pattern, prefix string, infra *configv1.Infrastructure) *argoapi.ApplicationSourceHelm { valueFiles := newApplicationValueFiles(p, prefix) - sharedValueFiles, err := getSharedValueFiles(p, prefix) + sharedValueFiles, err := getSharedValueFiles(p, prefix, infra) if err != nil { fmt.Printf("Could not fetch sharedValueFiles: %s", err) } @@ -607,7 +622,7 @@ func commonApplicationSourceHelm(p *api.Pattern, prefix string) *argoapi.Applica ValueFiles: valueFiles, // Parameters is a list of Helm parameters which are passed to the helm template command upon manifest generation - Parameters: newApplicationParameters(p), + Parameters: newApplicationParameters(p, infra), // This is to be able to pass down the extraParams to the single applications Values: newApplicationValues(p), @@ -644,7 +659,7 @@ func newArgoOperatorApplication(p *api.Pattern, spec *argoapi.ApplicationSpec) * return &app } -func newSourceApplication(p *api.Pattern) *argoapi.Application { +func newSourceApplication(p *api.Pattern, infra *configv1.Infrastructure) *argoapi.Application { // Argo uses... // r := regexp.MustCompile("(/|:)") // root := filepath.Join(os.TempDir(), r.ReplaceAllString(NormalizeGitURL(rawRepoURL), "_")) @@ -653,7 +668,7 @@ func newSourceApplication(p *api.Pattern) *argoapi.Application { RepoURL: p.Spec.GitConfig.TargetRepo, Path: "common/clustergroup", TargetRevision: p.Spec.GitConfig.TargetRevision, - Helm: commonApplicationSourceHelm(p, ""), + Helm: commonApplicationSourceHelm(p, "", infra), } spec := commonApplicationSpec(p, []argoapi.ApplicationSource{source}) @@ -661,7 +676,7 @@ func newSourceApplication(p *api.Pattern) *argoapi.Application { return newArgoOperatorApplication(p, spec) } -func newMultiSourceApplication(p *api.Pattern) *argoapi.Application { +func newMultiSourceApplication(p *api.Pattern, infra *configv1.Infrastructure) *argoapi.Application { sources := []argoapi.ApplicationSource{} var baseSource *argoapi.ApplicationSource @@ -681,14 +696,14 @@ func newMultiSourceApplication(p *api.Pattern) *argoapi.Application { RepoURL: p.Spec.MultiSourceConfig.HelmRepoUrl, Chart: "clustergroup", TargetRevision: getClusterGroupChartVersion(p), - Helm: commonApplicationSourceHelm(p, "$patternref"), + Helm: commonApplicationSourceHelm(p, "$patternref", infra), } } else { baseSource = &argoapi.ApplicationSource{ RepoURL: p.Spec.MultiSourceConfig.ClusterGroupGitRepoUrl, Path: ".", TargetRevision: p.Spec.MultiSourceConfig.ClusterGroupChartGitRevision, - Helm: commonApplicationSourceHelm(p, "$patternref"), + Helm: commonApplicationSourceHelm(p, "$patternref", infra), } } sources = append(sources, *baseSource) @@ -712,14 +727,14 @@ func getClusterGroupChartVersion(p *api.Pattern) string { return clusterGroupChartVersion } -func newArgoApplication(p *api.Pattern) *argoapi.Application { +func newArgoApplication(p *api.Pattern, infra *configv1.Infrastructure) *argoapi.Application { // -- ArgoCD Application var targetApp *argoapi.Application if *p.Spec.MultiSourceConfig.Enabled { - targetApp = newMultiSourceApplication(p) + targetApp = newMultiSourceApplication(p, infra) } else { - targetApp = newSourceApplication(p) + targetApp = newSourceApplication(p, infra) } return targetApp diff --git a/internal/controller/argo_test.go b/internal/controller/argo_test.go index ff5a6089e..9e6d0e091 100644 --- a/internal/controller/argo_test.go +++ b/internal/controller/argo_test.go @@ -94,7 +94,7 @@ var _ = Describe("Argo Pattern", func() { TargetRevision: pattern.Spec.GitConfig.TargetRevision, Helm: &argoapi.ApplicationSourceHelm{ ValueFiles: newApplicationValueFiles(pattern, ""), - Parameters: newApplicationParameters(pattern), + Parameters: newApplicationParameters(pattern, nil), Values: newApplicationValues(pattern), IgnoreMissingValueFiles: true, }, @@ -137,7 +137,7 @@ var _ = Describe("Argo Pattern", func() { // This is needed to debug any failures as gomega truncates the diff output format.MaxDepth = 100 format.MaxLength = 0 - Expect(newArgoApplication(pattern)).To(Equal(argoApp)) + Expect(newArgoApplication(pattern, nil)).To(Equal(argoApp)) }) }) Context("Default multi source", func() { @@ -160,7 +160,7 @@ var _ = Describe("Argo Pattern", func() { *appSource, } multiSourceArgoApp.Spec.Sources[1].Helm.ValueFiles = newApplicationValueFiles(pattern, "$patternref") - Expect(newMultiSourceApplication(pattern)).To(Equal(multiSourceArgoApp)) + Expect(newMultiSourceApplication(pattern, nil)).To(Equal(multiSourceArgoApp)) }) }) Context("multiSource with MultiSourceClusterGroupChartGitRevision set", func() { @@ -184,7 +184,7 @@ var _ = Describe("Argo Pattern", func() { *appSource, } multiSourceArgoApp.Spec.Sources[1].Helm.ValueFiles = newApplicationValueFiles(pattern, "$patternref") - Expect(newMultiSourceApplication(pattern)).To(Equal(multiSourceArgoApp)) + Expect(newMultiSourceApplication(pattern, nil)).To(Equal(multiSourceArgoApp)) }) }) }) @@ -405,7 +405,7 @@ var _ = Describe("Argo Pattern", func() { } }) It("Test default newApplicationParameters", func() { - Expect(newApplicationParameters(pattern)).To(Equal(append(appParameters, + Expect(newApplicationParameters(pattern, nil)).To(Equal(append(appParameters, argoapi.HelmParameter{ Name: "global.multiSourceSupport", Value: "false", @@ -439,7 +439,7 @@ var _ = Describe("Argo Pattern", func() { Value: "test2value", }, } - Expect(newApplicationParameters(pattern)).To(Equal(append(appParameters, + Expect(newApplicationParameters(pattern, nil)).To(Equal(append(appParameters, argoapi.HelmParameter{ Name: "global.multiSourceSupport", Value: "false", @@ -472,7 +472,7 @@ var _ = Describe("Argo Pattern", func() { It("Test newApplicationParameters with multiSource", func() { tmpBool := true pattern.Spec.MultiSourceConfig.Enabled = &tmpBool - Expect(newApplicationParameters(pattern)).To(Equal(append(appParameters, + Expect(newApplicationParameters(pattern, nil)).To(Equal(append(appParameters, argoapi.HelmParameter{ Name: "global.multiSourceSupport", Value: "true", @@ -500,7 +500,7 @@ var _ = Describe("Argo Pattern", func() { var sources []argoapi.ApplicationSource BeforeEach(func() { - multiSourceArgoApp = newMultiSourceApplication(pattern) + multiSourceArgoApp = newMultiSourceApplication(pattern, nil) sources = multiSourceArgoApp.Spec.Sources }) It("compareSource() function identical", func() { diff --git a/internal/controller/pattern_controller.go b/internal/controller/pattern_controller.go index a7230d8b8..bcac96447 100644 --- a/internal/controller/pattern_controller.go +++ b/internal/controller/pattern_controller.go @@ -30,6 +30,7 @@ import ( kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" @@ -40,6 +41,7 @@ import ( argoapi "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" argoclient "github.com/argoproj/argo-cd/v3/pkg/client/clientset/versioned" + configv1 "github.com/openshift/api/config/v1" configclient "github.com/openshift/client-go/config/clientset/versioned" routeclient "github.com/openshift/client-go/route/clientset/versioned" olmclient "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" @@ -223,7 +225,17 @@ func (r *PatternReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct return r.actionPerformed(qualifiedInstance, ret, err) } - targetApp := newArgoApplication(qualifiedInstance) + // Fetch infrastructure for cluster API server URL and control plane topology + infra := &configv1.Infrastructure{} + if err := r.Get(ctx, types.NamespacedName{Name: "cluster"}, infra); err != nil { + // If infrastructure cannot be fetched, continue with nil (handled in newApplicationParameters) + log.Printf("Warning: Could not fetch infrastructure object: %v. Parameters global.clusterAPIServerURL and global.controlPlaneTopology will not be set.", err) + infra = nil + } else { + log.Printf("Successfully fetched infrastructure: APIServerURL=%s, ControlPlaneTopology=%s", infra.Status.APIServerURL, infra.Status.ControlPlaneTopology) + } + + targetApp := newArgoApplication(qualifiedInstance, infra) _ = controllerutil.SetOwnerReference(qualifiedInstance, targetApp, r.Scheme) app, err := getApplication(r.argoClient, applicationName(qualifiedInstance), clusterWideNS) if app == nil { @@ -510,7 +522,15 @@ func (r *PatternReconciler) finalizeObject(instance *api.Pattern) error { } ns := getClusterWideArgoNamespace() - targetApp := newArgoApplication(qualifiedInstance) + // Fetch infrastructure for cluster API server URL and control plane topology + infra := &configv1.Infrastructure{} + if err := r.Get(context.TODO(), types.NamespacedName{Name: "cluster"}, infra); err != nil { + // If infrastructure cannot be fetched, continue with nil (handled in newApplicationParameters) + log.Printf("Warning: Could not fetch infrastructure object during finalization: %v. Parameters global.clusterAPIServerURL and global.controlPlaneTopology will not be set.", err) + infra = nil + } + + targetApp := newArgoApplication(qualifiedInstance, infra) _ = controllerutil.SetOwnerReference(qualifiedInstance, targetApp, r.Scheme) app, _ := getApplication(r.argoClient, applicationName(qualifiedInstance), ns)