Skip to content

Commit e2df731

Browse files
Merge pull request from GHSA-9m6p-x4h2-6frq
* feat: limit jq.Run with timeout Signed-off-by: pashakostohrys <[email protected]> * chore: fix import order Signed-off-by: pashakostohrys <[email protected]> * feat: customize error message and add doc section Signed-off-by: pashakostohrys <[email protected]> * feat: improve log and change a way how to get variable Signed-off-by: pashakostohrys <[email protected]> * chore: fix import`s order Signed-off-by: pashakostohrys <[email protected]> * chore: rename variable inside sts Signed-off-by: pashakostohrys <[email protected]> * chore: fix import order Signed-off-by: pashakostohrys <[email protected]> * chore: run lint and resolve conflicts Signed-off-by: pashakostohrys <[email protected]> --------- Signed-off-by: pashakostohrys <[email protected]>
1 parent 9e4a0f5 commit e2df731

File tree

23 files changed

+186
-70
lines changed

23 files changed

+186
-70
lines changed

applicationset/controllers/applicationset_controller.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import (
5050
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
5151
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
5252
argoutil "github.com/argoproj/argo-cd/v2/util/argo"
53+
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
5354

5455
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
5556
)
@@ -623,7 +624,7 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
623624
},
624625
}
625626

626-
action, err := utils.CreateOrUpdate(ctx, appLog, r.Client, applicationSet.Spec.IgnoreApplicationDifferences, found, func() error {
627+
action, err := utils.CreateOrUpdate(ctx, appLog, r.Client, applicationSet.Spec.IgnoreApplicationDifferences, normalizers.IgnoreNormalizerOpts{}, found, func() error {
627628
// Copy only the Application/ObjectMeta fields that are significant, from the generatedApp
628629
found.Spec = generatedApp.Spec
629630

applicationset/utils/createOrUpdate.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
2121
"github.com/argoproj/argo-cd/v2/util/argo"
2222
argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff"
23+
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
2324
)
2425

2526
// CreateOrUpdate overrides "sigs.k8s.io/controller-runtime" function
@@ -35,7 +36,7 @@ import (
3536
// The MutateFn is called regardless of creating or updating an object.
3637
//
3738
// It returns the executed operation and an error.
38-
func CreateOrUpdate(ctx context.Context, logCtx *log.Entry, c client.Client, ignoreAppDifferences argov1alpha1.ApplicationSetIgnoreDifferences, obj *argov1alpha1.Application, f controllerutil.MutateFn) (controllerutil.OperationResult, error) {
39+
func CreateOrUpdate(ctx context.Context, logCtx *log.Entry, c client.Client, ignoreAppDifferences argov1alpha1.ApplicationSetIgnoreDifferences, ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts, obj *argov1alpha1.Application, f controllerutil.MutateFn) (controllerutil.OperationResult, error) {
3940

4041
key := client.ObjectKeyFromObject(obj)
4142
if err := c.Get(ctx, key, obj); err != nil {
@@ -60,7 +61,7 @@ func CreateOrUpdate(ctx context.Context, logCtx *log.Entry, c client.Client, ign
6061

6162
// Apply ignoreApplicationDifferences rules to remove ignored fields from both the live and the desired state. This
6263
// prevents those differences from appearing in the diff and therefore in the patch.
63-
err := applyIgnoreDifferences(ignoreAppDifferences, normalizedLive, obj)
64+
err := applyIgnoreDifferences(ignoreAppDifferences, normalizedLive, obj, ignoreNormalizerOpts)
6465
if err != nil {
6566
return controllerutil.OperationResultNone, fmt.Errorf("failed to apply ignore differences: %w", err)
6667
}
@@ -134,14 +135,14 @@ func mutate(f controllerutil.MutateFn, key client.ObjectKey, obj client.Object)
134135
}
135136

136137
// applyIgnoreDifferences applies the ignore differences rules to the found application. It modifies the applications in place.
137-
func applyIgnoreDifferences(applicationSetIgnoreDifferences argov1alpha1.ApplicationSetIgnoreDifferences, found *argov1alpha1.Application, generatedApp *argov1alpha1.Application) error {
138+
func applyIgnoreDifferences(applicationSetIgnoreDifferences argov1alpha1.ApplicationSetIgnoreDifferences, found *argov1alpha1.Application, generatedApp *argov1alpha1.Application, ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts) error {
138139
if len(applicationSetIgnoreDifferences) == 0 {
139140
return nil
140141
}
141142

142143
generatedAppCopy := generatedApp.DeepCopy()
143144
diffConfig, err := argodiff.NewDiffConfigBuilder().
144-
WithDiffSettings(applicationSetIgnoreDifferences.ToApplicationIgnoreDifferences(), nil, false).
145+
WithDiffSettings(applicationSetIgnoreDifferences.ToApplicationIgnoreDifferences(), nil, false, ignoreNormalizerOpts).
145146
WithNoCache().
146147
Build()
147148
if err != nil {

applicationset/utils/createOrUpdate_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1010

1111
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
12+
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
1213
)
1314

1415
func Test_applyIgnoreDifferences(t *testing.T) {
@@ -222,7 +223,7 @@ spec:
222223
generatedApp := v1alpha1.Application{TypeMeta: appMeta}
223224
err = yaml.Unmarshal([]byte(tc.generatedApp), &generatedApp)
224225
require.NoError(t, err, tc.generatedApp)
225-
err = applyIgnoreDifferences(tc.ignoreDifferences, &foundApp, &generatedApp)
226+
err = applyIgnoreDifferences(tc.ignoreDifferences, &foundApp, &generatedApp, normalizers.IgnoreNormalizerOpts{})
226227
require.NoError(t, err)
227228
yamlFound, err := yaml.Marshal(tc.foundApp)
228229
require.NoError(t, err)

cmd/argocd-application-controller/commands/argocd_application_controller.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
2121
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
2222
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
23+
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
2324
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
2425
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
2526
"github.com/argoproj/argo-cd/v2/util/cli"
@@ -68,6 +69,7 @@ func NewCommand() *cobra.Command {
6869
persistResourceHealth bool
6970
shardingAlgorithm string
7071
enableDynamicClusterDistribution bool
72+
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
7173
)
7274
var command = cobra.Command{
7375
Use: cliName,
@@ -160,6 +162,7 @@ func NewCommand() *cobra.Command {
160162
persistResourceHealth,
161163
clusterFilter,
162164
applicationNamespaces,
165+
ignoreNormalizerOpts,
163166
)
164167
errors.CheckError(err)
165168
cacheutil.CollectMetrics(redisClient, appController.GetMetricsServer())
@@ -206,6 +209,7 @@ func NewCommand() *cobra.Command {
206209
command.Flags().BoolVar(&persistResourceHealth, "persist-resource-health", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_PERSIST_RESOURCE_HEALTH", true), "Enables storing the managed resources health in the Application CRD")
207210
command.Flags().StringVar(&shardingAlgorithm, "sharding-method", env.StringFromEnv(common.EnvControllerShardingAlgorithm, common.DefaultShardingAlgorithm), "Enables choice of sharding method. Supported sharding methods are : [legacy, round-robin] ")
208211
command.Flags().BoolVar(&enableDynamicClusterDistribution, "dynamic-cluster-distribution-enabled", env.ParseBoolFromEnv(common.EnvEnableDynamicClusterDistribution, false), "Enables dynamic cluster distribution.")
212+
command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "", env.ParseDurationFromEnv("ARGOCD_IGNORE_NORMALIZER_JQ_TIMEOUT", 0*time.Second, 0, math.MaxInt64), "Set ignore normalizer JQ execution timeout")
209213
cacheSource = appstatecache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
210214
redisClient = client
211215
})

cmd/argocd/commands/admin/app.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
appinformers "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions"
3131
reposerverclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
3232
"github.com/argoproj/argo-cd/v2/util/argo"
33+
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
3334
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
3435
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
3536
"github.com/argoproj/argo-cd/v2/util/cli"
@@ -228,11 +229,12 @@ func diffReconcileResults(res1 reconcileResults, res2 reconcileResults) error {
228229

229230
func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
230231
var (
231-
clientConfig clientcmd.ClientConfig
232-
selector string
233-
repoServerAddress string
234-
outputFormat string
235-
refresh bool
232+
clientConfig clientcmd.ClientConfig
233+
selector string
234+
repoServerAddress string
235+
outputFormat string
236+
refresh bool
237+
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
236238
)
237239

238240
var command = &cobra.Command{
@@ -270,7 +272,7 @@ func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
270272

271273
appClientset := appclientset.NewForConfigOrDie(cfg)
272274
kubeClientset := kubernetes.NewForConfigOrDie(cfg)
273-
result, err = reconcileApplications(ctx, kubeClientset, appClientset, namespace, repoServerClient, selector, newLiveStateCache)
275+
result, err = reconcileApplications(ctx, kubeClientset, appClientset, namespace, repoServerClient, selector, newLiveStateCache, ignoreNormalizerOpts)
274276
errors.CheckError(err)
275277
} else {
276278
appClientset := appclientset.NewForConfigOrDie(cfg)
@@ -285,6 +287,7 @@ func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
285287
command.Flags().StringVar(&selector, "l", "", "Label selector")
286288
command.Flags().StringVar(&outputFormat, "o", "yaml", "Output format (yaml|json)")
287289
command.Flags().BoolVar(&refresh, "refresh", false, "If set to true then recalculates apps reconciliation")
290+
command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout", normalizers.DefaultJQExecutionTimeout, "Set ignore normalizer JQ execution timeout")
288291

289292
return command
290293
}
@@ -334,6 +337,7 @@ func reconcileApplications(
334337
repoServerClient reposerverclient.Clientset,
335338
selector string,
336339
createLiveStateCache func(argoDB db.ArgoDB, appInformer kubecache.SharedIndexInformer, settingsMgr *settings.SettingsManager, server *metrics.MetricsServer) cache.LiveStateCache,
340+
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts,
337341
) ([]appReconcileResult, error) {
338342
settingsMgr := settings.NewSettingsManager(ctx, kubeClientset, namespace)
339343
argoDB := db.NewDB(namespace, settingsMgr, kubeClientset)
@@ -374,7 +378,7 @@ func reconcileApplications(
374378
)
375379

376380
appStateManager := controller.NewAppStateManager(
377-
argoDB, appClientset, repoServerClient, namespace, kubeutil.NewKubectl(), settingsMgr, stateCache, projInformer, server, cache, time.Second, argo.NewResourceTracking(), false)
381+
argoDB, appClientset, repoServerClient, namespace, kubeutil.NewKubectl(), settingsMgr, stateCache, projInformer, server, cache, time.Second, argo.NewResourceTracking(), false, ignoreNormalizerOpts)
378382

379383
appsList, err := appClientset.ArgoprojV1alpha1().Applications(namespace).List(ctx, v1.ListOptions{LabelSelector: selector})
380384
if err != nil {

cmd/argocd/commands/admin/app_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
argocdclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
2424
"github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks"
2525
"github.com/argoproj/argo-cd/v2/test"
26+
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
2627
"github.com/argoproj/argo-cd/v2/util/db"
2728
"github.com/argoproj/argo-cd/v2/util/settings"
2829
)
@@ -113,6 +114,7 @@ func TestGetReconcileResults_Refresh(t *testing.T) {
113114
func(argoDB db.ArgoDB, appInformer cache.SharedIndexInformer, settingsMgr *settings.SettingsManager, server *metrics.MetricsServer) statecache.LiveStateCache {
114115
return &liveStateCache
115116
},
117+
normalizers.IgnoreNormalizerOpts{},
116118
)
117119

118120
if !assert.NoError(t, err) {

cmd/argocd/commands/admin/settings.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ argocd admin settings resource-overrides ignore-differences ./deploy.yaml --argo
432432
// configurations. This requires access to live resources which is not the
433433
// purpose of this command. This will just apply jsonPointers and
434434
// jqPathExpressions configurations.
435-
normalizer, err := normalizers.NewIgnoreNormalizer(nil, overrides)
435+
normalizer, err := normalizers.NewIgnoreNormalizer(nil, overrides, normalizers.IgnoreNormalizerOpts{})
436436
errors.CheckError(err)
437437

438438
normalizedRes := res.DeepCopy()
@@ -457,6 +457,9 @@ argocd admin settings resource-overrides ignore-differences ./deploy.yaml --argo
457457
}
458458

459459
func NewResourceIgnoreResourceUpdatesCommand(cmdCtx commandContext) *cobra.Command {
460+
var (
461+
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
462+
)
460463
var command = &cobra.Command{
461464
Use: "ignore-resource-updates RESOURCE_YAML_PATH",
462465
Short: "Renders fields excluded from resource updates",
@@ -478,7 +481,7 @@ argocd admin settings resource-overrides ignore-resource-updates ./deploy.yaml -
478481
return
479482
}
480483

481-
normalizer, err := normalizers.NewIgnoreNormalizer(nil, overrides)
484+
normalizer, err := normalizers.NewIgnoreNormalizer(nil, overrides, ignoreNormalizerOpts)
482485
errors.CheckError(err)
483486

484487
normalizedRes := res.DeepCopy()
@@ -499,6 +502,7 @@ argocd admin settings resource-overrides ignore-resource-updates ./deploy.yaml -
499502
})
500503
},
501504
}
505+
command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout", normalizers.DefaultJQExecutionTimeout, "Set ignore normalizer JQ execution timeout")
502506
return command
503507
}
504508

cmd/argocd/commands/app.go

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import (
4444
"github.com/argoproj/argo-cd/v2/reposerver/repository"
4545
"github.com/argoproj/argo-cd/v2/util/argo"
4646
argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff"
47+
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
4748
"github.com/argoproj/argo-cd/v2/util/cli"
4849
"github.com/argoproj/argo-cd/v2/util/errors"
4950
"github.com/argoproj/argo-cd/v2/util/git"
@@ -964,14 +965,15 @@ type objKeyLiveTarget struct {
964965
// NewApplicationDiffCommand returns a new instance of an `argocd app diff` command
965966
func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
966967
var (
967-
refresh bool
968-
hardRefresh bool
969-
exitCode bool
970-
local string
971-
revision string
972-
localRepoRoot string
973-
serverSideGenerate bool
974-
localIncludes []string
968+
refresh bool
969+
hardRefresh bool
970+
exitCode bool
971+
local string
972+
revision string
973+
localRepoRoot string
974+
serverSideGenerate bool
975+
localIncludes []string
976+
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
975977
)
976978
shortDesc := "Perform a diff against the target and live state."
977979
var command = &cobra.Command{
@@ -1038,7 +1040,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
10381040
}
10391041
}
10401042
proj := getProject(c, clientOpts, ctx, app.Spec.Project)
1041-
foundDiffs := findandPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption)
1043+
foundDiffs := findandPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption, ignoreNormalizerOpts)
10421044
if foundDiffs && exitCode {
10431045
os.Exit(1)
10441046
}
@@ -1052,6 +1054,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
10521054
command.Flags().StringVar(&localRepoRoot, "local-repo-root", "/", "Path to the repository root. Used together with --local allows setting the repository root")
10531055
command.Flags().BoolVar(&serverSideGenerate, "server-side-generate", false, "Used with --local, this will send your manifests to the server for diffing")
10541056
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.")
1057+
command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout", normalizers.DefaultJQExecutionTimeout, "Set ignore normalizer JQ execution timeout")
10551058
return command
10561059
}
10571060

@@ -1066,7 +1069,7 @@ type DifferenceOption struct {
10661069
}
10671070

10681071
// 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
1069-
func findandPrintDiff(ctx context.Context, app *argoappv1.Application, proj *argoappv1.AppProject, resources *application.ManagedResourcesResponse, argoSettings *settings.Settings, diffOptions *DifferenceOption) bool {
1072+
func findandPrintDiff(ctx context.Context, app *argoappv1.Application, proj *argoappv1.AppProject, resources *application.ManagedResourcesResponse, argoSettings *settings.Settings, diffOptions *DifferenceOption, ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts) bool {
10701073
var foundDiffs bool
10711074
liveObjs, err := cmdutil.LiveObjects(resources.Items)
10721075
errors.CheckError(err)
@@ -1121,7 +1124,7 @@ func findandPrintDiff(ctx context.Context, app *argoappv1.Application, proj *arg
11211124
// compareOptions in the protobuf
11221125
ignoreAggregatedRoles := false
11231126
diffConfig, err := argodiff.NewDiffConfigBuilder().
1124-
WithDiffSettings(app.Spec.IgnoreDifferences, overrides, ignoreAggregatedRoles).
1127+
WithDiffSettings(app.Spec.IgnoreDifferences, overrides, ignoreAggregatedRoles, ignoreNormalizerOpts).
11251128
WithTracking(argoSettings.AppLabelKey, argoSettings.TrackingMethod).
11261129
WithNoCache().
11271130
Build()
@@ -1614,6 +1617,8 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
16141617
diffChangesConfirm bool
16151618
projects []string
16161619
output string
1620+
appNamespace string
1621+
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
16171622
)
16181623
var command = &cobra.Command{
16191624
Use: "sync [APPNAME... | -l selector | --project project-name]",
@@ -1838,7 +1843,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
18381843
fmt.Printf("====== Previewing differences between live and desired state of application %s ======\n", appQualifiedName)
18391844

18401845
proj := getProject(c, clientOpts, ctx, app.Spec.Project)
1841-
foundDiffs = findandPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption)
1846+
foundDiffs = findandPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption, ignoreNormalizerOpts)
18421847
if foundDiffs {
18431848
if !diffChangesConfirm {
18441849
yesno := cli.AskToProceed(fmt.Sprintf("Please review changes to application %s shown above. Do you want to continue the sync process? (y/n): ", appQualifiedName))
@@ -1896,6 +1901,8 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
18961901
command.Flags().BoolVar(&diffChanges, "preview-changes", false, "Preview difference against the target and live state before syncing app and wait for user confirmation")
18971902
command.Flags().StringArrayVar(&projects, "project", []string{}, "Sync apps that belong to the specified projects. This option may be specified repeatedly.")
18981903
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|tree|tree=detailed")
1904+
command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Only sync an application in namespace")
1905+
command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout", normalizers.DefaultJQExecutionTimeout, "Set ignore normalizer JQ execution timeout")
18991906
return command
19001907
}
19011908

0 commit comments

Comments
 (0)