Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added support to expose ArgoCD instance URL #20327

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
3 changes: 3 additions & 0 deletions cmd/argocd-repo-server/commands/argocd_repo_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func NewCommand() *cobra.Command {
disableManifestMaxExtractedSize bool
includeHiddenDirectories bool
cmpUseManifestGeneratePaths bool
argocdInstanceID string
)
command := cobra.Command{
Use: cliName,
Expand Down Expand Up @@ -138,6 +139,7 @@ func NewCommand() *cobra.Command {
HelmRegistryMaxIndexSize: helmRegistryMaxIndexSizeQuantity.ToDec().Value(),
IncludeHiddenDirectories: includeHiddenDirectories,
CMPUseManifestGeneratePaths: cmpUseManifestGeneratePaths,
ArgoCDInstanceID: argocdInstanceID,
}, askPassServer)
errors.CheckError(err)

Expand Down Expand Up @@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions cmd/argocd/commands/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -1382,6 +1382,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)
}
Expand Down
3 changes: 3 additions & 0 deletions common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,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"

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions manifests/core-install.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions manifests/ha/install.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions manifests/ha/namespace-install.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions manifests/install.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions manifests/namespace-install.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 13 additions & 1 deletion reposerver/repository/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ type RepoServerInitConstants struct {
DisableHelmManifestMaxExtractedSize bool
IncludeHiddenDirectories bool
CMPUseManifestGeneratePaths bool
ArgoCDInstanceID string
}

// NewService returns a new instance of the Manifest service
Expand Down Expand Up @@ -806,7 +807,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 {
Expand Down Expand Up @@ -1381,6 +1382,7 @@ type (
cmpTarDoneCh chan<- bool
cmpTarExcludedGlobs []string
cmpUseManifestGeneratePaths bool
argocdInstanceID string
}
)

Expand Down Expand Up @@ -1417,6 +1419,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...)
Expand Down Expand Up @@ -1505,6 +1514,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 {
Expand Down
27 changes: 27 additions & 0 deletions util/argo/resource_tracking.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand Down
35 changes: 35 additions & 0 deletions util/argo/resource_tracking_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:<group>/<kind>:<namespace>/<name>")
Expand Down