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(cli): add support for multiple sources to app diff|manifests command with revisions flag #17650

Merged
merged 9 commits into from
Apr 3, 2024
90 changes: 82 additions & 8 deletions cmd/argocd/commands/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,8 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
serverSideGenerate bool
localIncludes []string
appNamespace string
revisions []string
sourceIndexes []int64
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be int64 ? I feel we can use uint or unit32 here as the index positions do not have negative numbers.

Copy link
Member Author

@ishitasequeira ishitasequeira Apr 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The command flagset does not support uint or uint32. Thus, had to keep it int or int64. Had used int64 to keep it consistent with what is needed by revisionSourceMappings. I can change this to int and then typecast it to int64 for creating revisionSourceMappings.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If flagset support is missing for uint, then let's stick to int64

)
shortDesc := "Perform a diff against the target and live state."
var command = &cobra.Command{
Expand All @@ -1138,6 +1140,11 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
c.HelpFunc()(c, args)
os.Exit(2)
}

if len(revisions) != len(sourceIndexes) {
errors.CheckError(fmt.Errorf("While using revisions and source-indexes, length of values for both flags should be same."))
}

clientset := headless.NewClientOrDie(clientOpts, c)
conn, appIf := clientset.NewApplicationClientOrDie()
defer argoio.Close(conn)
Expand All @@ -1156,7 +1163,27 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
argoSettings, err := settingsIf.Get(ctx, &settings.SettingsQuery{})
errors.CheckError(err)
diffOption := &DifferenceOption{}
if revision != "" {
if app.Spec.HasMultipleSources() && len(revisions) > 0 && len(sourceIndexes) > 0 {

revisionSourceMappings := make(map[int64]string, 0)
for i, index := range sourceIndexes {
if index <= 0 {
errors.CheckError(fmt.Errorf("source-index cannot be less than or equal to 0. Index starts at 1."))
crenshaw-dev marked this conversation as resolved.
Show resolved Hide resolved
}
revisionSourceMappings[index] = revisions[i]
}

q := application.ApplicationManifestQuery{
Name: &appName,
AppNamespace: &appNs,
RevisionSourceMappings: revisionSourceMappings,
}
res, err := appIf.GetManifests(ctx, &q)
errors.CheckError(err)

diffOption.res = res
diffOption.revisionSourceMappings = &revisionSourceMappings
} else if revision != "" {
q := application.ApplicationManifestQuery{
Name: &appName,
Revision: &revision,
Expand Down Expand Up @@ -1206,17 +1233,20 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
command.Flags().BoolVar(&serverSideGenerate, "server-side-generate", false, "Used with --local, this will send your manifests to the server for diffing")
command.Flags().StringArrayVar(&localIncludes, "local-include", []string{"*.yaml", "*.yml", "*.json"}, "Used with --server-side-generate, specify patterns of filenames to send. Matching is based on filename and not path.")
command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Only render the difference in namespace")
command.Flags().StringArrayVar(&revisions, "revisions", []string{}, "Show manifests at specific revisions for the index of sources in source-indexes")
command.Flags().Int64SliceVar(&sourceIndexes, "source-indexes", []int64{}, "List of source indexes. Default is empty array. Indexes start at 1.")
return command
}

// DifferenceOption struct to store diff options
type DifferenceOption struct {
local string
localRepoRoot string
revision string
cluster *argoappv1.Cluster
res *repoapiclient.ManifestResponse
serversideRes *repoapiclient.ManifestResponse
local string
localRepoRoot string
revision string
cluster *argoappv1.Cluster
res *repoapiclient.ManifestResponse
serversideRes *repoapiclient.ManifestResponse
revisionSourceMappings *map[int64]string
}

// findandPrintDiff ... Prints difference between application current state and state stored in git or locally, returns boolean as true if difference is found else returns false
Expand All @@ -1228,7 +1258,7 @@ func findandPrintDiff(ctx context.Context, app *argoappv1.Application, proj *arg
if diffOptions.local != "" {
localObjs := groupObjsByKey(getLocalObjects(ctx, app, proj, diffOptions.local, diffOptions.localRepoRoot, argoSettings.AppLabelKey, diffOptions.cluster.Info.ServerVersion, diffOptions.cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.TrackingMethod), liveObjs, app.Spec.Destination.Namespace)
items = groupObjsForDiff(resources, localObjs, items, argoSettings, app.InstanceName(argoSettings.ControllerNamespace), app.Spec.Destination.Namespace)
} else if diffOptions.revision != "" {
} else if diffOptions.revision != "" || (diffOptions.revisionSourceMappings != nil) {
var unstructureds []*unstructured.Unstructured
for _, mfst := range diffOptions.res.Manifests {
obj, err := argoappv1.UnmarshalToUnstructured(mfst)
Expand Down Expand Up @@ -2708,23 +2738,41 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob
var (
source string
revision string
revisions []string
sourceIndexes []int64
local string
localRepoRoot string
)
var command = &cobra.Command{
Use: "manifests APPNAME",
Short: "Print manifests of an application",
Example: templates.Examples(`
# Get manifests for an application
argocd app manifests my-app

# Get manifests for an application at a specific revision
argocd app manifests my-app --revision 0.0.1

# Get manifests for a multi-source application at specific revisions for specific sources
argocd app manifests my-app --revisions 0.0.1 --source-indexes 1 --revisions 0.0.2 --source-indexes 2
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()

if len(args) != 1 {
c.HelpFunc()(c, args)
os.Exit(1)
}

if len(revisions) != len(sourceIndexes) {
errors.CheckError(fmt.Errorf("While using revisions and source-indexes, length of values for both flags should be same."))
}

appName, appNs := argo.ParseFromQualifiedName(args[0], "")
clientset := headless.NewClientOrDie(clientOpts, c)
conn, appIf := clientset.NewApplicationClientOrDie()
defer argoio.Close(conn)

resources, err := appIf.ManagedResources(ctx, &application.ResourcesQuery{
ApplicationName: &appName,
AppNamespace: &appNs,
Expand All @@ -2750,6 +2798,30 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob

proj := getProject(c, clientOpts, ctx, app.Spec.Project)
unstructureds = getLocalObjects(context.Background(), app, proj.Project, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.TrackingMethod)
} else if len(revisions) > 0 && len(sourceIndexes) > 0 {

revisionSourceMappings := make(map[int64]string, 0)
for i, index := range sourceIndexes {
if index <= 0 {
errors.CheckError(fmt.Errorf("source-index cannot be less than or equal to 0, Index starts at 1"))
}
revisionSourceMappings[index] = revisions[i]
}

q := application.ApplicationManifestQuery{
Name: &appName,
AppNamespace: &appNs,
Revision: pointer.String(revision),
RevisionSourceMappings: revisionSourceMappings,
}
res, err := appIf.GetManifests(ctx, &q)
errors.CheckError(err)

for _, mfst := range res.Manifests {
obj, err := argoappv1.UnmarshalToUnstructured(mfst)
errors.CheckError(err)
unstructureds = append(unstructureds, obj)
}
} else if revision != "" {
q := application.ApplicationManifestQuery{
Name: &appName,
Expand Down Expand Up @@ -2787,6 +2859,8 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob
}
command.Flags().StringVar(&source, "source", "git", "Source of manifests. One of: live|git")
command.Flags().StringVar(&revision, "revision", "", "Show manifests at a specific revision")
command.Flags().StringArrayVar(&revisions, "revisions", []string{}, "Show manifests at specific revisions for the index of sources in source-indexes")
command.Flags().Int64SliceVar(&sourceIndexes, "source-indexes", []int64{}, "List of source indexes. Default is empty array. Indexes start at 1.")
command.Flags().StringVar(&local, "local", "", "If set, show locally-generated manifests. Value is the absolute path to app manifests within the manifest repo. Example: '/home/username/apps/env/app-1'.")
command.Flags().StringVar(&localRepoRoot, "local-repo-root", ".", "Path to the local repository root. Used together with --local allows setting the repository root. Example: '/home/username/apps'.")
return command
Expand Down
2 changes: 2 additions & 0 deletions docs/user-guide/commands/argocd_app_diff.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ argocd app diff APPNAME [flags]
--local-repo-root string Path to the repository root. Used together with --local allows setting the repository root (default "/")
--refresh Refresh application data when retrieving
--revision string Compare live app to a particular revision
--revisions stringArray Show manifests at specific revisions for the index of sources in source-indexes
--server-side-generate Used with --local, this will send your manifests to the server for diffing
--source-indexes int64Slice List of source indexes. Default is empty array. Indexes start at 1. (default [])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

intArray type would make it clearer to users and not leak go implementation details

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, this is an auto-generated document. I am using int64Slice datatype for sourceIndexes as we do not have a function to parse int[] input in flagSet.

```

### Options inherited from parent commands
Expand Down
25 changes: 20 additions & 5 deletions docs/user-guide/commands/argocd_app_manifests.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,29 @@ Print manifests of an application
argocd app manifests APPNAME [flags]
```

### Examples

```
# Get manifests for an application
argocd app manifests my-app

# Get manifests for an application at a specific revision
argocd app manifests my-app --revision 0.0.1

# Get manifests for a multi-source application at specific revisions for specific sources
argocd app manifests my-app --revisions 0.0.1 --source-indexes 1 --revisions 0.0.2 --source-indexes 2
```

### Options

```
-h, --help help for manifests
--local string If set, show locally-generated manifests. Value is the absolute path to app manifests within the manifest repo. Example: '/home/username/apps/env/app-1'.
--local-repo-root string Path to the local repository root. Used together with --local allows setting the repository root. Example: '/home/username/apps'. (default ".")
--revision string Show manifests at a specific revision
--source string Source of manifests. One of: live|git (default "git")
-h, --help help for manifests
--local string If set, show locally-generated manifests. Value is the absolute path to app manifests within the manifest repo. Example: '/home/username/apps/env/app-1'.
--local-repo-root string Path to the local repository root. Used together with --local allows setting the repository root. Example: '/home/username/apps'. (default ".")
--revision string Show manifests at a specific revision
--revisions stringArray Show manifests at specific revisions for the index of sources in source-indexes
--source string Source of manifests. One of: live|git (default "git")
--source-indexes int64Slice List of source indexes. Default is empty array. Indexes start at 1. (default [])
```

### Options inherited from parent commands
Expand Down
Loading
Loading