diff --git a/cmd/argocd-repo-server/commands/argocd_repo_server.go b/cmd/argocd-repo-server/commands/argocd_repo_server.go index f8bb868f0bd0f..3b06e64c0619a 100644 --- a/cmd/argocd-repo-server/commands/argocd_repo_server.go +++ b/cmd/argocd-repo-server/commands/argocd_repo_server.go @@ -76,6 +76,7 @@ func NewCommand() *cobra.Command { disableManifestMaxExtractedSize bool includeHiddenDirectories bool cmpUseManifestGeneratePaths bool + argocdInstanceID string ) command := cobra.Command{ Use: cliName, @@ -138,6 +139,7 @@ func NewCommand() *cobra.Command { HelmRegistryMaxIndexSize: helmRegistryMaxIndexSizeQuantity.ToDec().Value(), IncludeHiddenDirectories: includeHiddenDirectories, CMPUseManifestGeneratePaths: cmpUseManifestGeneratePaths, + ArgoCDInstanceID: argocdInstanceID, }, askPassServer) errors.CheckError(err) @@ -244,6 +246,7 @@ func NewCommand() *cobra.Command { command.Flags().BoolVar(&disableManifestMaxExtractedSize, "disable-helm-manifest-max-extracted-size", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE", false), "Disable maximum size of helm manifest archives when extracted") command.Flags().BoolVar(&includeHiddenDirectories, "include-hidden-directories", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_INCLUDE_HIDDEN_DIRECTORIES", false), "Include hidden directories from Git") command.Flags().BoolVar(&cmpUseManifestGeneratePaths, "plugin-use-manifest-generate-paths", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_PLUGIN_USE_MANIFEST_GENERATE_PATHS", false), "Pass the resources described in argocd.argoproj.io/manifest-generate-paths value to the cmpserver to generate the application manifests.") + command.Flags().StringVar(&argocdInstanceID, "argocd-instance-id", env.StringFromEnv("ARGOCD_INSTANCE_ID", ""), "Server URL of the argocd instance that the repo server is part of") tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command) cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, cacheutil.Options{ OnClientCreated: func(client *redis.Client) { diff --git a/cmd/argocd/commands/app.go b/cmd/argocd/commands/app.go index 29b3ee2df27da..182fca03aac29 100644 --- a/cmd/argocd/commands/app.go +++ b/cmd/argocd/commands/app.go @@ -1384,6 +1384,10 @@ func groupObjsForDiff(resources *application.ManagedResourcesResponse, objs map[ errors.CheckError(err) } + if err := resourceTracking.SetAppInstanceID(local, argoSettings.URL); err != nil { + log.Warnf("Failed to set Application Instance ID due to missing or invalid ArgoCD URL in ArgoCD Configmap") + } + items = append(items, objKeyLiveTarget{key, live, local}) delete(objs, key) } diff --git a/common/common.go b/common/common.go index d2e47aa5b1607..1c1c6e19b9d73 100644 --- a/common/common.go +++ b/common/common.go @@ -182,6 +182,9 @@ const ( AnnotationKeyAppInstance = "argocd.argoproj.io/tracking-id" AnnotationInstallationID = "argocd.argoproj.io/installation-id" + // AnnotationKeyAppInstanceID is the Argo CD server URL that is managing this resource + AnnotationKeyAppInstanceID = "argocd.argoproj.io/instance-id" + // AnnotationCompareOptions is a comma-separated list of options for comparison AnnotationCompareOptions = "argocd.argoproj.io/compare-options" diff --git a/docs/operator-manual/server-commands/argocd-repo-server.md b/docs/operator-manual/server-commands/argocd-repo-server.md index 12e4d34d14028..9120b2e7b50ed 100644 --- a/docs/operator-manual/server-commands/argocd-repo-server.md +++ b/docs/operator-manual/server-commands/argocd-repo-server.md @@ -17,6 +17,7 @@ argocd-repo-server [flags] ``` --address string Listen on given address for incoming connections (default "0.0.0.0") --allow-oob-symlinks Allow out-of-bounds symlinks in repositories (not recommended) + --argocd-instance-id string Server URL of the argocd instance that the repo server is part of --default-cache-expiration duration Cache expiration default (default 24h0m0s) --disable-helm-manifest-max-extracted-size Disable maximum size of helm manifest archives when extracted --disable-tls Disable TLS on the gRPC endpoint diff --git a/manifests/base/repo-server/argocd-repo-server-deployment.yaml b/manifests/base/repo-server/argocd-repo-server-deployment.yaml index 02a11fabe9715..407f4ef9e6053 100644 --- a/manifests/base/repo-server/argocd-repo-server-deployment.yaml +++ b/manifests/base/repo-server/argocd-repo-server-deployment.yaml @@ -221,6 +221,12 @@ spec: key: reposerver.include.hidden.directories name: argocd-cmd-params-cm optional: true + - name: ARGOCD_INSTANCE_ID + valueFrom: + configMapKeyRef: + key: url + name: argocd-cm + optional: true - name: HELM_CACHE_HOME value: /helm-working-dir - name: HELM_CONFIG_HOME diff --git a/manifests/core-install.yaml b/manifests/core-install.yaml index 2c54ad05ea977..9049d4775a6fa 100644 --- a/manifests/core-install.yaml +++ b/manifests/core-install.yaml @@ -23112,6 +23112,12 @@ spec: key: reposerver.include.hidden.directories name: argocd-cmd-params-cm optional: true + - name: ARGOCD_INSTANCE_ID + valueFrom: + configMapKeyRef: + key: url + name: argocd-cm + optional: true - name: HELM_CACHE_HOME value: /helm-working-dir - name: HELM_CONFIG_HOME diff --git a/manifests/ha/install.yaml b/manifests/ha/install.yaml index 9615aa8c27d39..bec9761caeef7 100644 --- a/manifests/ha/install.yaml +++ b/manifests/ha/install.yaml @@ -24710,6 +24710,12 @@ spec: key: reposerver.include.hidden.directories name: argocd-cmd-params-cm optional: true + - name: ARGOCD_INSTANCE_ID + valueFrom: + configMapKeyRef: + key: url + name: argocd-cm + optional: true - name: HELM_CACHE_HOME value: /helm-working-dir - name: HELM_CONFIG_HOME diff --git a/manifests/ha/namespace-install.yaml b/manifests/ha/namespace-install.yaml index 1897f8a0901f4..3150d2ef67c41 100644 --- a/manifests/ha/namespace-install.yaml +++ b/manifests/ha/namespace-install.yaml @@ -2335,6 +2335,12 @@ spec: key: reposerver.include.hidden.directories name: argocd-cmd-params-cm optional: true + - name: ARGOCD_INSTANCE_ID + valueFrom: + configMapKeyRef: + key: url + name: argocd-cm + optional: true - name: HELM_CACHE_HOME value: /helm-working-dir - name: HELM_CONFIG_HOME diff --git a/manifests/install.yaml b/manifests/install.yaml index f64634743cac3..88acabd1c6c07 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -23782,6 +23782,12 @@ spec: key: reposerver.include.hidden.directories name: argocd-cmd-params-cm optional: true + - name: ARGOCD_INSTANCE_ID + valueFrom: + configMapKeyRef: + key: url + name: argocd-cm + optional: true - name: HELM_CACHE_HOME value: /helm-working-dir - name: HELM_CONFIG_HOME diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index 4f7ffccdbbb95..527863225f12c 100644 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -1407,6 +1407,12 @@ spec: key: reposerver.include.hidden.directories name: argocd-cmd-params-cm optional: true + - name: ARGOCD_INSTANCE_ID + valueFrom: + configMapKeyRef: + key: url + name: argocd-cm + optional: true - name: HELM_CACHE_HOME value: /helm-working-dir - name: HELM_CONFIG_HOME diff --git a/reposerver/repository/repository.go b/reposerver/repository/repository.go index cf3a22e0a8bbe..35c878d07b925 100644 --- a/reposerver/repository/repository.go +++ b/reposerver/repository/repository.go @@ -116,6 +116,7 @@ type RepoServerInitConstants struct { DisableHelmManifestMaxExtractedSize bool IncludeHiddenDirectories bool CMPUseManifestGeneratePaths bool + ArgoCDInstanceID string } // NewService returns a new instance of the Manifest service @@ -808,7 +809,7 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA, } } - manifestGenResult, err = GenerateManifests(ctx, opContext.appPath, repoRoot, commitSHA, q, false, s.gitCredsStore, s.initConstants.MaxCombinedDirectoryManifestsSize, s.gitRepoPaths, WithCMPTarDoneChannel(ch.tarDoneCh), WithCMPTarExcludedGlobs(s.initConstants.CMPTarExcludedGlobs), WithCMPUseManifestGeneratePaths(s.initConstants.CMPUseManifestGeneratePaths)) + manifestGenResult, err = GenerateManifests(ctx, opContext.appPath, repoRoot, commitSHA, q, false, s.gitCredsStore, s.initConstants.MaxCombinedDirectoryManifestsSize, s.gitRepoPaths, WithCMPTarDoneChannel(ch.tarDoneCh), WithCMPTarExcludedGlobs(s.initConstants.CMPTarExcludedGlobs), WithCMPUseManifestGeneratePaths(s.initConstants.CMPUseManifestGeneratePaths), WithArgoCDInstanceID(s.initConstants.ArgoCDInstanceID)) } refSourceCommitSHAs := make(map[string]string) if len(repoRefs) > 0 { @@ -1383,6 +1384,7 @@ type ( cmpTarDoneCh chan<- bool cmpTarExcludedGlobs []string cmpUseManifestGeneratePaths bool + argocdInstanceID string } ) @@ -1419,6 +1421,13 @@ func WithCMPUseManifestGeneratePaths(enabled bool) GenerateManifestOpt { } } +// WithArgoCDInstanceID sets the argocd instance server url which manages this resource. +func WithArgoCDInstanceID(argocdInstanceID string) GenerateManifestOpt { + return func(o *generateManifestOpt) { + o.argocdInstanceID = argocdInstanceID + } +} + // GenerateManifests generates manifests from a path. Overrides are applied as a side effect on the given ApplicationSource. func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string, q *apiclient.ManifestRequest, isLocal bool, gitCredsStore git.CredsStore, maxCombinedManifestQuantity resource.Quantity, gitRepoPaths io.TempPaths, opts ...GenerateManifestOpt) (*apiclient.ManifestResponse, error) { opt := newGenerateManifestOpt(opts...) @@ -1507,6 +1516,9 @@ func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string, if err != nil { return nil, fmt.Errorf("failed to set app instance tracking info on manifest: %w", err) } + if err := resourceTracking.SetAppInstanceID(target, opt.argocdInstanceID); err != nil { + log.Warnf("Failed to set Application Instance ID due to missing or invalid ArgoCD URL in ArgoCD Configmap") + } } manifestStr, err := json.Marshal(target.Object) if err != nil { diff --git a/util/argo/resource_tracking.go b/util/argo/resource_tracking.go index bd951ebb29d9c..136c50c67a081 100644 --- a/util/argo/resource_tracking.go +++ b/util/argo/resource_tracking.go @@ -31,7 +31,9 @@ var ( type ResourceTracking interface { GetAppName(un *unstructured.Unstructured, key string, trackingMethod v1alpha1.TrackingMethod, installationID string) string GetAppInstance(un *unstructured.Unstructured, key string, trackingMethod v1alpha1.TrackingMethod, installationID string) *AppInstanceValue + GetAppInstanceID(un *unstructured.Unstructured) string SetAppInstance(un *unstructured.Unstructured, key, val, namespace string, trackingMethod v1alpha1.TrackingMethod, instanceID string) error + SetAppInstanceID(un *unstructured.Unstructured, url string) error BuildAppInstanceValue(value AppInstanceValue) string ParseAppInstanceValue(value string) (*AppInstanceValue, error) Normalize(config, live *unstructured.Unstructured, labelKey, trackingMethod string) error @@ -80,6 +82,14 @@ func (rt *resourceTracking) getAppInstanceValue(un *unstructured.Unstructured, i return value } +func (rt *resourceTracking) getAppInstanceIdValue(un *unstructured.Unstructured) string { + appInstanceIdValue, err := argokube.GetAppInstanceAnnotation(un, common.AnnotationKeyAppInstanceID) + if err != nil { + return "" + } + return appInstanceIdValue +} + // GetAppName retrieve application name base on tracking method func (rt *resourceTracking) GetAppName(un *unstructured.Unstructured, key string, trackingMethod v1alpha1.TrackingMethod, instanceID string) string { retrieveAppInstanceValue := func() string { @@ -121,6 +131,11 @@ func (rt *resourceTracking) GetAppInstance(un *unstructured.Unstructured, key st } } +// GetAppInstanceID retrieve instance ID of the resource. +func (rt *resourceTracking) GetAppInstanceID(un *unstructured.Unstructured) string { + return rt.getAppInstanceIdValue(un) +} + // UnstructuredToAppInstanceValue will build the AppInstanceValue based // on the provided unstructured. The given namespace works as a default // value if the resource's namespace is not defined. It should be the @@ -140,6 +155,18 @@ func UnstructuredToAppInstanceValue(un *unstructured.Unstructured, appName, name } } +// SetAppInstanceID sets the app instance ID annotation if the URL is not empty. +func (rt *resourceTracking) SetAppInstanceID(un *unstructured.Unstructured, url string) error { + if url == "" { + return fmt.Errorf("ArgoCD URL is missing") + } + err := argokube.SetAppInstanceAnnotation(un, common.AnnotationKeyAppInstanceID, url) + if err != nil { + return fmt.Errorf("failed to set app instance ID annotation: %w", err) + } + return nil +} + // SetAppInstance set label/annotation base on tracking method func (rt *resourceTracking) SetAppInstance(un *unstructured.Unstructured, key, val, namespace string, trackingMethod v1alpha1.TrackingMethod, instanceID string) error { setAppInstanceAnnotation := func() error { diff --git a/util/argo/resource_tracking_test.go b/util/argo/resource_tracking_test.go index 1616c1abce9a6..1715f58ab67ab 100644 --- a/util/argo/resource_tracking_test.go +++ b/util/argo/resource_tracking_test.go @@ -131,6 +131,41 @@ func TestSetAppInstanceAnnotationNotFound(t *testing.T) { assert.Equal(t, "", app) } +func TestSetAppInstanceIdAnnotation(t *testing.T) { + yamlBytes, err := os.ReadFile("testdata/svc.yaml") + require.NoError(t, err) + + var obj unstructured.Unstructured + err = yaml.Unmarshal(yamlBytes, &obj) + require.NoError(t, err) + + resourceTracking := NewResourceTracking() + + err = resourceTracking.SetAppInstanceID(&obj, "argocd.com") + require.NoError(t, err) + + value := resourceTracking.GetAppInstanceID(&obj) + assert.Equal(t, "argocd.com", value) + + err = resourceTracking.SetAppInstanceID(&obj, "") + require.Error(t, err) + assert.Contains(t, err.Error(), "ArgoCD URL is missing") +} + +func TestSetAppInstanceIdAnnotationNotFound(t *testing.T) { + yamlBytes, err := os.ReadFile("testdata/svc.yaml") + require.NoError(t, err) + + var obj unstructured.Unstructured + err = yaml.Unmarshal(yamlBytes, &obj) + require.NoError(t, err) + + resourceTracking := NewResourceTracking() + + value := resourceTracking.GetAppInstanceID(&obj) + assert.Equal(t, "", value) +} + func TestParseAppInstanceValue(t *testing.T) { resourceTracking := NewResourceTracking() appInstanceValue, err := resourceTracking.ParseAppInstanceValue("app:/:/")