Compare commits

..

1 Commits

Author SHA1 Message Date
Alexandre Gaudreault
0e4115e6fd test 2024-08-12 12:16:38 -04:00
452 changed files with 30002 additions and 8229 deletions

View File

@@ -172,7 +172,7 @@ jobs:
- name: Run all unit tests
run: make test-local
- name: Generate test results artifacts
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
with:
name: test-results
path: test-results
@@ -236,7 +236,7 @@ jobs:
- name: Run all unit tests
run: make test-race-local
- name: Generate test results artifacts
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
with:
name: race-results
path: test-results/
@@ -323,8 +323,6 @@ jobs:
NODE_ENV: production
NODE_ONLINE_ENV: online
HOST_ARCH: amd64
# If we're on the master branch, set the codecov token so that we upload bundle analysis
CODECOV_TOKEN: ${{ github.ref == 'refs/heads/master' && secrets.CODECOV_TOKEN || '' }}
working-directory: ui/
- name: Run ESLint
run: yarn lint
@@ -379,13 +377,6 @@ jobs:
fail_ci_if_error: true
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- name: Upload test results to Codecov
if: github.ref == 'refs/heads/master' && github.event_name == 'push' && github.repository == 'argoproj/argo-cd'
uses: codecov/test-results-action@1b5b448b98e58ba90d1a1a1d9fcb72ca2263be46 # v1.0.0
with:
file: test-results/junit.xml
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
- name: Perform static code analysis using SonarCloud
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -506,13 +497,13 @@ jobs:
goreman run stop-all || echo "goreman trouble"
sleep 30
- name: Upload e2e coverage report
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
with:
name: e2e-code-coverage
path: /tmp/coverage
if: ${{ matrix.k3s.latest }}
- name: Upload e2e-server logs
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
with:
name: e2e-server-k8s${{ matrix.k3s.version }}.log
path: /tmp/e2e-server.log

View File

@@ -143,7 +143,7 @@ jobs:
- name: Build and push container image
id: image
uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 #v6.7.0
uses: docker/build-push-action@16ebe778df0e7752d2cfcbd924afdbbd89c1a755 #v6.6.1
with:
context: .
platforms: ${{ inputs.platforms }}

View File

@@ -64,7 +64,7 @@ jobs:
git stash pop
- name: Create pull request
uses: peter-evans/create-pull-request@4320041ed380b20e97d388d56a7fb4f9b8c20e79 # v7.0.0
uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6.1.0
with:
commit-message: "Bump version to ${{ inputs.TARGET_VERSION }}"
title: "Bump version to ${{ inputs.TARGET_VERSION }} on ${{ inputs.TARGET_BRANCH }} branch"

View File

@@ -295,7 +295,7 @@ jobs:
if: ${{ env.UPDATE_VERSION == 'true' }}
- name: Create PR to update VERSION on master branch
uses: peter-evans/create-pull-request@4320041ed380b20e97d388d56a7fb4f9b8c20e79 # v7.0.0
uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6.1.0
with:
commit-message: Bump version in master
title: "chore: Bump version in master"

View File

@@ -54,7 +54,7 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
with:
name: SARIF file
path: results.sarif

View File

@@ -1,12 +1,9 @@
issues:
exclude:
- SA1019
- SA5011
max-issues-per-linter: 0
max-same-issues: 0
exclude-rules:
- path: '(.+)_test\.go'
linters:
- unparam
linters:
enable:
- errcheck
@@ -20,7 +17,6 @@ linters:
- misspell
- staticcheck
- testifylint
- unparam
- unused
- whitespace
linters-settings:

View File

@@ -83,7 +83,7 @@ WORKDIR /home/argocd
####################################################################################################
# Argo CD UI stage
####################################################################################################
FROM --platform=$BUILDPLATFORM docker.io/library/node:22.8.0@sha256:8ec02324cb37718197de92e51677781be9f1345c709f31a1f44440c6036d24a2 AS argocd-ui
FROM --platform=$BUILDPLATFORM docker.io/library/node:22.3.0@sha256:5e4044ff6001d06e7748e35bfa4f80c73cf5f5a7360a1b782995e038a01b0585 AS argocd-ui
WORKDIR /src
COPY ["ui/package.json", "ui/yarn.lock", "./"]

View File

@@ -254,7 +254,7 @@ cli: test-tools-image
.PHONY: cli-local
cli-local: clean-debug
CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -gcflags="all=-N -l" $(COVERAGE_FLAG) -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd
CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build $(COVERAGE_FLAG) -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd
.PHONY: gen-resources-cli-local
gen-resources-cli-local: clean-debug

View File

@@ -1,3 +1,5 @@
Test for DCO. do not merge
**Releases:**
[![Release Version](https://img.shields.io/github/v/release/argoproj/argo-cd?label=argo-cd)](https://github.com/argoproj/argo-cd/releases/latest)
[![Artifact HUB](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/argo-cd)](https://artifacthub.io/packages/helm/argo/argo-cd)

View File

@@ -31,9 +31,11 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes"
k8scache "k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
@@ -43,11 +45,11 @@ import (
"github.com/argoproj/argo-cd/v2/applicationset/controllers/template"
"github.com/argoproj/argo-cd/v2/applicationset/generators"
"github.com/argoproj/argo-cd/v2/applicationset/metrics"
"github.com/argoproj/argo-cd/v2/applicationset/status"
"github.com/argoproj/argo-cd/v2/applicationset/utils"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/util/db"
"github.com/argoproj/argo-cd/v2/util/glob"
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
@@ -88,7 +90,7 @@ type ApplicationSetReconciler struct {
SCMRootCAPath string
GlobalPreservedAnnotations []string
GlobalPreservedLabels []string
Metrics *metrics.ApplicationsetMetrics
Cache cache.Cache
}
// +kubebuilder:rbac:groups=argoproj.io,resources=applicationsets,verbs=get;list;watch;create;update;patch;delete
@@ -99,7 +101,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
var applicationSetInfo argov1alpha1.ApplicationSet
parametersGenerated := false
startTime := time.Now()
if err := r.Get(ctx, req.NamespacedName, &applicationSetInfo); err != nil {
if client.IgnoreNotFound(err) != nil {
logCtx.WithError(err).Infof("unable to get ApplicationSet: '%v' ", err)
@@ -107,10 +109,6 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
return ctrl.Result{}, client.IgnoreNotFound(err)
}
defer func() {
r.Metrics.ObserveReconcile(&applicationSetInfo, time.Since(startTime))
}()
// Do not attempt to further reconcile the ApplicationSet if it is being deleted.
if applicationSetInfo.ObjectMeta.DeletionTimestamp != nil {
appsetName := applicationSetInfo.ObjectMeta.Name
@@ -244,8 +242,20 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
if r.EnableProgressiveSyncs {
// trigger appropriate application syncs if RollingSync strategy is enabled
if progressiveSyncsRollingSyncStrategyEnabled(&applicationSetInfo) {
validApps = r.syncValidApplications(logCtx, &applicationSetInfo, appSyncMap, appMap, validApps)
if progressiveSyncsStrategyEnabled(&applicationSetInfo, "RollingSync") {
validApps, err = r.syncValidApplications(logCtx, &applicationSetInfo, appSyncMap, appMap, validApps)
if err != nil {
_ = r.setApplicationSetStatusCondition(ctx,
&applicationSetInfo,
argov1alpha1.ApplicationSetCondition{
Type: argov1alpha1.ApplicationSetConditionErrorOccurred,
Message: err.Error(),
Reason: argov1alpha1.ApplicationSetReasonSyncApplicationError,
Status: argov1alpha1.ApplicationSetConditionStatusTrue,
}, parametersGenerated,
)
return ctrl.Result{}, err
}
}
}
@@ -399,21 +409,8 @@ func (r *ApplicationSetReconciler) setApplicationSetStatusCondition(ctx context.
paramtersGeneratedCondition := getParametersGeneratedCondition(paramtersGenerated, condition.Message)
resourceUpToDateCondition := getResourceUpToDateCondition(errOccurred, condition.Message, condition.Reason)
evaluatedTypes := map[argov1alpha1.ApplicationSetConditionType]bool{
argov1alpha1.ApplicationSetConditionErrorOccurred: true,
argov1alpha1.ApplicationSetConditionParametersGenerated: true,
argov1alpha1.ApplicationSetConditionResourcesUpToDate: true,
}
newConditions := []argov1alpha1.ApplicationSetCondition{errOccurredCondition, paramtersGeneratedCondition, resourceUpToDateCondition}
if progressiveSyncsRollingSyncStrategyEnabled(applicationSet) {
evaluatedTypes[argov1alpha1.ApplicationSetConditionRolloutProgressing] = true
if condition.Type == argov1alpha1.ApplicationSetConditionRolloutProgressing {
newConditions = append(newConditions, condition)
}
}
needToUpdateConditions := false
for _, condition := range newConditions {
// do nothing if appset already has same condition
@@ -424,8 +421,13 @@ func (r *ApplicationSetReconciler) setApplicationSetStatusCondition(ctx context.
}
}
}
evaluatedTypes := map[argov1alpha1.ApplicationSetConditionType]bool{
argov1alpha1.ApplicationSetConditionErrorOccurred: true,
argov1alpha1.ApplicationSetConditionParametersGenerated: true,
argov1alpha1.ApplicationSetConditionResourcesUpToDate: true,
}
if needToUpdateConditions || len(applicationSet.Status.Conditions) < len(newConditions) {
if needToUpdateConditions || len(applicationSet.Status.Conditions) < 3 {
// fetch updated Application Set object before updating it
namespacedName := types.NamespacedName{Namespace: applicationSet.Namespace, Name: applicationSet.Name}
if err := r.Get(ctx, namespacedName, applicationSet); err != nil {
@@ -501,7 +503,7 @@ func (r *ApplicationSetReconciler) getMinRequeueAfter(applicationSetInfo *argov1
func ignoreNotAllowedNamespaces(namespaces []string) predicate.Predicate {
return predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool {
return utils.IsNamespaceAllowed(namespaces, e.Object.GetNamespace())
return glob.MatchStringInList(namespaces, e.Object.GetNamespace(), false)
},
}
}
@@ -544,6 +546,25 @@ func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager, enableProg
Complete(r)
}
func (r *ApplicationSetReconciler) updateCache(ctx context.Context, obj client.Object, logger *log.Entry) {
informer, err := r.Cache.GetInformer(ctx, obj)
if err != nil {
logger.Errorf("failed to get informer: %v", err)
return
}
// The controller runtime abstract away informers creation
// so unfortunately could not find any other way to access informer store.
k8sInformer, ok := informer.(k8scache.SharedInformer)
if !ok {
logger.Error("informer is not a kubernetes informer")
return
}
if err := k8sInformer.GetStore().Update(obj); err != nil {
logger.Errorf("failed to update cache: %v", err)
return
}
}
// createOrUpdateInCluster will create / update application resources in the cluster.
// - For new applications, it will call create
// - For existing application, it will call update
@@ -641,6 +662,7 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
}
continue
}
r.updateCache(ctx, found, appLog)
if action != controllerutil.OperationResultNone {
// Don't pollute etcd with "unchanged Application" events
@@ -807,6 +829,7 @@ func (r *ApplicationSetReconciler) removeFinalizerOnInvalidDestination(ctx conte
if err := r.Client.Patch(ctx, updated, patch); err != nil {
return fmt.Errorf("error updating finalizers: %w", err)
}
r.updateCache(ctx, updated, appLog)
// Application must have updated list of finalizers
updated.DeepCopyInto(app)
@@ -836,9 +859,12 @@ func (r *ApplicationSetReconciler) removeOwnerReferencesOnDeleteAppSet(ctx conte
}
func (r *ApplicationSetReconciler) performProgressiveSyncs(ctx context.Context, logCtx *log.Entry, appset argov1alpha1.ApplicationSet, applications []argov1alpha1.Application, desiredApplications []argov1alpha1.Application, appMap map[string]argov1alpha1.Application) (map[string]bool, error) {
appDependencyList, appStepMap := r.buildAppDependencyList(logCtx, appset, desiredApplications)
appDependencyList, appStepMap, err := r.buildAppDependencyList(logCtx, appset, desiredApplications)
if err != nil {
return nil, fmt.Errorf("failed to build app dependency list: %w", err)
}
_, err := r.updateApplicationSetApplicationStatus(ctx, logCtx, &appset, applications, appStepMap)
_, err = r.updateApplicationSetApplicationStatus(ctx, logCtx, &appset, applications, appStepMap)
if err != nil {
return nil, fmt.Errorf("failed to update applicationset app status: %w", err)
}
@@ -848,27 +874,34 @@ func (r *ApplicationSetReconciler) performProgressiveSyncs(ctx context.Context,
logCtx.Infof("step %v: %+v", i+1, step)
}
appSyncMap := r.buildAppSyncMap(appset, appDependencyList, appMap)
appSyncMap, err := r.buildAppSyncMap(ctx, appset, appDependencyList, appMap)
if err != nil {
return nil, fmt.Errorf("failed to build app sync map: %w", err)
}
logCtx.Infof("Application allowed to sync before maxUpdate?: %+v", appSyncMap)
_, err = r.updateApplicationSetApplicationStatusProgress(ctx, logCtx, &appset, appSyncMap, appStepMap)
_, err = r.updateApplicationSetApplicationStatusProgress(ctx, logCtx, &appset, appSyncMap, appStepMap, appMap)
if err != nil {
return nil, fmt.Errorf("failed to update applicationset application status progress: %w", err)
}
_ = r.updateApplicationSetApplicationStatusConditions(ctx, &appset)
_, err = r.updateApplicationSetApplicationStatusConditions(ctx, &appset)
if err != nil {
return nil, fmt.Errorf("failed to update applicationset application status conditions: %w", err)
}
return appSyncMap, nil
}
// this list tracks which Applications belong to each RollingUpdate step
func (r *ApplicationSetReconciler) buildAppDependencyList(logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, applications []argov1alpha1.Application) ([][]string, map[string]int) {
func (r *ApplicationSetReconciler) buildAppDependencyList(logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, applications []argov1alpha1.Application) ([][]string, map[string]int, error) {
if applicationSet.Spec.Strategy == nil || applicationSet.Spec.Strategy.Type == "" || applicationSet.Spec.Strategy.Type == "AllAtOnce" {
return [][]string{}, map[string]int{}
return [][]string{}, map[string]int{}, nil
}
steps := []argov1alpha1.ApplicationSetRolloutStep{}
if progressiveSyncsRollingSyncStrategyEnabled(&applicationSet) {
if progressiveSyncsStrategyEnabled(&applicationSet, "RollingSync") {
steps = applicationSet.Spec.Strategy.RollingSync.Steps
}
@@ -909,7 +942,7 @@ func (r *ApplicationSetReconciler) buildAppDependencyList(logCtx *log.Entry, app
}
}
return appDependencyList, appStepMap
return appDependencyList, appStepMap, nil
}
func labelMatchedExpression(logCtx *log.Entry, val string, matchExpression argov1alpha1.ApplicationMatchExpression) bool {
@@ -933,7 +966,7 @@ func labelMatchedExpression(logCtx *log.Entry, val string, matchExpression argov
}
// this map is used to determine which stage of Applications are ready to be updated in the reconciler loop
func (r *ApplicationSetReconciler) buildAppSyncMap(applicationSet argov1alpha1.ApplicationSet, appDependencyList [][]string, appMap map[string]argov1alpha1.Application) map[string]bool {
func (r *ApplicationSetReconciler) buildAppSyncMap(ctx context.Context, applicationSet argov1alpha1.ApplicationSet, appDependencyList [][]string, appMap map[string]argov1alpha1.Application) (map[string]bool, error) {
appSyncMap := map[string]bool{}
syncEnabled := true
@@ -970,11 +1003,11 @@ func (r *ApplicationSetReconciler) buildAppSyncMap(applicationSet argov1alpha1.A
}
}
return appSyncMap
return appSyncMap, nil
}
func appSyncEnabledForNextStep(appset *argov1alpha1.ApplicationSet, app argov1alpha1.Application, appStatus argov1alpha1.ApplicationSetApplicationStatus) bool {
if progressiveSyncsRollingSyncStrategyEnabled(appset) {
if progressiveSyncsStrategyEnabled(appset, "RollingSync") {
// we still need to complete the current step if the Application is not yet Healthy or there are still pending Application changes
return isApplicationHealthy(app) && appStatus.Status == "Healthy"
}
@@ -982,8 +1015,16 @@ func appSyncEnabledForNextStep(appset *argov1alpha1.ApplicationSet, app argov1al
return true
}
func progressiveSyncsRollingSyncStrategyEnabled(appset *argov1alpha1.ApplicationSet) bool {
return appset.Spec.Strategy != nil && appset.Spec.Strategy.RollingSync != nil && appset.Spec.Strategy.Type == "RollingSync"
func progressiveSyncsStrategyEnabled(appset *argov1alpha1.ApplicationSet, strategyType string) bool {
if appset.Spec.Strategy == nil || appset.Spec.Strategy.Type != strategyType {
return false
}
if strategyType == "RollingSync" && appset.Spec.Strategy.RollingSync == nil {
return false
}
return true
}
func isApplicationHealthy(app argov1alpha1.Application) bool {
@@ -1041,7 +1082,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
}
appOutdated := false
if progressiveSyncsRollingSyncStrategyEnabled(applicationSet) {
if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") {
appOutdated = syncStatusString == "OutOfSync"
}
@@ -1107,7 +1148,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
}
// check Applications that are in Waiting status and promote them to Pending if needed
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress(ctx context.Context, logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appStepMap map[string]int) ([]argov1alpha1.ApplicationSetApplicationStatus, error) {
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress(ctx context.Context, logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appStepMap map[string]int, appMap map[string]argov1alpha1.Application) ([]argov1alpha1.ApplicationSetApplicationStatus, error) {
now := metav1.Now()
appStatuses := make([]argov1alpha1.ApplicationSetApplicationStatus, 0, len(applicationSet.Status.ApplicationStatus))
@@ -1118,7 +1159,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress
totalCountMap := []int{}
length := 0
if progressiveSyncsRollingSyncStrategyEnabled(applicationSet) {
if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") {
length = len(applicationSet.Spec.Strategy.RollingSync.Steps)
}
for s := 0; s < length; s++ {
@@ -1130,7 +1171,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress
for _, appStatus := range applicationSet.Status.ApplicationStatus {
totalCountMap[appStepMap[appStatus.Application]] += 1
if progressiveSyncsRollingSyncStrategyEnabled(applicationSet) {
if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") {
if appStatus.Status == "Pending" || appStatus.Status == "Progressing" {
updateCountMap[appStepMap[appStatus.Application]] += 1
}
@@ -1140,7 +1181,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress
for _, appStatus := range applicationSet.Status.ApplicationStatus {
maxUpdateAllowed := true
maxUpdate := &intstr.IntOrString{}
if progressiveSyncsRollingSyncStrategyEnabled(applicationSet) {
if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") {
maxUpdate = applicationSet.Spec.Strategy.RollingSync.Steps[appStepMap[appStatus.Application]].MaxUpdate
}
@@ -1184,7 +1225,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress
return appStatuses, nil
}
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusConditions(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet) []argov1alpha1.ApplicationSetCondition {
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusConditions(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet) ([]argov1alpha1.ApplicationSetCondition, error) {
appSetProgressing := false
for _, appStatus := range applicationSet.Status.ApplicationStatus {
if appStatus.Status != "Healthy" {
@@ -1209,7 +1250,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusConditio
Message: "ApplicationSet Rollout Rollout started",
Reason: argov1alpha1.ApplicationSetReasonApplicationSetModified,
Status: argov1alpha1.ApplicationSetConditionStatusTrue,
}, true,
}, false,
)
} else if !appSetProgressing && appSetConditionProgressing {
_ = r.setApplicationSetStatusCondition(ctx,
@@ -1219,11 +1260,11 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusConditio
Message: "ApplicationSet Rollout Rollout complete",
Reason: argov1alpha1.ApplicationSetReasonApplicationSetRolloutComplete,
Status: argov1alpha1.ApplicationSetConditionStatusFalse,
}, true,
}, false,
)
}
return applicationSet.Status.Conditions
return applicationSet.Status.Conditions, nil
}
func findApplicationStatusIndex(appStatuses []argov1alpha1.ApplicationSetApplicationStatus, application string) int {
@@ -1249,16 +1290,9 @@ func (r *ApplicationSetReconciler) migrateStatus(ctx context.Context, appset *ar
}
if update {
namespacedName := types.NamespacedName{Namespace: appset.Namespace, Name: appset.Name}
if err := r.Client.Status().Update(ctx, appset); err != nil {
return fmt.Errorf("unable to set application set status: %w", err)
}
if err := r.Get(ctx, namespacedName, appset); err != nil {
if client.IgnoreNotFound(err) != nil {
return nil
}
return fmt.Errorf("error fetching updated application set: %w", err)
}
}
return nil
}
@@ -1340,7 +1374,7 @@ func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Contex
return nil
}
func (r *ApplicationSetReconciler) syncValidApplications(logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appMap map[string]argov1alpha1.Application, validApps []argov1alpha1.Application) []argov1alpha1.Application {
func (r *ApplicationSetReconciler) syncValidApplications(logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appMap map[string]argov1alpha1.Application, validApps []argov1alpha1.Application) ([]argov1alpha1.Application, error) {
rolloutApps := []argov1alpha1.Application{}
for i := range validApps {
pruneEnabled := false
@@ -1361,15 +1395,15 @@ func (r *ApplicationSetReconciler) syncValidApplications(logCtx *log.Entry, appl
// check appSyncMap to determine which Applications are ready to be updated and which should be skipped
if appSyncMap[validApps[i].Name] && appMap[validApps[i].Name].Status.Sync.Status == "OutOfSync" && appSetStatusPending {
logCtx.Infof("triggering sync for application: %v, prune enabled: %v", validApps[i].Name, pruneEnabled)
validApps[i] = syncApplication(validApps[i], pruneEnabled)
validApps[i], _ = syncApplication(validApps[i], pruneEnabled)
}
rolloutApps = append(rolloutApps, validApps[i])
}
return rolloutApps
return rolloutApps, nil
}
// used by the RollingSync Progressive Sync strategy to trigger a sync of a particular Application resource
func syncApplication(application argov1alpha1.Application, prune bool) argov1alpha1.Application {
func syncApplication(application argov1alpha1.Application, prune bool) (argov1alpha1.Application, error) {
operation := argov1alpha1.Operation{
InitiatedBy: argov1alpha1.OperationInitiator{
Username: "applicationset-controller",
@@ -1395,7 +1429,7 @@ func syncApplication(application argov1alpha1.Application, prune bool) argov1alp
}
application.Operation = &operation
return application
return application, nil
}
func getOwnsHandlerPredicates(enableProgressiveSyncs bool) predicate.Funcs {

View File

@@ -20,8 +20,11 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
kubefake "k8s.io/client-go/kubernetes/fake"
k8scache "k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/cache"
crtcache "sigs.k8s.io/controller-runtime/pkg/cache"
crtclient "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
@@ -34,7 +37,6 @@ import (
"github.com/argoproj/argo-cd/v2/applicationset/generators/mocks"
"github.com/argoproj/argo-cd/v2/applicationset/utils"
appsetmetrics "github.com/argoproj/argo-cd/v2/applicationset/metrics"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake"
dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks"
@@ -42,6 +44,34 @@ import (
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
)
type fakeStore struct {
k8scache.Store
}
func (f *fakeStore) Update(obj interface{}) error {
return nil
}
type fakeInformer struct {
k8scache.SharedInformer
}
func (f *fakeInformer) AddIndexers(indexers k8scache.Indexers) error {
return nil
}
func (f *fakeInformer) GetStore() k8scache.Store {
return &fakeStore{}
}
type fakeCache struct {
cache.Cache
}
func (f *fakeCache) GetInformer(ctx context.Context, obj crtclient.Object, opt ...crtcache.InformerGetOption) (cache.Informer, error) {
return &fakeInformer{}, nil
}
func TestCreateOrUpdateInCluster(t *testing.T) {
scheme := runtime.NewScheme()
err := v1alpha1.AddToScheme(scheme)
@@ -1059,13 +1089,12 @@ func TestCreateOrUpdateInCluster(t *testing.T) {
}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)),
Metrics: metrics,
Cache: &fakeCache{},
}
err = r.createOrUpdateInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.desiredApps)
@@ -1170,14 +1199,13 @@ func TestRemoveFinalizerOnInvalidDestination_FinalizerTypes(t *testing.T) {
objects := append([]runtime.Object{}, secret)
kubeclientset := kubefake.NewSimpleClientset(objects...)
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(10),
KubeClientset: kubeclientset,
Metrics: metrics,
Cache: &fakeCache{},
}
// settingsMgr := settings.NewSettingsManager(context.TODO(), kubeclientset, "namespace")
// argoDB := db.NewDB("namespace", settingsMgr, r.KubeClientset)
@@ -1329,14 +1357,13 @@ func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) {
objects := append([]runtime.Object{}, secret)
kubeclientset := kubefake.NewSimpleClientset(objects...)
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(10),
KubeClientset: kubeclientset,
Metrics: metrics,
Cache: &fakeCache{},
}
// settingsMgr := settings.NewSettingsManager(context.TODO(), kubeclientset, "argocd")
// argoDB := db.NewDB("argocd", settingsMgr, r.KubeClientset)
@@ -1418,14 +1445,13 @@ func TestRemoveOwnerReferencesOnDeleteAppSet(t *testing.T) {
initObjs := []crtclient.Object{&app, &appSet}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(10),
KubeClientset: nil,
Metrics: metrics,
Cache: &fakeCache{},
}
err = r.removeOwnerReferencesOnDeleteAppSet(context.Background(), appSet)
@@ -1620,13 +1646,12 @@ func TestCreateApplications(t *testing.T) {
}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)),
Metrics: metrics,
Cache: &fakeCache{},
}
err = r.createInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.apps)
@@ -1764,14 +1789,12 @@ func TestDeleteInCluster(t *testing.T) {
}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)),
KubeClientset: kubefake.NewSimpleClientset(),
Metrics: metrics,
}
err = r.deleteInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.desiredApps)
@@ -1812,7 +1835,6 @@ func TestGetMinRequeueAfter(t *testing.T) {
require.NoError(t, err)
client := fake.NewClientBuilder().WithScheme(scheme).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
generator := v1alpha1.ApplicationSetGenerator{
List: &v1alpha1.ListGenerator{},
@@ -1836,7 +1858,7 @@ func TestGetMinRequeueAfter(t *testing.T) {
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(0),
Metrics: metrics,
Cache: &fakeCache{},
Generators: map[string]generators.Generator{
"List": &generatorMock10,
"Git": &generatorMock1,
@@ -1883,16 +1905,14 @@ func TestRequeueGeneratorFails(t *testing.T) {
generatorMock.On("GenerateParams", &generator, mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).
Return([]map[string]interface{}{}, fmt.Errorf("Simulated error generating params that could be related to an external service/API call"))
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(0),
Cache: &fakeCache{},
Generators: map[string]generators.Generator{
"PullRequest": &generatorMock,
},
Metrics: metrics,
}
req := ctrl.Request{
@@ -1916,7 +1936,6 @@ func TestValidateGeneratedApplications(t *testing.T) {
require.NoError(t, err)
client := fake.NewClientBuilder().WithScheme(scheme).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
// Valid cluster
myCluster := v1alpha1.Cluster{
@@ -2102,12 +2121,12 @@ func TestValidateGeneratedApplications(t *testing.T) {
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(1),
Cache: &fakeCache{},
Generators: map[string]generators.Generator{},
ArgoDB: &argoDBMock,
ArgoCDNamespace: "namespace",
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
KubeClientset: kubeclientset,
Metrics: metrics,
}
appSetInfo := v1alpha1.ApplicationSet{}
@@ -2192,7 +2211,6 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
argoObjs := []runtime.Object{&project}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithStatusSubresource(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
goodCluster := v1alpha1.Cluster{Server: "https://good-cluster", Name: "good-cluster"}
badCluster := v1alpha1.Cluster{Server: "https://bad-cluster", Name: "bad-cluster"}
argoDBMock.On("GetCluster", mock.Anything, "https://good-cluster").Return(&goodCluster, nil)
@@ -2206,6 +2224,7 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
Scheme: scheme,
Renderer: &utils.Render{},
Recorder: record.NewFakeRecorder(1),
Cache: &fakeCache{},
Generators: map[string]generators.Generator{
"List": generators.NewListGenerator(),
},
@@ -2214,7 +2233,6 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
KubeClientset: kubeclientset,
Policy: v1alpha1.ApplicationsSyncPolicySync,
ArgoCDNamespace: "argocd",
Metrics: metrics,
}
req := ctrl.Request{
@@ -2248,179 +2266,54 @@ func TestSetApplicationSetStatusCondition(t *testing.T) {
err = v1alpha1.AddToScheme(scheme)
require.NoError(t, err)
testCases := []struct {
appset v1alpha1.ApplicationSet
conditions []v1alpha1.ApplicationSetCondition
testfunc func(t *testing.T, appset v1alpha1.ApplicationSet)
}{
{
appset: v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "name",
Namespace: "argocd",
},
Spec: v1alpha1.ApplicationSetSpec{
Generators: []v1alpha1.ApplicationSetGenerator{
{List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`),
}},
}},
},
Template: v1alpha1.ApplicationSetTemplate{},
},
},
conditions: []v1alpha1.ApplicationSetCondition{
{
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
Message: "All applications have been generated successfully",
Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate,
Status: v1alpha1.ApplicationSetConditionStatusTrue,
},
},
testfunc: func(t *testing.T, appset v1alpha1.ApplicationSet) {
assert.Len(t, appset.Status.Conditions, 3)
},
appSet := v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "name",
Namespace: "argocd",
},
{
appset: v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "name",
Namespace: "argocd",
},
Spec: v1alpha1.ApplicationSetSpec{
Generators: []v1alpha1.ApplicationSetGenerator{
{List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`),
}},
}},
},
Template: v1alpha1.ApplicationSetTemplate{},
},
},
conditions: []v1alpha1.ApplicationSetCondition{
{
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
Message: "All applications have been generated successfully",
Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate,
Status: v1alpha1.ApplicationSetConditionStatusTrue,
},
{
Type: v1alpha1.ApplicationSetConditionRolloutProgressing,
Message: "ApplicationSet Rollout Rollout started",
Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate,
Status: v1alpha1.ApplicationSetConditionStatusTrue,
},
},
testfunc: func(t *testing.T, appset v1alpha1.ApplicationSet) {
assert.Len(t, appset.Status.Conditions, 3)
isProgressingCondition := false
for _, condition := range appset.Status.Conditions {
if condition.Type == v1alpha1.ApplicationSetConditionRolloutProgressing {
isProgressingCondition = true
break
}
}
assert.False(t, isProgressingCondition, "no RolloutProgressing should be set for applicationsets that don't have rolling strategy")
Spec: v1alpha1.ApplicationSetSpec{
Generators: []v1alpha1.ApplicationSetGenerator{
{List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`),
}},
}},
},
Template: v1alpha1.ApplicationSetTemplate{},
},
{
appset: v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "name",
Namespace: "argocd",
},
Spec: v1alpha1.ApplicationSetSpec{
Generators: []v1alpha1.ApplicationSetGenerator{
{List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`),
}},
}},
},
Template: v1alpha1.ApplicationSetTemplate{},
Strategy: &v1alpha1.ApplicationSetStrategy{
Type: "RollingSync",
RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{
Steps: []v1alpha1.ApplicationSetRolloutStep{
{
MatchExpressions: []v1alpha1.ApplicationMatchExpression{
{
Key: "test",
Operator: "In",
Values: []string{"test"},
},
},
},
},
},
},
},
},
conditions: []v1alpha1.ApplicationSetCondition{
{
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
Message: "All applications have been generated successfully",
Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate,
Status: v1alpha1.ApplicationSetConditionStatusTrue,
},
{
Type: v1alpha1.ApplicationSetConditionRolloutProgressing,
Message: "ApplicationSet Rollout Rollout started",
Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate,
Status: v1alpha1.ApplicationSetConditionStatusTrue,
},
},
testfunc: func(t *testing.T, appset v1alpha1.ApplicationSet) {
assert.Len(t, appset.Status.Conditions, 4)
}
isProgressingCondition := false
for _, condition := range appset.Status.Conditions {
if condition.Type == v1alpha1.ApplicationSetConditionRolloutProgressing {
isProgressingCondition = true
break
}
}
assert.True(t, isProgressingCondition, "RolloutProgressing should be set for rollout strategy appset")
},
},
appCondition := v1alpha1.ApplicationSetCondition{
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
Message: "All applications have been generated successfully",
Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate,
Status: v1alpha1.ApplicationSetConditionStatusTrue,
}
kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...)
argoDBMock := dbmocks.ArgoDB{}
argoObjs := []runtime.Object{}
for _, testCase := range testCases {
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&testCase.appset).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Renderer: &utils.Render{},
Recorder: record.NewFakeRecorder(1),
Generators: map[string]generators.Generator{
"List": generators.NewListGenerator(),
},
ArgoDB: &argoDBMock,
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
KubeClientset: kubeclientset,
Metrics: metrics,
}
for _, condition := range testCase.conditions {
err = r.setApplicationSetStatusCondition(context.TODO(), &testCase.appset, condition, true)
require.NoError(t, err)
}
testCase.testfunc(t, testCase.appset)
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Renderer: &utils.Render{},
Recorder: record.NewFakeRecorder(1),
Cache: &fakeCache{},
Generators: map[string]generators.Generator{
"List": generators.NewListGenerator(),
},
ArgoDB: &argoDBMock,
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
KubeClientset: kubeclientset,
}
err = r.setApplicationSetStatusCondition(context.TODO(), &appSet, appCondition, true)
require.NoError(t, err)
assert.Len(t, appSet.Status.Conditions, 3)
}
func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alpha1.ApplicationsSyncPolicy, recordBuffer int, allowPolicyOverride bool) v1alpha1.Application {
@@ -2471,7 +2364,6 @@ func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
argoObjs := []runtime.Object{&defaultProject}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithStatusSubresource(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
goodCluster := v1alpha1.Cluster{Server: "https://good-cluster", Name: "good-cluster"}
argoDBMock.On("GetCluster", mock.Anything, "https://good-cluster").Return(&goodCluster, nil)
argoDBMock.On("ListClusters", mock.Anything).Return(&v1alpha1.ClusterList{Items: []v1alpha1.Cluster{
@@ -2483,6 +2375,7 @@ func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
Scheme: scheme,
Renderer: &utils.Render{},
Recorder: record.NewFakeRecorder(recordBuffer),
Cache: &fakeCache{},
Generators: map[string]generators.Generator{
"List": generators.NewListGenerator(),
},
@@ -2492,7 +2385,6 @@ func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
KubeClientset: kubeclientset,
Policy: v1alpha1.ApplicationsSyncPolicySync,
EnablePolicyOverride: allowPolicyOverride,
Metrics: metrics,
}
req := ctrl.Request{
@@ -2636,7 +2528,6 @@ func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
argoObjs := []runtime.Object{&defaultProject}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithStatusSubresource(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
goodCluster := v1alpha1.Cluster{Server: "https://good-cluster", Name: "good-cluster"}
argoDBMock.On("GetCluster", mock.Anything, "https://good-cluster").Return(&goodCluster, nil)
argoDBMock.On("ListClusters", mock.Anything).Return(&v1alpha1.ClusterList{Items: []v1alpha1.Cluster{
@@ -2648,6 +2539,7 @@ func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
Scheme: scheme,
Renderer: &utils.Render{},
Recorder: record.NewFakeRecorder(recordBuffer),
Cache: &fakeCache{},
Generators: map[string]generators.Generator{
"List": generators.NewListGenerator(),
},
@@ -2657,7 +2549,6 @@ func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
KubeClientset: kubeclientset,
Policy: v1alpha1.ApplicationsSyncPolicySync,
EnablePolicyOverride: allowPolicyOverride,
Metrics: metrics,
}
req := ctrl.Request{
@@ -2839,13 +2730,13 @@ func TestPolicies(t *testing.T) {
}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithStatusSubresource(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Renderer: &utils.Render{},
Recorder: record.NewFakeRecorder(10),
Cache: &fakeCache{},
Generators: map[string]generators.Generator{
"List": generators.NewListGenerator(),
},
@@ -2854,7 +2745,6 @@ func TestPolicies(t *testing.T) {
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
KubeClientset: kubeclientset,
Policy: policy,
Metrics: metrics,
}
req := ctrl.Request{
@@ -3001,20 +2891,19 @@ func TestSetApplicationSetApplicationStatus(t *testing.T) {
} {
t.Run(cc.name, func(t *testing.T) {
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cc.appSet).WithStatusSubresource(&cc.appSet).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Renderer: &utils.Render{},
Recorder: record.NewFakeRecorder(1),
Cache: &fakeCache{},
Generators: map[string]generators.Generator{
"List": generators.NewListGenerator(),
},
ArgoDB: &argoDBMock,
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
KubeClientset: kubeclientset,
Metrics: metrics,
}
err = r.setAppSetApplicationStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.appStatuses)
@@ -3034,7 +2923,6 @@ func TestBuildAppDependencyList(t *testing.T) {
require.NoError(t, err)
client := fake.NewClientBuilder().WithScheme(scheme).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
for _, cc := range []struct {
name string
@@ -3771,14 +3659,15 @@ func TestBuildAppDependencyList(t *testing.T) {
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(1),
Cache: &fakeCache{},
Generators: map[string]generators.Generator{},
ArgoDB: &argoDBMock,
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
KubeClientset: kubeclientset,
Metrics: metrics,
}
appDependencyList, appStepMap := r.buildAppDependencyList(log.NewEntry(log.StandardLogger()), cc.appSet, cc.apps)
appDependencyList, appStepMap, err := r.buildAppDependencyList(log.NewEntry(log.StandardLogger()), cc.appSet, cc.apps)
require.NoError(t, err, "expected no errors, but errors occurred")
assert.Equal(t, cc.expectedList, appDependencyList, "expected appDependencyList did not match actual")
assert.Equal(t, cc.expectedStepMap, appStepMap, "expected appStepMap did not match actual")
})
@@ -3794,7 +3683,6 @@ func TestBuildAppSyncMap(t *testing.T) {
require.NoError(t, err)
client := fake.NewClientBuilder().WithScheme(scheme).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
for _, cc := range []struct {
name string
@@ -4362,14 +4250,15 @@ func TestBuildAppSyncMap(t *testing.T) {
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(1),
Cache: &fakeCache{},
Generators: map[string]generators.Generator{},
ArgoDB: &argoDBMock,
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
KubeClientset: kubeclientset,
Metrics: metrics,
}
appSyncMap := r.buildAppSyncMap(cc.appSet, cc.appDependencyList, cc.appMap)
appSyncMap, err := r.buildAppSyncMap(context.TODO(), cc.appSet, cc.appDependencyList, cc.appMap)
require.NoError(t, err, "expected no errors, but errors occurred")
assert.Equal(t, cc.expectedMap, appSyncMap, "expected appSyncMap did not match actual")
})
}
@@ -5146,17 +5035,16 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
argoObjs := []runtime.Object{}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cc.appSet).WithStatusSubresource(&cc.appSet).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(1),
Cache: &fakeCache{},
Generators: map[string]generators.Generator{},
ArgoDB: &argoDBMock,
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
KubeClientset: kubeclientset,
Metrics: metrics,
}
appStatuses, err := r.updateApplicationSetApplicationStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.apps, cc.appStepMap)
@@ -5900,20 +5788,19 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) {
argoObjs := []runtime.Object{}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&cc.appSet).WithStatusSubresource(&cc.appSet).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(1),
Cache: &fakeCache{},
Generators: map[string]generators.Generator{},
ArgoDB: &argoDBMock,
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
KubeClientset: kubeclientset,
Metrics: metrics,
}
appStatuses, err := r.updateApplicationSetApplicationStatusProgress(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.appSyncMap, cc.appStepMap)
appStatuses, err := r.updateApplicationSetApplicationStatusProgress(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.appSyncMap, cc.appStepMap, cc.appMap)
// opt out of testing the LastTransitionTime is accurate
for i := range appStatuses {
@@ -6116,17 +6003,16 @@ func TestUpdateResourceStatus(t *testing.T) {
argoObjs := []runtime.Object{}
client := fake.NewClientBuilder().WithScheme(scheme).WithStatusSubresource(&cc.appSet).WithObjects(&cc.appSet).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(1),
Cache: &fakeCache{},
Generators: map[string]generators.Generator{},
ArgoDB: &argoDBMock,
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
KubeClientset: kubeclientset,
Metrics: metrics,
}
err := r.updateResourcesStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.apps)

View File

@@ -4,8 +4,6 @@ import (
"context"
"fmt"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
log "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/types"
@@ -26,29 +24,29 @@ type clusterSecretEventHandler struct {
Client client.Client
}
func (h *clusterSecretEventHandler) Create(ctx context.Context, e event.CreateEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
func (h *clusterSecretEventHandler) Create(ctx context.Context, e event.CreateEvent, q workqueue.RateLimitingInterface) {
h.queueRelatedAppGenerators(ctx, q, e.Object)
}
func (h *clusterSecretEventHandler) Update(ctx context.Context, e event.UpdateEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
func (h *clusterSecretEventHandler) Update(ctx context.Context, e event.UpdateEvent, q workqueue.RateLimitingInterface) {
h.queueRelatedAppGenerators(ctx, q, e.ObjectNew)
}
func (h *clusterSecretEventHandler) Delete(ctx context.Context, e event.DeleteEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
func (h *clusterSecretEventHandler) Delete(ctx context.Context, e event.DeleteEvent, q workqueue.RateLimitingInterface) {
h.queueRelatedAppGenerators(ctx, q, e.Object)
}
func (h *clusterSecretEventHandler) Generic(ctx context.Context, e event.GenericEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
func (h *clusterSecretEventHandler) Generic(ctx context.Context, e event.GenericEvent, q workqueue.RateLimitingInterface) {
h.queueRelatedAppGenerators(ctx, q, e.Object)
}
// addRateLimitingInterface defines the Add method of workqueue.RateLimitingInterface, allow us to easily mock
// it for testing purposes.
type addRateLimitingInterface[T comparable] interface {
Add(item T)
type addRateLimitingInterface interface {
Add(item interface{})
}
func (h *clusterSecretEventHandler) queueRelatedAppGenerators(ctx context.Context, q addRateLimitingInterface[reconcile.Request], object client.Object) {
func (h *clusterSecretEventHandler) queueRelatedAppGenerators(ctx context.Context, q addRateLimitingInterface, object client.Object) {
// Check for label, lookup all ApplicationSets that might match the cluster, queue them all
if object.GetLabels()[generators.ArgoCDSecretTypeLabel] != generators.ArgoCDSecretTypeCluster {
return

View File

@@ -551,18 +551,24 @@ func TestClusterEventHandler(t *testing.T) {
handler.queueRelatedAppGenerators(context.Background(), &mockAddRateLimitingInterface, &test.secret)
assert.False(t, mockAddRateLimitingInterface.errorOccurred)
assert.ElementsMatch(t, mockAddRateLimitingInterface.addedItems, test.expectedRequests)
})
}
}
// Add checks the type, and adds it to the internal list of received additions
func (obj *mockAddRateLimitingInterface) Add(item reconcile.Request) {
obj.addedItems = append(obj.addedItems, item)
func (obj *mockAddRateLimitingInterface) Add(item interface{}) {
if req, ok := item.(ctrl.Request); ok {
obj.addedItems = append(obj.addedItems, req)
} else {
obj.errorOccurred = true
}
}
type mockAddRateLimitingInterface struct {
addedItems []reconcile.Request
errorOccurred bool
addedItems []ctrl.Request
}
func TestNestedGeneratorHasClusterGenerator_NestedClusterGenerator(t *testing.T) {

View File

@@ -17,7 +17,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"github.com/argoproj/argo-cd/v2/applicationset/generators"
appsetmetrics "github.com/argoproj/argo-cd/v2/applicationset/metrics"
"github.com/argoproj/argo-cd/v2/applicationset/services/mocks"
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
)
@@ -61,7 +60,7 @@ func TestRequeueAfter(t *testing.T) {
terminalGenerators := map[string]generators.Generator{
"List": generators.NewListGenerator(),
"Clusters": generators.NewClusterGenerator(k8sClient, ctx, appClientset, "argocd"),
"Git": generators.NewGitGenerator(mockServer, "namespace"),
"Git": generators.NewGitGenerator(mockServer),
"SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), scmConfig),
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, fakeDynClient, appClientset, "argocd"),
"PullRequest": generators.NewPullRequestGenerator(k8sClient, scmConfig),
@@ -90,13 +89,11 @@ func TestRequeueAfter(t *testing.T) {
}
client := fake.NewClientBuilder().WithScheme(scheme).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(0),
Generators: topLevelGenerators,
Metrics: metrics,
}
type args struct {

View File

@@ -346,7 +346,7 @@ func getMockClusterGenerator() Generator {
func getMockGitGenerator() Generator {
argoCDServiceMock := mocks.Repos{}
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return([]string{"app1", "app2", "app_3", "p1/app4"}, nil)
gitGenerator := NewGitGenerator(&argoCDServiceMock, "namespace")
gitGenerator := NewGitGenerator(&argoCDServiceMock)
return gitGenerator
}

View File

@@ -24,16 +24,13 @@ import (
var _ Generator = (*GitGenerator)(nil)
type GitGenerator struct {
repos services.Repos
namespace string
repos services.Repos
}
func NewGitGenerator(repos services.Repos, namespace string) Generator {
func NewGitGenerator(repos services.Repos) Generator {
g := &GitGenerator{
repos: repos,
namespace: namespace,
repos: repos,
}
return g
}
@@ -62,25 +59,21 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic
noRevisionCache := appSet.RefreshRequired()
verifyCommit := false
// When the project field is templated, the contents of the git repo are required to run the git generator and get the templated value,
// but git generator cannot be called without verifying the commit signature.
// In this case, we skip the signature verification.
if !strings.Contains(appSet.Spec.Template.Spec.Project, "{{") {
project := appSet.Spec.Template.Spec.Project
appProject := &argoprojiov1alpha1.AppProject{}
namespace := g.namespace
if namespace == "" {
namespace = appSet.Namespace
}
if err := client.Get(context.TODO(), types.NamespacedName{Name: project, Namespace: namespace}, appProject); err != nil {
return nil, fmt.Errorf("error getting project %s: %w", project, err)
}
// we need to verify the signature on the Git revision if GPG is enabled
verifyCommit = appProject.Spec.SignatureKeys != nil && len(appProject.Spec.SignatureKeys) > 0 && gpg.IsGPGEnabled()
var project string
if strings.Contains(appSet.Spec.Template.Spec.Project, "{{") {
project = appSetGenerator.Git.Template.Spec.Project
} else {
project = appSet.Spec.Template.Spec.Project
}
appProject := &argoprojiov1alpha1.AppProject{}
if err := client.Get(context.TODO(), types.NamespacedName{Name: appSet.Spec.Template.Spec.Project, Namespace: appSet.Namespace}, appProject); err != nil {
return nil, fmt.Errorf("error getting project %s: %w", project, err)
}
// we need to verify the signature on the Git revision if GPG is enabled
verifyCommit := appProject.Spec.SignatureKeys != nil && len(appProject.Spec.SignatureKeys) > 0 && gpg.IsGPGEnabled()
var err error
var res []map[string]interface{}
if len(appSetGenerator.Git.Directories) != 0 {

View File

@@ -323,7 +323,7 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
gitGenerator := NewGitGenerator(&argoCDServiceMock)
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "set",
@@ -624,7 +624,7 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
gitGenerator := NewGitGenerator(&argoCDServiceMock)
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "set",
@@ -989,7 +989,7 @@ cluster:
argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError)
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
gitGenerator := NewGitGenerator(&argoCDServiceMock)
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "set",
@@ -1345,7 +1345,7 @@ cluster:
argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError)
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
gitGenerator := NewGitGenerator(&argoCDServiceMock)
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "set",
@@ -1383,114 +1383,3 @@ cluster:
})
}
}
func TestGitGenerator_GenerateParams(t *testing.T) {
cases := []struct {
name string
directories []argoprojiov1alpha1.GitDirectoryGeneratorItem
pathParamPrefix string
repoApps []string
repoPathsError error
repoFileContents map[string][]byte
values map[string]string
expected []map[string]interface{}
expectedError error
appset argoprojiov1alpha1.ApplicationSet
callGetDirectories bool
}{
{
name: "Signature Verification - ignores templated project field",
repoApps: []string{
"app1",
},
repoPathsError: nil,
appset: argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "set",
Namespace: "namespace",
},
Spec: argoprojiov1alpha1.ApplicationSetSpec{
Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
Git: &argoprojiov1alpha1.GitGenerator{
RepoURL: "RepoURL",
Revision: "Revision",
Directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{Path: "*"}},
PathParamPrefix: "",
Values: map[string]string{
"foo": "bar",
},
},
}},
Template: argoprojiov1alpha1.ApplicationSetTemplate{
Spec: argoprojiov1alpha1.ApplicationSpec{
Project: "{{.project}}",
},
},
},
},
callGetDirectories: true,
expected: []map[string]interface{}{{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "path[0]": "app1", "values.foo": "bar"}},
expectedError: nil,
},
{
name: "Signature Verification - Checks for non-templated project field",
repoApps: []string{
"app1",
},
repoPathsError: nil,
appset: argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "set",
Namespace: "namespace",
},
Spec: argoprojiov1alpha1.ApplicationSetSpec{
Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
Git: &argoprojiov1alpha1.GitGenerator{
RepoURL: "RepoURL",
Revision: "Revision",
Directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{Path: "*"}},
PathParamPrefix: "",
Values: map[string]string{
"foo": "bar",
},
},
}},
Template: argoprojiov1alpha1.ApplicationSetTemplate{
Spec: argoprojiov1alpha1.ApplicationSpec{
Project: "project",
},
},
},
},
callGetDirectories: false,
expected: []map[string]interface{}{{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "path[0]": "app1", "values.foo": "bar"}},
expectedError: fmt.Errorf("error getting project project: appprojects.argoproj.io \"project\" not found"),
},
}
for _, testCase := range cases {
argoCDServiceMock := mocks.Repos{}
if testCase.callGetDirectories {
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCase.repoApps, testCase.repoPathsError)
}
gitGenerator := NewGitGenerator(&argoCDServiceMock, "namespace")
scheme := runtime.NewScheme()
err := v1alpha1.AddToScheme(scheme)
require.NoError(t, err)
appProject := argoprojiov1alpha1.AppProject{}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appProject).Build()
got, err := gitGenerator.GenerateParams(&testCase.appset.Spec.Generators[0], &testCase.appset, client)
if testCase.expectedError != nil {
require.EqualError(t, err, testCase.expectedError.Error())
} else {
require.NoError(t, err)
assert.Equal(t, testCase.expected, got)
}
argoCDServiceMock.AssertExpectations(t)
}
}

View File

@@ -1089,7 +1089,7 @@ func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
repoServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(map[string][]byte{
"some/path.json": []byte("test: content"),
}, nil)
gitGenerator := NewGitGenerator(repoServiceMock, "")
gitGenerator := NewGitGenerator(repoServiceMock)
matrixGenerator := NewMatrixGenerator(map[string]Generator{
"List": listGeneratorMock,

View File

@@ -14,7 +14,7 @@ func GetGenerators(ctx context.Context, c client.Client, k8sClient kubernetes.In
terminalGenerators := map[string]Generator{
"List": NewListGenerator(),
"Clusters": NewClusterGenerator(c, ctx, k8sClient, namespace),
"Git": NewGitGenerator(argoCDService, namespace),
"Git": NewGitGenerator(argoCDService),
"SCMProvider": NewSCMProviderGenerator(c, scmConfig),
"ClusterDecisionResource": NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, namespace),
"PullRequest": NewPullRequestGenerator(c, scmConfig),

View File

@@ -1,22 +0,0 @@
package metrics
import (
"github.com/prometheus/client_golang/prometheus"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
)
// Fake implementation for testing
func NewFakeAppsetMetrics(client ctrlclient.WithWatch) *ApplicationsetMetrics {
reconcileHistogram := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "argocd_appset_reconcile",
Help: "Application reconciliation performance in seconds.",
// Buckets can be set later on after observing median time
},
[]string{"name", "namespace"},
)
return &ApplicationsetMetrics{
reconcileHistogram: reconcileHistogram,
}
}

View File

@@ -1,131 +0,0 @@
package metrics
import (
"time"
"github.com/prometheus/client_golang/prometheus"
"k8s.io/apimachinery/pkg/labels"
"sigs.k8s.io/controller-runtime/pkg/metrics"
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
applisters "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
metricsutil "github.com/argoproj/argo-cd/v2/util/metrics"
)
var (
descAppsetLabels *prometheus.Desc
descAppsetDefaultLabels = []string{"namespace", "name"}
descAppsetInfo = prometheus.NewDesc(
"argocd_appset_info",
"Information about applicationset",
append(descAppsetDefaultLabels, "resource_update_status"),
nil,
)
descAppsetGeneratedApps = prometheus.NewDesc(
"argocd_appset_owned_applications",
"Number of applications owned by the applicationset",
descAppsetDefaultLabels,
nil,
)
)
type ApplicationsetMetrics struct {
reconcileHistogram *prometheus.HistogramVec
}
type appsetCollector struct {
lister applisters.ApplicationSetLister
// appsClientSet appclientset.Interface
labels []string
filter func(appset *argoappv1.ApplicationSet) bool
}
func NewApplicationsetMetrics(appsetLister applisters.ApplicationSetLister, appsetLabels []string, appsetFilter func(appset *argoappv1.ApplicationSet) bool) ApplicationsetMetrics {
reconcileHistogram := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "argocd_appset_reconcile",
Help: "Application reconciliation performance in seconds.",
// Buckets can be set later on after observing median time
},
descAppsetDefaultLabels,
)
appsetCollector := newAppsetCollector(appsetLister, appsetLabels, appsetFilter)
// Register collectors and metrics
metrics.Registry.MustRegister(reconcileHistogram)
metrics.Registry.MustRegister(appsetCollector)
return ApplicationsetMetrics{
reconcileHistogram: reconcileHistogram,
}
}
func (m *ApplicationsetMetrics) ObserveReconcile(appset *argoappv1.ApplicationSet, duration time.Duration) {
m.reconcileHistogram.WithLabelValues(appset.Namespace, appset.Name).Observe(duration.Seconds())
}
func newAppsetCollector(lister applisters.ApplicationSetLister, labels []string, filter func(appset *argoappv1.ApplicationSet) bool) *appsetCollector {
descAppsetDefaultLabels = []string{"namespace", "name"}
if len(labels) > 0 {
descAppsetLabels = prometheus.NewDesc(
"argocd_appset_labels",
"Applicationset labels translated to Prometheus labels",
append(descAppsetDefaultLabels, metricsutil.NormalizeLabels("label", labels)...),
nil,
)
}
return &appsetCollector{
lister: lister,
labels: labels,
filter: filter,
}
}
// Describe implements the prometheus.Collector interface
func (c *appsetCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- descAppsetInfo
ch <- descAppsetGeneratedApps
if len(c.labels) > 0 {
ch <- descAppsetLabels
}
}
// Collect implements the prometheus.Collector interface
func (c *appsetCollector) Collect(ch chan<- prometheus.Metric) {
appsets, _ := c.lister.List(labels.NewSelector())
for _, appset := range appsets {
if c.filter(appset) {
collectAppset(appset, c.labels, ch)
}
}
}
func collectAppset(appset *argoappv1.ApplicationSet, labelsToCollect []string, ch chan<- prometheus.Metric) {
labelValues := make([]string, 0)
commonLabelValues := []string{appset.Namespace, appset.Name}
for _, label := range labelsToCollect {
labelValues = append(labelValues, appset.GetLabels()[label])
}
resourceUpdateStatus := "Unknown"
for _, condition := range appset.Status.Conditions {
if condition.Type == argoappv1.ApplicationSetConditionResourcesUpToDate {
resourceUpdateStatus = condition.Reason
}
}
if len(labelsToCollect) > 0 {
ch <- prometheus.MustNewConstMetric(descAppsetLabels, prometheus.GaugeValue, 1, append(commonLabelValues, labelValues...)...)
}
ch <- prometheus.MustNewConstMetric(descAppsetInfo, prometheus.GaugeValue, 1, appset.Namespace, appset.Name, resourceUpdateStatus)
ch <- prometheus.MustNewConstMetric(descAppsetGeneratedApps, prometheus.GaugeValue, float64(len(appset.Status.Resources)), appset.Namespace, appset.Name)
}

View File

@@ -1,256 +0,0 @@
package metrics
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/argoproj/argo-cd/v2/applicationset/utils"
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/runtime"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
fake "sigs.k8s.io/controller-runtime/pkg/client/fake"
prometheus "github.com/prometheus/client_golang/prometheus"
metricsutil "github.com/argoproj/argo-cd/v2/util/metrics"
"sigs.k8s.io/controller-runtime/pkg/metrics"
"sigs.k8s.io/yaml"
)
var (
applicationsetNamespaces = []string{"argocd", "test-namespace1"}
filter = func(appset *argoappv1.ApplicationSet) bool {
return utils.IsNamespaceAllowed(applicationsetNamespaces, appset.Namespace)
}
collectedLabels = []string{"included/test"}
)
const fakeAppsetList = `
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: test1
namespace: argocd
labels:
included/test: test
not-included.label/test: test
spec:
generators:
- git:
directories:
- path: test/*
repoURL: https://github.com/test/test.git
revision: HEAD
template:
metadata:
name: '{{.path.basename}}'
spec:
destination:
namespace: '{{.path.basename}}'
server: https://kubernetes.default.svc
project: default
source:
path: '{{.path.path}}'
repoURL: https://github.com/test/test.git
targetRevision: HEAD
status:
resources:
- group: argoproj.io
health:
status: Missing
kind: Application
name: test-app1
namespace: argocd
status: OutOfSync
version: v1alpha1
- group: argoproj.io
health:
status: Missing
kind: Application
name: test-app2
namespace: argocd
status: OutOfSync
version: v1alpha1
conditions:
- lastTransitionTime: "2024-01-01T00:00:00Z"
message: Successfully generated parameters for all Applications
reason: ApplicationSetUpToDate
status: "False"
type: ErrorOccurred
- lastTransitionTime: "2024-01-01T00:00:00Z"
message: Successfully generated parameters for all Applications
reason: ParametersGenerated
status: "True"
type: ParametersGenerated
- lastTransitionTime: "2024-01-01T00:00:00Z"
message: ApplicationSet up to date
reason: ApplicationSetUpToDate
status: "True"
type: ResourcesUpToDate
---
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: test2
namespace: argocd
labels:
not-included.label/test: test
spec:
generators:
- git:
directories:
- path: test/*
repoURL: https://github.com/test/test.git
revision: HEAD
template:
metadata:
name: '{{.path.basename}}'
spec:
destination:
namespace: '{{.path.basename}}'
server: https://kubernetes.default.svc
project: default
source:
path: '{{.path.path}}'
repoURL: https://github.com/test/test.git
targetRevision: HEAD
---
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: should-be-filtered-out
namespace: not-allowed
spec:
generators:
- git:
directories:
- path: test/*
repoURL: https://github.com/test/test.git
revision: HEAD
template:
metadata:
name: '{{.path.basename}}'
spec:
destination:
namespace: '{{.path.basename}}'
server: https://kubernetes.default.svc
project: default
source:
path: '{{.path.path}}'
repoURL: https://github.com/test/test.git
targetRevision: HEAD
`
func newFakeAppsets(fakeAppsetYAML string) []argoappv1.ApplicationSet {
var results []argoappv1.ApplicationSet
appsetRawYamls := strings.Split(fakeAppsetYAML, "---")
for _, appsetRawYaml := range appsetRawYamls {
var appset argoappv1.ApplicationSet
err := yaml.Unmarshal([]byte(appsetRawYaml), &appset)
if err != nil {
panic(err)
}
results = append(results, appset)
}
return results
}
func TestApplicationsetCollector(t *testing.T) {
appsetList := newFakeAppsets(fakeAppsetList)
client := initializeClient(appsetList)
metrics.Registry = prometheus.NewRegistry()
appsetCollector := newAppsetCollector(utils.NewAppsetLister(client), collectedLabels, filter)
metrics.Registry.MustRegister(appsetCollector)
req, err := http.NewRequest("GET", "/metrics", nil)
require.NoError(t, err)
rr := httptest.NewRecorder()
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{})
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
// Test correct appset_info and owned applications
assert.Contains(t, rr.Body.String(), `
argocd_appset_info{name="test1",namespace="argocd",resource_update_status="ApplicationSetUpToDate"} 1
`)
assert.Contains(t, rr.Body.String(), `
argocd_appset_owned_applications{name="test1",namespace="argocd"} 2
`)
// Test labels collection - should not include labels not included in the list of collected labels and include the ones that do.
assert.Contains(t, rr.Body.String(), `
argocd_appset_labels{label_included_test="test",name="test1",namespace="argocd"} 1
`)
assert.NotContains(t, rr.Body.String(), normalizeLabel("not-included.label/test"))
// If collected label is not present on the applicationset the value should be empty
assert.Contains(t, rr.Body.String(), `
argocd_appset_labels{label_included_test="",name="test2",namespace="argocd"} 1
`)
// If ResourcesUpToDate condition is not present on the applicationset the status should be reported as 'Unknown'
assert.Contains(t, rr.Body.String(), `
argocd_appset_info{name="test2",namespace="argocd",resource_update_status="Unknown"} 1
`)
// If there are no resources on the applicationset the owned application gague should return 0
assert.Contains(t, rr.Body.String(), `
argocd_appset_owned_applications{name="test2",namespace="argocd"} 0
`)
// Test that filter is working
assert.NotContains(t, rr.Body.String(), `name="should-be-filtered-out"`)
}
func TestObserveReconcile(t *testing.T) {
appsetList := newFakeAppsets(fakeAppsetList)
client := initializeClient(appsetList)
metrics.Registry = prometheus.NewRegistry()
appsetMetrics := NewApplicationsetMetrics(utils.NewAppsetLister(client), collectedLabels, filter)
req, err := http.NewRequest("GET", "/metrics", nil)
require.NoError(t, err)
rr := httptest.NewRecorder()
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{})
appsetMetrics.ObserveReconcile(&appsetList[0], 5*time.Second)
handler.ServeHTTP(rr, req)
assert.Contains(t, rr.Body.String(), `
argocd_appset_reconcile_sum{name="test1",namespace="argocd"} 5
`)
// If there are no resources on the applicationset the owned application gague should return 0
assert.Contains(t, rr.Body.String(), `
argocd_appset_reconcile_count{name="test1",namespace="argocd"} 1
`)
}
func initializeClient(appsets []argoappv1.ApplicationSet) ctrlclient.WithWatch {
scheme := runtime.NewScheme()
err := argoappv1.AddToScheme(scheme)
if err != nil {
panic(err)
}
var clientObjects []ctrlclient.Object
for _, appset := range appsets {
clientObjects = append(clientObjects, appset.DeepCopy())
}
return fake.NewClientBuilder().WithScheme(scheme).WithObjects(clientObjects...).Build()
}
func normalizeLabel(label string) string {
return metricsutil.NormalizeLabels("label", []string{label})[0]
}

View File

@@ -1,63 +0,0 @@
package utils
import (
"context"
"k8s.io/apimachinery/pkg/labels"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
. "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
. "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
)
// Implements AppsetLister interface with controller-runtime client
type AppsetLister struct {
Client ctrlclient.Client
}
func NewAppsetLister(client ctrlclient.Client) ApplicationSetLister {
return &AppsetLister{Client: client}
}
func (l *AppsetLister) List(selector labels.Selector) (ret []*ApplicationSet, err error) {
return clientListAppsets(l.Client, ctrlclient.ListOptions{})
}
// ApplicationSets returns an object that can list and get ApplicationSets.
func (l *AppsetLister) ApplicationSets(namespace string) ApplicationSetNamespaceLister {
return &appsetNamespaceLister{
Client: l.Client,
Namespace: namespace,
}
}
// Implements ApplicationSetNamespaceLister
type appsetNamespaceLister struct {
Client ctrlclient.Client
Namespace string
}
func (n *appsetNamespaceLister) List(selector labels.Selector) (ret []*ApplicationSet, err error) {
return clientListAppsets(n.Client, ctrlclient.ListOptions{Namespace: n.Namespace})
}
func (n *appsetNamespaceLister) Get(name string) (*ApplicationSet, error) {
appset := ApplicationSet{}
err := n.Client.Get(context.TODO(), ctrlclient.ObjectKeyFromObject(&appset), &appset)
return &appset, err
}
func clientListAppsets(client ctrlclient.Client, listOptions ctrlclient.ListOptions) (ret []*ApplicationSet, err error) {
var appsetlist ApplicationSetList
var results []*ApplicationSet
err = client.List(context.TODO(), &appsetlist, &listOptions)
if err == nil {
for _, appset := range appsetlist.Items {
results = append(results, appset.DeepCopy())
}
}
return results, err
}

View File

@@ -132,12 +132,9 @@ func getLocalCluster(clientset kubernetes.Interface) *appv1.Cluster {
initLocalCluster.Do(func() {
info, err := clientset.Discovery().ServerVersion()
if err == nil {
// nolint:staticcheck
localCluster.ServerVersion = fmt.Sprintf("%s.%s", info.Major, info.Minor)
// nolint:staticcheck
localCluster.ConnectionState = appv1.ConnectionState{Status: appv1.ConnectionStatusSuccessful}
} else {
// nolint:staticcheck
localCluster.ConnectionState = appv1.ConnectionState{
Status: appv1.ConnectionStatusFailed,
Message: err.Error(),
@@ -146,7 +143,6 @@ func getLocalCluster(clientset kubernetes.Interface) *appv1.Cluster {
})
cluster := localCluster.DeepCopy()
now := metav1.Now()
// nolint:staticcheck
cluster.ConnectionState.ModifiedAt = &now
return cluster
}

View File

@@ -23,7 +23,6 @@ import (
log "github.com/sirupsen/logrus"
argoappsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/glob"
)
var sprigFuncMap = sprig.GenericFuncMap() // a singleton for better performance
@@ -47,10 +46,6 @@ type Renderer interface {
type Render struct{}
func IsNamespaceAllowed(namespaces []string, namespace string) bool {
return glob.MatchStringInList(namespaces, namespace, glob.REGEXP)
}
func copyValueIntoUnexported(destination, value reflect.Value) {
reflect.NewAt(destination.Type(), unsafe.Pointer(destination.UnsafeAddr())).
Elem().

124
assets/swagger.json generated
View File

@@ -4489,27 +4489,6 @@
}
}
},
"applicationsetApplicationSetGenerateRequest": {
"type": "object",
"title": "ApplicationSetGetQuery is a query for applicationset resources",
"properties": {
"applicationSet": {
"$ref": "#/definitions/v1alpha1ApplicationSet"
}
}
},
"applicationsetApplicationSetGenerateResponse": {
"type": "object",
"title": "ApplicationSetGenerateResponse is a response for applicationset generate request",
"properties": {
"applications": {
"type": "array",
"items": {
"$ref": "#/definitions/v1alpha1Application"
}
}
}
},
"applicationsetApplicationSetResponse": {
"type": "object",
"properties": {
@@ -4535,43 +4514,6 @@
}
}
},
"applicationv1alpha1ResourceStatus": {
"type": "object",
"title": "ResourceStatus holds the current sync and health status of a resource\nTODO: describe members of this type",
"properties": {
"group": {
"type": "string"
},
"health": {
"$ref": "#/definitions/v1alpha1HealthStatus"
},
"hook": {
"type": "boolean"
},
"kind": {
"type": "string"
},
"name": {
"type": "string"
},
"namespace": {
"type": "string"
},
"requiresPruning": {
"type": "boolean"
},
"status": {
"type": "string"
},
"syncWave": {
"type": "integer",
"format": "int64"
},
"version": {
"type": "string"
}
}
},
"clusterClusterID": {
"type": "object",
"title": "ClusterID holds a cluster server URL or cluster name",
@@ -5522,7 +5464,7 @@
"properties": {
"matchExpressions": {
"type": "array",
"title": "matchExpressions is a list of label selector requirements. The requirements are ANDed.\n+optional\n+listType=atomic",
"title": "matchExpressions is a list of label selector requirements. The requirements are ANDed.\n+optional",
"items": {
"$ref": "#/definitions/v1LabelSelectorRequirement"
}
@@ -5550,7 +5492,7 @@
},
"values": {
"type": "array",
"title": "values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge patch.\n+optional\n+listType=atomic",
"title": "values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge patch.\n+optional",
"items": {
"type": "string"
}
@@ -5674,7 +5616,7 @@
"type": "string"
},
"kubeProxyVersion": {
"description": "Deprecated: KubeProxy Version reported by the node.",
"description": "KubeProxy Version reported by the node.",
"type": "string"
},
"kubeletVersion": {
@@ -5723,7 +5665,7 @@
},
"finalizers": {
"type": "array",
"title": "Must be empty before the object is deleted from the registry. Each entry\nis an identifier for the responsible component that will remove the entry\nfrom the list. If the deletionTimestamp of the object is non-nil, entries\nin this list can only be removed.\nFinalizers may be processed and removed in any order. Order is NOT enforced\nbecause it introduces significant risk of stuck finalizers.\nfinalizers is a shared field, any actor with permission can reorder it.\nIf the finalizer list is processed in order, then this can lead to a situation\nin which the component responsible for the first finalizer in the list is\nwaiting for a signal (field value, external system, or other) produced by a\ncomponent responsible for a finalizer later in the list, resulting in a deadlock.\nWithout enforced ordering finalizers are free to order amongst themselves and\nare not vulnerable to ordering changes in the list.\n+optional\n+patchStrategy=merge\n+listType=set",
"title": "Must be empty before the object is deleted from the registry. Each entry\nis an identifier for the responsible component that will remove the entry\nfrom the list. If the deletionTimestamp of the object is non-nil, entries\nin this list can only be removed.\nFinalizers may be processed and removed in any order. Order is NOT enforced\nbecause it introduces significant risk of stuck finalizers.\nfinalizers is a shared field, any actor with permission can reorder it.\nIf the finalizer list is processed in order, then this can lead to a situation\nin which the component responsible for the first finalizer in the list is\nwaiting for a signal (field value, external system, or other) produced by a\ncomponent responsible for a finalizer later in the list, resulting in a deadlock.\nWithout enforced ordering finalizers are free to order amongst themselves and\nare not vulnerable to ordering changes in the list.\n+optional\n+patchStrategy=merge",
"items": {
"type": "string"
}
@@ -5745,7 +5687,7 @@
}
},
"managedFields": {
"description": "ManagedFields maps workflow-id and version to the set of fields\nthat are managed by that workflow. This is mostly for internal\nhousekeeping, and users typically shouldn't need to set or\nunderstand this field. A workflow can be the user's name, a\ncontroller's name, or the name of a specific apply path like\n\"ci-cd\". The set of fields is always in the version that the\nworkflow used when modifying the object.\n\n+optional\n+listType=atomic",
"description": "ManagedFields maps workflow-id and version to the set of fields\nthat are managed by that workflow. This is mostly for internal\nhousekeeping, and users typically shouldn't need to set or\nunderstand this field. A workflow can be the user's name, a\ncontroller's name, or the name of a specific apply path like\n\"ci-cd\". The set of fields is always in the version that the\nworkflow used when modifying the object.\n\n+optional",
"type": "array",
"items": {
"$ref": "#/definitions/v1ManagedFieldsEntry"
@@ -5761,7 +5703,7 @@
},
"ownerReferences": {
"type": "array",
"title": "List of objects depended by this object. If ALL objects in the list have\nbeen deleted, this object will be garbage collected. If this object is managed by a controller,\nthen an entry in this list will point to this controller, with the controller field set to true.\nThere cannot be more than one managing controller.\n+optional\n+patchMergeKey=uid\n+patchStrategy=merge\n+listType=map\n+listMapKey=uid",
"title": "List of objects depended by this object. If ALL objects in the list have\nbeen deleted, this object will be garbage collected. If this object is managed by a controller,\nthen an entry in this list will point to this controller, with the controller field set to true.\nThere cannot be more than one managing controller.\n+optional\n+patchMergeKey=uid\n+patchStrategy=merge",
"items": {
"$ref": "#/definitions/v1OwnerReference"
}
@@ -6392,7 +6334,7 @@
"description": "Resources is a list of Applications resources managed by this application set.",
"type": "array",
"items": {
"$ref": "#/definitions/applicationv1alpha1ResourceStatus"
"$ref": "#/definitions/v1alpha1ResourceStatus"
}
}
}
@@ -6859,7 +6801,7 @@
"type": "array",
"title": "Resources is a list of Kubernetes resources managed by this application",
"items": {
"$ref": "#/definitions/applicationv1alpha1ResourceStatus"
"$ref": "#/definitions/v1alpha1ResourceStatus"
}
},
"sourceType": {
@@ -6925,11 +6867,6 @@
"items": {
"$ref": "#/definitions/v1alpha1ResourceNode"
}
},
"shardsCount": {
"type": "integer",
"format": "int64",
"title": "ShardsCount contains total number of shards the application tree is split into"
}
}
},
@@ -8242,10 +8179,6 @@
"type": "string",
"title": "GithubAppPrivateKey specifies the private key PEM data for authentication via GitHub app"
},
"noProxy": {
"type": "string",
"title": "NoProxy specifies a list of targets where the proxy isn't used, applies only in cases where the proxy is applied"
},
"password": {
"type": "string",
"title": "Password for authenticating at the repo server"
@@ -8352,10 +8285,6 @@
"type": "string",
"title": "Name specifies a name to be used for this repo. Only used with Helm repos"
},
"noProxy": {
"type": "string",
"title": "NoProxy specifies a list of targets where the proxy isn't used, applies only in cases where the proxy is applied"
},
"password": {
"type": "string",
"title": "Password contains the password or PAT used for authenticating at the remote repository"
@@ -8753,6 +8682,43 @@
}
}
},
"v1alpha1ResourceStatus": {
"type": "object",
"title": "ResourceStatus holds the current sync and health status of a resource\nTODO: describe members of this type",
"properties": {
"group": {
"type": "string"
},
"health": {
"$ref": "#/definitions/v1alpha1HealthStatus"
},
"hook": {
"type": "boolean"
},
"kind": {
"type": "string"
},
"name": {
"type": "string"
},
"namespace": {
"type": "string"
},
"requiresPruning": {
"type": "boolean"
},
"status": {
"type": "string"
},
"syncWave": {
"type": "integer",
"format": "int64"
},
"version": {
"type": "string"
}
}
},
"v1alpha1RetryStrategy": {
"type": "object",
"title": "RetryStrategy contains information about the strategy to apply when a sync failed",

View File

@@ -35,7 +35,6 @@ import (
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
appsetmetrics "github.com/argoproj/argo-cd/v2/applicationset/metrics"
"github.com/argoproj/argo-cd/v2/applicationset/services"
appv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
@@ -70,7 +69,6 @@ func NewCommand() *cobra.Command {
allowedScmProviders []string
globalPreservedAnnotations []string
globalPreservedLabels []string
metricsAplicationsetLabels []string
enableScmProviders bool
webhookParallelism int
)
@@ -169,7 +167,7 @@ func NewCommand() *cobra.Command {
tlsConfig := apiclient.TLSConfiguration{
DisableTLS: repoServerPlaintext,
StrictValidation: repoServerStrictTLS,
StrictValidation: repoServerPlaintext,
}
if !repoServerPlaintext && repoServerStrictTLS {
@@ -196,13 +194,6 @@ func NewCommand() *cobra.Command {
startWebhookServer(webhookHandler, webhookAddr)
}
metrics := appsetmetrics.NewApplicationsetMetrics(
utils.NewAppsetLister(mgr.GetClient()),
metricsAplicationsetLabels,
func(appset *appv1alpha1.ApplicationSet) bool {
return utils.IsNamespaceAllowed(applicationSetNamespaces, appset.Namespace)
})
if err = (&controllers.ApplicationSetReconciler{
Generators: topLevelGenerators,
Client: mgr.GetClient(),
@@ -220,7 +211,7 @@ func NewCommand() *cobra.Command {
SCMRootCAPath: scmRootCAPath,
GlobalPreservedAnnotations: globalPreservedAnnotations,
GlobalPreservedLabels: globalPreservedLabels,
Metrics: &metrics,
Cache: mgr.GetCache(),
}).SetupWithManager(mgr, enableProgressiveSyncs, maxConcurrentReconciliations); err != nil {
log.Error(err, "unable to create controller", "controller", "ApplicationSet")
os.Exit(1)
@@ -262,7 +253,6 @@ func NewCommand() *cobra.Command {
command.Flags().StringSliceVar(&globalPreservedAnnotations, "preserved-annotations", env.StringsFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_ANNOTATIONS", []string{}, ","), "Sets global preserved field values for annotations")
command.Flags().StringSliceVar(&globalPreservedLabels, "preserved-labels", env.StringsFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_GLOBAL_PRESERVED_LABELS", []string{}, ","), "Sets global preserved field values for labels")
command.Flags().IntVar(&webhookParallelism, "webhook-parallelism-limit", env.ParseNumFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_WEBHOOK_PARALLELISM_LIMIT", 50, 1, 1000), "Number of webhook requests processed concurrently")
command.Flags().StringSliceVar(&metricsAplicationsetLabels, "metrics-applicationset-labels", []string{}, "List of Application labels that will be added to the argocd_applicationset_labels metric")
return &command
}

View File

@@ -136,8 +136,8 @@ func NewRunDexCommand() *cobra.Command {
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_DEX_SERVER_LOGFORMAT", "text"), "Set the logging format. One of: text|json")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_DEX_SERVER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error")
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "text", "Set the logging format. One of: text|json")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
command.Flags().BoolVar(&disableTLS, "disable-tls", env.ParseBoolFromEnv("ARGOCD_DEX_SERVER_DISABLE_TLS", false), "Disable TLS on the HTTP endpoint")
return &command
}
@@ -204,8 +204,8 @@ func NewGenDexConfigCommand() *cobra.Command {
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_DEX_SERVER_LOGFORMAT", "text"), "Set the logging format. One of: text|json")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_DEX_SERVER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error")
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "text", "Set the logging format. One of: text|json")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
command.Flags().StringVarP(&out, "out", "o", "", "Output to the specified file instead of stdout")
command.Flags().BoolVar(&disableTLS, "disable-tls", env.ParseBoolFromEnv("ARGOCD_DEX_SERVER_DISABLE_TLS", false), "Disable TLS on the HTTP endpoint")
return &command

View File

@@ -159,7 +159,7 @@ func NewCommand() *cobra.Command {
command.Flags().StringVar(&logFormat, "logformat", env.StringFromEnv("ARGOCD_NOTIFICATIONS_CONTROLLER_LOGFORMAT", "text"), "Set the logging format. One of: text|json")
command.Flags().IntVar(&metricsPort, "metrics-port", defaultMetricsPort, "Metrics port")
command.Flags().StringVar(&argocdRepoServer, "argocd-repo-server", common.DefaultRepoServerAddr, "Argo CD repo server address")
command.Flags().BoolVar(&argocdRepoServerPlaintext, "argocd-repo-server-plaintext", env.ParseBoolFromEnv("ARGOCD_NOTIFICATION_CONTROLLER_REPO_SERVER_PLAINTEXT", false), "Use a plaintext client (non-TLS) to connect to repository server")
command.Flags().BoolVar(&argocdRepoServerPlaintext, "argocd-repo-server-plaintext", false, "Use a plaintext client (non-TLS) to connect to repository server")
command.Flags().BoolVar(&argocdRepoServerStrictTLS, "argocd-repo-server-strict-tls", false, "Perform strict validation of TLS certificates when connecting to repo server")
command.Flags().StringVar(&configMapName, "config-map-name", "argocd-notifications-cm", "Set notifications ConfigMap name")
command.Flags().StringVar(&secretName, "secret-name", "argocd-notifications-secret", "Set notifications Secret name")

View File

@@ -8,8 +8,6 @@ import (
"time"
"github.com/redis/go-redis/v9"
"k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"github.com/argoproj/pkg/stats"
log "github.com/sirupsen/logrus"
@@ -144,11 +142,7 @@ func NewCommand() *cobra.Command {
dynamicClient := dynamic.NewForConfigOrDie(config)
scheme := runtime.NewScheme()
_ = clientgoscheme.AddToScheme(scheme)
_ = v1alpha1.AddToScheme(scheme)
controllerClient, err := client.New(config, client.Options{Scheme: scheme})
controllerClient, err := client.New(config, client.Options{})
errors.CheckError(err)
controllerClient = client.NewDryRunClient(controllerClient)

View File

@@ -1,13 +1,10 @@
package admin
import (
"context"
"reflect"
"strings"
"github.com/spf13/cobra"
apiv1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -86,12 +83,11 @@ func newArgoCDClientsets(config *rest.Config, namespace string) *argoCDClientset
dynamicIf, err := dynamic.NewForConfig(config)
errors.CheckError(err)
return &argoCDClientsets{
configMaps: dynamicIf.Resource(configMapResource).Namespace(namespace),
secrets: dynamicIf.Resource(secretResource).Namespace(namespace),
// To support applications and applicationsets in any namespace we will watch all namespaces and filter them afterwards
applications: dynamicIf.Resource(applicationsResource),
configMaps: dynamicIf.Resource(configMapResource).Namespace(namespace),
secrets: dynamicIf.Resource(secretResource).Namespace(namespace),
applications: dynamicIf.Resource(applicationsResource).Namespace(namespace),
projects: dynamicIf.Resource(appprojectsResource).Namespace(namespace),
applicationSets: dynamicIf.Resource(appplicationSetResource),
applicationSets: dynamicIf.Resource(appplicationSetResource).Namespace(namespace),
}
}
@@ -222,52 +218,3 @@ func specsEqual(left, right unstructured.Unstructured) bool {
}
return false
}
type argocdAdditonalNamespaces struct {
applicationNamespaces []string
applicationsetNamespaces []string
}
const (
applicationsetNamespacesCmdParamsKey = "applicationsetcontroller.namespaces"
applicationNamespacesCmdParamsKey = "application.namespaces"
)
// Get additional namespaces from argocd-cmd-params
func getAdditionalNamespaces(ctx context.Context, argocdClientsets *argoCDClientsets) *argocdAdditonalNamespaces {
applicationNamespaces := make([]string, 0)
applicationsetNamespaces := make([]string, 0)
un, err := argocdClientsets.configMaps.Get(ctx, common.ArgoCDCmdParamsConfigMapName, v1.GetOptions{})
errors.CheckError(err)
var cm apiv1.ConfigMap
err = runtime.DefaultUnstructuredConverter.FromUnstructured(un.Object, &cm)
errors.CheckError(err)
namespacesListFromString := func(namespaces string) []string {
listOfNamespaces := []string{}
ss := strings.Split(namespaces, ",")
for _, namespace := range ss {
if namespace != "" {
listOfNamespaces = append(listOfNamespaces, strings.TrimSpace(namespace))
}
}
return listOfNamespaces
}
if strNamespaces, ok := cm.Data[applicationNamespacesCmdParamsKey]; ok {
applicationNamespaces = namespacesListFromString(strNamespaces)
}
if strNamespaces, ok := cm.Data[applicationsetNamespacesCmdParamsKey]; ok {
applicationsetNamespaces = namespacesListFromString(strNamespaces)
}
return &argocdAdditonalNamespaces{
applicationNamespaces: applicationNamespaces,
applicationsetNamespaces: applicationsetNamespaces,
}
}

View File

@@ -1,75 +0,0 @@
package admin
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
dynfake "k8s.io/client-go/dynamic/fake"
)
func TestGetAdditionalNamespaces(t *testing.T) {
createArgoCDCmdCMWithKeys := func(data map[string]interface{}) *unstructured.Unstructured {
return &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "argocd-cmd-params-cm",
"namespace": "argocd",
},
"data": data,
},
}
}
testCases := []struct {
CmdParamsKeys map[string]interface{}
expected argocdAdditonalNamespaces
description string
}{
{
description: "empty configmap should return no additional namespaces",
CmdParamsKeys: map[string]interface{}{},
expected: argocdAdditonalNamespaces{applicationNamespaces: []string{}, applicationsetNamespaces: []string{}},
},
{
description: "empty strings in respective keys in cm shoud return empty namespace list",
CmdParamsKeys: map[string]interface{}{applicationsetNamespacesCmdParamsKey: "", applicationNamespacesCmdParamsKey: ""},
expected: argocdAdditonalNamespaces{applicationNamespaces: []string{}, applicationsetNamespaces: []string{}},
},
{
description: "when only one of the keys in the cm is set only correct respective list of namespaces should be returned",
CmdParamsKeys: map[string]interface{}{applicationNamespacesCmdParamsKey: "foo, bar*"},
expected: argocdAdditonalNamespaces{applicationsetNamespaces: []string{}, applicationNamespaces: []string{"foo", "bar*"}},
},
{
description: "when only one of the keys in the cm is set only correct respective list of namespaces should be returned",
CmdParamsKeys: map[string]interface{}{applicationsetNamespacesCmdParamsKey: "foo, bar*"},
expected: argocdAdditonalNamespaces{applicationNamespaces: []string{}, applicationsetNamespaces: []string{"foo", "bar*"}},
},
{
description: "whitespaces are removed for both multiple and single namespace",
CmdParamsKeys: map[string]interface{}{applicationNamespacesCmdParamsKey: " bar ", applicationsetNamespacesCmdParamsKey: " foo , bar* "},
expected: argocdAdditonalNamespaces{applicationNamespaces: []string{"bar"}, applicationsetNamespaces: []string{"foo", "bar*"}},
},
}
for _, c := range testCases {
fakeDynClient := dynfake.NewSimpleDynamicClient(runtime.NewScheme(), createArgoCDCmdCMWithKeys(c.CmdParamsKeys))
argoCDClientsets := &argoCDClientsets{
configMaps: fakeDynClient.Resource(configMapResource).Namespace("argocd"),
applications: fakeDynClient.Resource(schema.GroupVersionResource{}),
applicationSets: fakeDynClient.Resource(schema.GroupVersionResource{}),
secrets: fakeDynClient.Resource(schema.GroupVersionResource{}),
projects: fakeDynClient.Resource(schema.GroupVersionResource{}),
}
result := getAdditionalNamespaces(context.TODO(), argoCDClientsets)
assert.Equal(t, c.expected, *result)
}
}

View File

@@ -20,16 +20,13 @@ import (
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/errors"
secutil "github.com/argoproj/argo-cd/v2/util/security"
)
// NewExportCommand defines a new command for exporting Kubernetes and Argo CD resources.
func NewExportCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
out string
applicationNamespaces []string
applicationsetNamespaces []string
clientConfig clientcmd.ClientConfig
out string
)
command := cobra.Command{
Use: "export",
@@ -61,47 +58,34 @@ func NewExportCommand() *cobra.Command {
acdClients := newArgoCDClientsets(config, namespace)
acdConfigMap, err := acdClients.configMaps.Get(ctx, common.ArgoCDConfigMapName, v1.GetOptions{})
errors.CheckError(err)
export(writer, *acdConfigMap, namespace)
export(writer, *acdConfigMap)
acdRBACConfigMap, err := acdClients.configMaps.Get(ctx, common.ArgoCDRBACConfigMapName, v1.GetOptions{})
errors.CheckError(err)
export(writer, *acdRBACConfigMap, namespace)
export(writer, *acdRBACConfigMap)
acdKnownHostsConfigMap, err := acdClients.configMaps.Get(ctx, common.ArgoCDKnownHostsConfigMapName, v1.GetOptions{})
errors.CheckError(err)
export(writer, *acdKnownHostsConfigMap, namespace)
export(writer, *acdKnownHostsConfigMap)
acdTLSCertsConfigMap, err := acdClients.configMaps.Get(ctx, common.ArgoCDTLSCertsConfigMapName, v1.GetOptions{})
errors.CheckError(err)
export(writer, *acdTLSCertsConfigMap, namespace)
export(writer, *acdTLSCertsConfigMap)
referencedSecrets := getReferencedSecrets(*acdConfigMap)
secrets, err := acdClients.secrets.List(ctx, v1.ListOptions{})
errors.CheckError(err)
for _, secret := range secrets.Items {
if isArgoCDSecret(referencedSecrets, secret) {
export(writer, secret, namespace)
export(writer, secret)
}
}
projects, err := acdClients.projects.List(ctx, v1.ListOptions{})
errors.CheckError(err)
for _, proj := range projects.Items {
export(writer, proj, namespace)
export(writer, proj)
}
additionalNamespaces := getAdditionalNamespaces(ctx, acdClients)
if len(applicationNamespaces) == 0 {
applicationNamespaces = additionalNamespaces.applicationNamespaces
}
if len(applicationsetNamespaces) == 0 {
applicationsetNamespaces = additionalNamespaces.applicationsetNamespaces
}
applications, err := acdClients.applications.List(ctx, v1.ListOptions{})
errors.CheckError(err)
for _, app := range applications.Items {
// Export application only if it is in one of the enabled namespaces
if secutil.IsNamespaceEnabled(app.GetNamespace(), namespace, applicationNamespaces) {
export(writer, app, namespace)
}
export(writer, app)
}
applicationSets, err := acdClients.applicationSets.List(ctx, v1.ListOptions{})
if err != nil && !apierr.IsNotFound(err) {
@@ -113,9 +97,7 @@ func NewExportCommand() *cobra.Command {
}
if applicationSets != nil {
for _, appSet := range applicationSets.Items {
if secutil.IsNamespaceEnabled(appSet.GetNamespace(), namespace, applicationsetNamespaces) {
export(writer, appSet, namespace)
}
export(writer, appSet)
}
}
},
@@ -123,21 +105,18 @@ func NewExportCommand() *cobra.Command {
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().StringVarP(&out, "out", "o", "-", "Output to the specified file instead of stdout")
command.Flags().StringSliceVarP(&applicationNamespaces, "application-namespaces", "", []string{}, fmt.Sprintf("Comma separated list of namespace globs to export applications from. If not provided value from '%s' in %s will be used,if it's not defined only applications from Argo CD namespace will be exported", applicationNamespacesCmdParamsKey, common.ArgoCDCmdParamsConfigMapName))
command.Flags().StringSliceVarP(&applicationsetNamespaces, "applicationset-namespaces", "", []string{}, fmt.Sprintf("Comma separated list of namespace globs to export applicationsets from. If not provided value from '%s' in %s will be used,if it's not defined only applicationsets from Argo CD namespace will be exported", applicationsetNamespacesCmdParamsKey, common.ArgoCDCmdParamsConfigMapName))
return &command
}
// NewImportCommand defines a new command for exporting Kubernetes and Argo CD resources.
func NewImportCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
prune bool
dryRun bool
verbose bool
stopOperation bool
applicationNamespaces []string
applicationsetNamespaces []string
clientConfig clientcmd.ClientConfig
prune bool
dryRun bool
verbose bool
stopOperation bool
)
command := cobra.Command{
Use: "import SOURCE",
@@ -156,8 +135,6 @@ func NewImportCommand() *cobra.Command {
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
acdClients := newArgoCDClientsets(config, namespace)
client, err := dynamic.NewForConfig(config)
errors.CheckError(err)
var input []byte
if in := args[0]; in == "-" {
@@ -171,15 +148,6 @@ func NewImportCommand() *cobra.Command {
dryRunMsg = " (dry run)"
}
additionalNamespaces := getAdditionalNamespaces(ctx, acdClients)
if len(applicationNamespaces) == 0 {
applicationNamespaces = additionalNamespaces.applicationNamespaces
}
if len(applicationsetNamespaces) == 0 {
applicationsetNamespaces = additionalNamespaces.applicationsetNamespaces
}
// pruneObjects tracks live objects and it's current resource version. any remaining
// items in this map indicates the resource should be pruned since it no longer appears
// in the backup
@@ -191,7 +159,7 @@ func NewImportCommand() *cobra.Command {
var referencedSecrets map[string]bool
for _, cm := range configMaps.Items {
if isArgoCDConfigMap(cm.GetName()) {
pruneObjects[kube.ResourceKey{Group: "", Kind: "ConfigMap", Name: cm.GetName(), Namespace: cm.GetNamespace()}] = cm
pruneObjects[kube.ResourceKey{Group: "", Kind: "ConfigMap", Name: cm.GetName()}] = cm
}
if cm.GetName() == common.ArgoCDConfigMapName {
referencedSecrets = getReferencedSecrets(cm)
@@ -202,20 +170,18 @@ func NewImportCommand() *cobra.Command {
errors.CheckError(err)
for _, secret := range secrets.Items {
if isArgoCDSecret(referencedSecrets, secret) {
pruneObjects[kube.ResourceKey{Group: "", Kind: "Secret", Name: secret.GetName(), Namespace: secret.GetNamespace()}] = secret
pruneObjects[kube.ResourceKey{Group: "", Kind: "Secret", Name: secret.GetName()}] = secret
}
}
applications, err := acdClients.applications.List(ctx, v1.ListOptions{})
errors.CheckError(err)
for _, app := range applications.Items {
if secutil.IsNamespaceEnabled(app.GetNamespace(), namespace, applicationNamespaces) {
pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.ApplicationKind, Name: app.GetName(), Namespace: app.GetNamespace()}] = app
}
pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.ApplicationKind, Name: app.GetName()}] = app
}
projects, err := acdClients.projects.List(ctx, v1.ListOptions{})
errors.CheckError(err)
for _, proj := range projects.Items {
pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.AppProjectKind, Name: proj.GetName(), Namespace: proj.GetNamespace()}] = proj
pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.AppProjectKind, Name: proj.GetName()}] = proj
}
applicationSets, err := acdClients.applicationSets.List(ctx, v1.ListOptions{})
if apierr.IsForbidden(err) || apierr.IsNotFound(err) {
@@ -225,9 +191,7 @@ func NewImportCommand() *cobra.Command {
}
if applicationSets != nil {
for _, appSet := range applicationSets.Items {
if secutil.IsNamespaceEnabled(appSet.GetNamespace(), namespace, applicationsetNamespaces) {
pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.ApplicationSetKind, Name: appSet.GetName(), Namespace: appSet.GetNamespace()}] = appSet
}
pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.ApplicationSetKind, Name: appSet.GetName()}] = appSet
}
}
@@ -236,33 +200,21 @@ func NewImportCommand() *cobra.Command {
errors.CheckError(err)
for _, bakObj := range backupObjects {
gvk := bakObj.GroupVersionKind()
// For objects without namespace, assume they belong in ArgoCD namespace
if bakObj.GetNamespace() == "" {
bakObj.SetNamespace(namespace)
}
key := kube.ResourceKey{Group: gvk.Group, Kind: gvk.Kind, Name: bakObj.GetName(), Namespace: bakObj.GetNamespace()}
key := kube.ResourceKey{Group: gvk.Group, Kind: gvk.Kind, Name: bakObj.GetName()}
liveObj, exists := pruneObjects[key]
delete(pruneObjects, key)
var dynClient dynamic.ResourceInterface
switch bakObj.GetKind() {
case "Secret":
dynClient = client.Resource(secretResource).Namespace(bakObj.GetNamespace())
dynClient = acdClients.secrets
case "ConfigMap":
dynClient = client.Resource(configMapResource).Namespace(bakObj.GetNamespace())
dynClient = acdClients.configMaps
case application.AppProjectKind:
dynClient = client.Resource(appprojectsResource).Namespace(bakObj.GetNamespace())
dynClient = acdClients.projects
case application.ApplicationKind:
dynClient = client.Resource(applicationsResource).Namespace(bakObj.GetNamespace())
// If application is not in one of the allowed namespaces do not import it
if !secutil.IsNamespaceEnabled(bakObj.GetNamespace(), namespace, applicationNamespaces) {
continue
}
dynClient = acdClients.applications
case application.ApplicationSetKind:
dynClient = client.Resource(appplicationSetResource).Namespace(bakObj.GetNamespace())
// If applicationset is not in one of the allowed namespaces do not import it
if !secutil.IsNamespaceEnabled(bakObj.GetNamespace(), namespace, applicationsetNamespaces) {
continue
}
dynClient = acdClients.applicationSets
}
if !exists {
isForbidden := false
@@ -276,7 +228,7 @@ func NewImportCommand() *cobra.Command {
}
}
if !isForbidden {
fmt.Printf("%s/%s %s in namespace %s created%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), bakObj.GetNamespace(), dryRunMsg)
fmt.Printf("%s/%s %s created%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
}
} else if specsEqual(*bakObj, liveObj) && checkAppHasNoNeedToStopOperation(liveObj, stopOperation) {
if verbose {
@@ -295,7 +247,7 @@ func NewImportCommand() *cobra.Command {
}
}
if !isForbidden {
fmt.Printf("%s/%s %s in namespace %s updated%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), bakObj.GetNamespace(), dryRunMsg)
fmt.Printf("%s/%s %s updated%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
}
}
}
@@ -306,11 +258,11 @@ func NewImportCommand() *cobra.Command {
var dynClient dynamic.ResourceInterface
switch key.Kind {
case "Secret":
dynClient = client.Resource(secretResource).Namespace(liveObj.GetNamespace())
dynClient = acdClients.secrets
case application.AppProjectKind:
dynClient = client.Resource(appprojectsResource).Namespace(liveObj.GetNamespace())
dynClient = acdClients.projects
case application.ApplicationKind:
dynClient = client.Resource(applicationsResource).Namespace(liveObj.GetNamespace())
dynClient = acdClients.applications
if !dryRun {
if finalizers := liveObj.GetFinalizers(); len(finalizers) > 0 {
newLive := liveObj.DeepCopy()
@@ -322,7 +274,7 @@ func NewImportCommand() *cobra.Command {
}
}
case application.ApplicationSetKind:
dynClient = client.Resource(appplicationSetResource).Namespace(liveObj.GetNamespace())
dynClient = acdClients.applicationSets
default:
log.Fatalf("Unexpected kind '%s' in prune list", key.Kind)
}
@@ -351,8 +303,6 @@ func NewImportCommand() *cobra.Command {
command.Flags().BoolVar(&prune, "prune", false, "Prune secrets, applications and projects which do not appear in the backup")
command.Flags().BoolVar(&verbose, "verbose", false, "Verbose output (versus only changed output)")
command.Flags().BoolVar(&stopOperation, "stop-operation", false, "Stop any existing operations")
command.Flags().StringSliceVarP(&applicationNamespaces, "application-namespaces", "", []string{}, fmt.Sprintf("Comma separated list of namespace globs to which import of applications is allowed. If not provided value from '%s' in %s will be used,if it's not defined only applications without an explicit namespace will be imported to the Argo CD namespace", applicationNamespacesCmdParamsKey, common.ArgoCDCmdParamsConfigMapName))
command.Flags().StringSliceVarP(&applicationsetNamespaces, "applicationset-namespaces", "", []string{}, fmt.Sprintf("Comma separated list of namespace globs which import of applicationsets is allowed. If not provided value from '%s' in %s will be used,if it's not defined only applicationsets without an explicit namespace will be imported to the Argo CD namespace", applicationsetNamespacesCmdParamsKey, common.ArgoCDCmdParamsConfigMapName))
return &command
}
@@ -370,14 +320,13 @@ func checkAppHasNoNeedToStopOperation(liveObj unstructured.Unstructured, stopOpe
}
// export writes the unstructured object and removes extraneous cruft from output before writing
func export(w io.Writer, un unstructured.Unstructured, argocdNamespace string) {
func export(w io.Writer, un unstructured.Unstructured) {
name := un.GetName()
finalizers := un.GetFinalizers()
apiVersion := un.GetAPIVersion()
kind := un.GetKind()
labels := un.GetLabels()
annotations := un.GetAnnotations()
namespace := un.GetNamespace()
unstructured.RemoveNestedField(un.Object, "metadata")
un.SetName(name)
un.SetFinalizers(finalizers)
@@ -385,9 +334,6 @@ func export(w io.Writer, un unstructured.Unstructured, argocdNamespace string) {
un.SetKind(kind)
un.SetLabels(labels)
un.SetAnnotations(annotations)
if namespace != argocdNamespace {
un.SetNamespace(namespace)
}
data, err := yaml.Marshal(un.Object)
errors.CheckError(err)
_, err = w.Write(data)

View File

@@ -35,8 +35,7 @@ func NewNotificationsCommand() *cobra.Command {
"notifications",
"argocd admin notifications",
applications,
settings.GetFactorySettingsForCLI(&argocdService, "argocd-notifications-secret", "argocd-notifications-cm", false),
func(clientConfig clientcmd.ClientConfig) {
settings.GetFactorySettings(argocdService, "argocd-notifications-secret", "argocd-notifications-cm", false), func(clientConfig clientcmd.ClientConfig) {
k8sCfg, err := clientConfig.ClientConfig()
if err != nil {
log.Fatalf("Failed to parse k8s config: %v", err)

View File

@@ -159,7 +159,7 @@ func NewSettingsCommand() *cobra.Command {
command.AddCommand(NewValidateSettingsCommand(&opts))
command.AddCommand(NewResourceOverridesCommand(&opts))
command.AddCommand(NewRBACCommand(&opts))
command.AddCommand(NewRBACCommand())
opts.clientConfig = cli.AddKubectlFlagsToCmd(command)
command.PersistentFlags().StringVar(&opts.argocdCMPath, "argocd-cm-path", "", "Path to local argocd-cm.yaml file")

View File

@@ -18,7 +18,6 @@ import (
"github.com/argoproj/argo-cd/v2/server/rbacpolicy"
"github.com/argoproj/argo-cd/v2/util/assets"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/rbac"
)
@@ -110,7 +109,7 @@ var extensionActions = actionTraitMap{
}
// NewRBACCommand is the command for 'rbac'
func NewRBACCommand(cmdCtx commandContext) *cobra.Command {
func NewRBACCommand() *cobra.Command {
command := &cobra.Command{
Use: "rbac",
Short: "Validate and test RBAC configuration",
@@ -118,13 +117,13 @@ func NewRBACCommand(cmdCtx commandContext) *cobra.Command {
c.HelpFunc()(c, args)
},
}
command.AddCommand(NewRBACCanCommand(cmdCtx))
command.AddCommand(NewRBACCanCommand())
command.AddCommand(NewRBACValidateCommand())
return command
}
// NewRBACCanCommand is the command for 'rbac can'
func NewRBACCanCommand(cmdCtx commandContext) *cobra.Command {
// NewRBACCanCommand is the command for 'rbac can-role'
func NewRBACCanCommand() *cobra.Command {
var (
policyFile string
defaultRole string
@@ -176,6 +175,11 @@ argocd admin settings rbac can someuser create application 'default/app' --defau
subResource = args[3]
}
userPolicy := ""
builtinPolicy := ""
var newDefaultRole string
namespace, nsOverride, err := clientConfig.Namespace()
if err != nil {
log.Fatalf("could not create k8s client: %v", err)
@@ -199,7 +203,6 @@ argocd admin settings rbac can someuser create application 'default/app' --defau
userPolicy, newDefaultRole, matchMode := getPolicy(ctx, policyFile, realClientset, namespace)
// Use built-in policy as augmentation if requested
builtinPolicy := ""
if useBuiltin {
builtinPolicy = assets.BuiltinPolicyCSV
}
@@ -210,30 +213,7 @@ argocd admin settings rbac can someuser create application 'default/app' --defau
defaultRole = newDefaultRole
}
// Logs RBAC will be enforced only if an internal var serverRBACLogEnforceEnable
// (representing server.rbac.log.enforce.enable env var in argocd-cm)
// is defined and has a "true" value
// Otherwise, no RBAC enforcement for logs will take place (meaning, 'can' request on a logs resource will result in "yes",
// even if there is no explicit RBAC allow, or if there is an explicit RBAC deny)
var isLogRbacEnforced func() bool
if nsOverride && policyFile == "" {
if resolveRBACResourceName(resource) == rbacpolicy.ResourceLogs {
isLogRbacEnforced = func() bool {
if opts, ok := cmdCtx.(*settingsOpts); ok {
opts.loadClusterSettings = true
opts.clientConfig = clientConfig
settingsMgr, err := opts.createSettingsManager(ctx)
errors.CheckError(err)
logEnforceEnable, err := settingsMgr.GetServerRBACLogEnforceEnable()
errors.CheckError(err)
return logEnforceEnable
}
return false
}
}
}
res := checkPolicy(subject, action, resource, subResource, builtinPolicy, userPolicy, defaultRole, matchMode, strict, isLogRbacEnforced)
res := checkPolicy(subject, action, resource, subResource, builtinPolicy, userPolicy, defaultRole, matchMode, strict)
if res {
if !quiet {
fmt.Println("Yes")
@@ -379,16 +359,20 @@ func getPolicyFromFile(policyFile string) (string, string, string, error) {
// Retrieve policy information from a ConfigMap
func getPolicyFromConfigMap(cm *corev1.ConfigMap) (string, string, string) {
var (
userPolicy string
defaultRole string
ok bool
)
userPolicy, ok = cm.Data[rbac.ConfigMapPolicyCSVKey]
if !ok {
userPolicy = ""
}
defaultRole, ok = cm.Data[rbac.ConfigMapPolicyDefaultKey]
if !ok {
defaultRole = ""
}
return rbac.PolicyCSV(cm.Data), defaultRole, cm.Data[rbac.ConfigMapMatchModeKey]
return userPolicy, defaultRole, cm.Data[rbac.ConfigMapMatchModeKey]
}
// getPolicyConfigMap fetches the RBAC config map from K8s cluster
@@ -402,7 +386,7 @@ func getPolicyConfigMap(ctx context.Context, client kubernetes.Interface, namesp
// checkPolicy checks whether given subject is allowed to execute specified
// action against specified resource
func checkPolicy(subject, action, resource, subResource, builtinPolicy, userPolicy, defaultRole, matchMode string, strict bool, isLogRbacEnforced func() bool) bool {
func checkPolicy(subject, action, resource, subResource, builtinPolicy, userPolicy, defaultRole, matchMode string, strict bool) bool {
enf := rbac.NewEnforcer(nil, "argocd", "argocd-rbac-cm", nil)
enf.SetDefaultRole(defaultRole)
enf.SetMatchMode(matchMode)
@@ -443,11 +427,8 @@ func checkPolicy(subject, action, resource, subResource, builtinPolicy, userPoli
if subResource == "*" || subResource == "" {
subResource = "*/*"
}
} else if realResource == rbacpolicy.ResourceLogs {
if isLogRbacEnforced != nil && !isLogRbacEnforced() {
return true
}
}
return enf.Enforce(subject, realResource, action, subResource)
}

View File

@@ -130,16 +130,6 @@ func Test_PolicyFromYAML(t *testing.T) {
require.NotEmpty(t, uPol)
require.Equal(t, "role:unknown", dRole)
require.Empty(t, matchMode)
require.True(t, checkPolicy("my-org:team-qa", "update", "project", "foo",
"", uPol, dRole, matchMode, true, nil))
}
func trueLogRbacEnforce() bool {
return true
}
func falseLogRbacEnforce() bool {
return false
}
func Test_PolicyFromK8s(t *testing.T) {
@@ -163,97 +153,43 @@ func Test_PolicyFromK8s(t *testing.T) {
require.Equal(t, "", matchMode)
t.Run("get applications", func(t *testing.T) {
ok := checkPolicy("role:user", "get", "applications", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil)
ok := checkPolicy("role:user", "get", "applications", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true)
require.True(t, ok)
})
t.Run("get clusters", func(t *testing.T) {
ok := checkPolicy("role:user", "get", "clusters", "*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil)
ok := checkPolicy("role:user", "get", "clusters", "*", assets.BuiltinPolicyCSV, uPol, dRole, "", true)
require.True(t, ok)
})
t.Run("get certificates", func(t *testing.T) {
ok := checkPolicy("role:user", "get", "certificates", "*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil)
ok := checkPolicy("role:user", "get", "certificates", "*", assets.BuiltinPolicyCSV, uPol, dRole, "", true)
require.False(t, ok)
})
t.Run("get certificates by default role", func(t *testing.T) {
ok := checkPolicy("role:user", "get", "certificates", "*", assets.BuiltinPolicyCSV, uPol, "role:readonly", "glob", true, nil)
ok := checkPolicy("role:user", "get", "certificates", "*", assets.BuiltinPolicyCSV, uPol, "role:readonly", "glob", true)
require.True(t, ok)
})
t.Run("get certificates by default role without builtin policy", func(t *testing.T) {
ok := checkPolicy("role:user", "get", "certificates", "*", "", uPol, "role:readonly", "glob", true, nil)
ok := checkPolicy("role:user", "get", "certificates", "*", "", uPol, "role:readonly", "glob", true)
require.False(t, ok)
})
t.Run("use regex match mode instead of glob", func(t *testing.T) {
ok := checkPolicy("role:user", "get", "certificates", ".*", assets.BuiltinPolicyCSV, uPol, "role:readonly", "regex", true, nil)
ok := checkPolicy("role:user", "get", "certificates", ".*", assets.BuiltinPolicyCSV, uPol, "role:readonly", "regex", true)
require.False(t, ok)
})
t.Run("get logs", func(t *testing.T) {
ok := checkPolicy("role:test", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil)
require.True(t, ok)
})
// no function is provided to check if logs rbac is enforced or not, so the policy permissions are queried to determine if no-such-user can get logs
t.Run("no-such-user get logs", func(t *testing.T) {
ok := checkPolicy("no-such-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil)
require.False(t, ok)
})
// logs rbac policy is enforced, and no-such-user is not granted logs permission in user policy, so the result should be false (cannot get logs)
t.Run("no-such-user get logs rbac enforced", func(t *testing.T) {
ok := checkPolicy("no-such-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, trueLogRbacEnforce)
require.False(t, ok)
})
// no-such-user is not granted logs permission in user policy, but logs rbac policy is not enforced, so logs permission is open to all
t.Run("no-such-user get logs rbac not enforced", func(t *testing.T) {
ok := checkPolicy("no-such-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, falseLogRbacEnforce)
require.True(t, ok)
})
// no function is provided to check if logs rbac is enforced or not, so the policy permissions are queried to determine if log-deny-user can get logs
t.Run("log-deny-user get logs", func(t *testing.T) {
ok := checkPolicy("log-deny-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil)
require.False(t, ok)
})
// logs rbac policy is enforced, and log-deny-user is denied logs permission in user policy, so the result should be false (cannot get logs)
t.Run("log-deny-user get logs rbac enforced", func(t *testing.T) {
ok := checkPolicy("log-deny-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, trueLogRbacEnforce)
require.False(t, ok)
})
// log-deny-user is denied logs permission in user policy, but logs rbac policy is not enforced, so logs permission is open to all
t.Run("log-deny-user get logs rbac not enforced", func(t *testing.T) {
ok := checkPolicy("log-deny-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, falseLogRbacEnforce)
require.True(t, ok)
})
// no function is provided to check if logs rbac is enforced or not, so the policy permissions are queried to determine if log-allow-user can get logs
t.Run("log-allow-user get logs", func(t *testing.T) {
ok := checkPolicy("log-allow-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil)
require.True(t, ok)
})
// logs rbac policy is enforced, and log-allow-user is granted logs permission in user policy, so the result should be true (can get logs)
t.Run("log-allow-user get logs rbac enforced", func(t *testing.T) {
ok := checkPolicy("log-allow-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, trueLogRbacEnforce)
require.True(t, ok)
})
// log-allow-user is granted logs permission in user policy, and logs rbac policy is not enforced, so logs permission is open to all
t.Run("log-allow-user get logs rbac not enforced", func(t *testing.T) {
ok := checkPolicy("log-allow-user", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, falseLogRbacEnforce)
ok := checkPolicy("role:test", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true)
require.True(t, ok)
})
t.Run("create exec", func(t *testing.T) {
ok := checkPolicy("role:test", "create", "exec", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil)
ok := checkPolicy("role:test", "create", "exec", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true)
require.True(t, ok)
})
t.Run("create applicationsets", func(t *testing.T) {
ok := checkPolicy("role:user", "create", "applicationsets", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil)
require.True(t, ok)
})
// trueLogRbacEnforce or falseLogRbacEnforce should not affect non-logs resources
t.Run("create applicationsets with trueLogRbacEnforce", func(t *testing.T) {
ok := checkPolicy("role:user", "create", "applicationsets", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, trueLogRbacEnforce)
require.True(t, ok)
})
t.Run("create applicationsets with falseLogRbacEnforce", func(t *testing.T) {
ok := checkPolicy("role:user", "create", "applicationsets", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, trueLogRbacEnforce)
ok := checkPolicy("role:user", "create", "applicationsets", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true)
require.True(t, ok)
})
t.Run("delete applicationsets", func(t *testing.T) {
ok := checkPolicy("role:user", "delete", "applicationsets", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil)
ok := checkPolicy("role:user", "delete", "applicationsets", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true)
require.True(t, ok)
})
}
@@ -293,49 +229,49 @@ p, role:readonly, certificates, get, .*, allow
p, role:, certificates, get, .*, allow`
t.Run("get applications", func(t *testing.T) {
ok := checkPolicy("role:user", "get", "applications", ".*/.*", builtInPolicy, uPol, dRole, "regex", true, nil)
ok := checkPolicy("role:user", "get", "applications", ".*/.*", builtInPolicy, uPol, dRole, "regex", true)
require.True(t, ok)
})
t.Run("get clusters", func(t *testing.T) {
ok := checkPolicy("role:user", "get", "clusters", ".*", builtInPolicy, uPol, dRole, "regex", true, nil)
ok := checkPolicy("role:user", "get", "clusters", ".*", builtInPolicy, uPol, dRole, "regex", true)
require.True(t, ok)
})
t.Run("get certificates", func(t *testing.T) {
ok := checkPolicy("role:user", "get", "certificates", ".*", builtInPolicy, uPol, dRole, "regex", true, nil)
ok := checkPolicy("role:user", "get", "certificates", ".*", builtInPolicy, uPol, dRole, "regex", true)
require.False(t, ok)
})
t.Run("get certificates by default role", func(t *testing.T) {
ok := checkPolicy("role:user", "get", "certificates", ".*", builtInPolicy, uPol, "role:readonly", "regex", true, nil)
ok := checkPolicy("role:user", "get", "certificates", ".*", builtInPolicy, uPol, "role:readonly", "regex", true)
require.True(t, ok)
})
t.Run("get certificates by default role without builtin policy", func(t *testing.T) {
ok := checkPolicy("role:user", "get", "certificates", ".*", "", uPol, "role:readonly", "regex", true, nil)
ok := checkPolicy("role:user", "get", "certificates", ".*", "", uPol, "role:readonly", "regex", true)
require.False(t, ok)
})
t.Run("use glob match mode instead of regex", func(t *testing.T) {
ok := checkPolicy("role:user", "get", "certificates", ".+", builtInPolicy, uPol, dRole, "glob", true, nil)
ok := checkPolicy("role:user", "get", "certificates", ".+", builtInPolicy, uPol, dRole, "glob", true)
require.False(t, ok)
})
t.Run("get logs via glob match mode", func(t *testing.T) {
ok := checkPolicy("role:user", "get", "logs", ".*/.*", builtInPolicy, uPol, dRole, "glob", true, nil)
ok := checkPolicy("role:user", "get", "logs", ".*/.*", builtInPolicy, uPol, dRole, "glob", true)
require.True(t, ok)
})
t.Run("create exec", func(t *testing.T) {
ok := checkPolicy("role:user", "create", "exec", ".*/.*", builtInPolicy, uPol, dRole, "regex", true, nil)
ok := checkPolicy("role:user", "create", "exec", ".*/.*", builtInPolicy, uPol, dRole, "regex", true)
require.True(t, ok)
})
t.Run("create applicationsets", func(t *testing.T) {
ok := checkPolicy("role:user", "create", "applicationsets", ".*/.*", builtInPolicy, uPol, dRole, "regex", true, nil)
ok := checkPolicy("role:user", "create", "applicationsets", ".*/.*", builtInPolicy, uPol, dRole, "regex", true)
require.True(t, ok)
})
t.Run("delete applicationsets", func(t *testing.T) {
ok := checkPolicy("role:user", "delete", "applicationsets", ".*/.*", builtInPolicy, uPol, dRole, "regex", true, nil)
ok := checkPolicy("role:user", "delete", "applicationsets", ".*/.*", builtInPolicy, uPol, dRole, "regex", true)
require.True(t, ok)
})
}
func TestNewRBACCanCommand(t *testing.T) {
command := NewRBACCanCommand(&settingsOpts{})
command := NewRBACCanCommand()
require.NotNil(t, command)
assert.Equal(t, "can", command.Name())

View File

@@ -12,10 +12,6 @@ data:
p, role:user, applicationsets, delete, */*, allow
p, role:user, logs, get, */*, allow
g, test, role:user
policy.overlay.csv: |
p, role:tester, applications, *, */*, allow
p, role:tester, projects, *, *, allow
g, my-org:team-qa, role:tester
policy.default: role:unknown
kind: ConfigMap
metadata:

View File

@@ -10,6 +10,4 @@ p, role:user, applicationsets, delete, */*, allow
p, role:test, certificates, get, *, allow
p, role:test, logs, get, */*, allow
p, role:test, exec, create, */*, allow
p, log-allow-user, logs, get, */*, allow
p, log-deny-user, logs, get, */*, deny
g, test, role:user
1 p role:user clusters get * allow
10 p role:test certificates get * allow
11 p role:test logs get */* allow
12 p role:test exec create */* allow
p log-allow-user logs get */* allow
p log-deny-user logs get */* deny
13 g test role:user

View File

@@ -2849,7 +2849,6 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob
errors.CheckError(err)
proj := getProject(c, clientOpts, ctx, app.Spec.Project)
// nolint:staticcheck
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(sourcePositions) > 0 {
q := application.ApplicationManifestQuery{

View File

@@ -36,7 +36,7 @@ func TestPrintTreeViewAppResources(t *testing.T) {
buf := &bytes.Buffer{}
w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0)
printTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, w)
printTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, false, false, w)
if err := w.Flush(); err != nil {
t.Fatal(err)
}
@@ -77,7 +77,7 @@ func TestPrintTreeViewDetailedAppResources(t *testing.T) {
buf := &bytes.Buffer{}
w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0)
printDetailedTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, w)
printDetailedTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, false, false, w)
if err := w.Flush(); err != nil {
t.Fatal(err)
}

View File

@@ -175,25 +175,25 @@ func parentChildInfo(nodes []v1alpha1.ResourceNode) (map[string]v1alpha1.Resourc
return mapUidToNode, mapParentToChild, parentNode
}
func printDetailedTreeViewAppResourcesNotOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, w *tabwriter.Writer) {
func printDetailedTreeViewAppResourcesNotOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, orphaned bool, listAll bool, w *tabwriter.Writer) {
for uid := range parentNodes {
detailedTreeViewAppResourcesNotOrphaned("", nodeMapping, parentChildMapping, nodeMapping[uid], w)
}
}
func printDetailedTreeViewAppResourcesOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, w *tabwriter.Writer) {
func printDetailedTreeViewAppResourcesOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, orphaned bool, listAll bool, w *tabwriter.Writer) {
for uid := range parentNodes {
detailedTreeViewAppResourcesOrphaned("", nodeMapping, parentChildMapping, nodeMapping[uid], w)
}
}
func printTreeViewAppResourcesNotOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, w *tabwriter.Writer) {
func printTreeViewAppResourcesNotOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, orphaned bool, listAll bool, w *tabwriter.Writer) {
for uid := range parentNodes {
treeViewAppResourcesNotOrphaned("", nodeMapping, parentChildMapping, nodeMapping[uid], w)
}
}
func printTreeViewAppResourcesOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, w *tabwriter.Writer) {
func printTreeViewAppResourcesOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, orphaned bool, listAll bool, w *tabwriter.Writer) {
for uid := range parentNodes {
treeViewAppResourcesOrphaned("", nodeMapping, parentChildMapping, nodeMapping[uid], w)
}
@@ -206,24 +206,24 @@ func printResources(listAll bool, orphaned bool, appResourceTree *v1alpha1.Appli
if !orphaned || listAll {
mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.Nodes)
printDetailedTreeViewAppResourcesNotOrphaned(mapUidToNode, mapParentToChild, parentNode, w)
printDetailedTreeViewAppResourcesNotOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w)
}
if orphaned || listAll {
mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.OrphanedNodes)
printDetailedTreeViewAppResourcesOrphaned(mapUidToNode, mapParentToChild, parentNode, w)
printDetailedTreeViewAppResourcesOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w)
}
} else if output == "tree" {
fmt.Fprintf(w, "GROUP\tKIND\tNAMESPACE\tNAME\tORPHANED\n")
if !orphaned || listAll {
mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.Nodes)
printTreeViewAppResourcesNotOrphaned(mapUidToNode, mapParentToChild, parentNode, w)
printTreeViewAppResourcesNotOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w)
}
if orphaned || listAll {
mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.OrphanedNodes)
printTreeViewAppResourcesOrphaned(mapUidToNode, mapParentToChild, parentNode, w)
printTreeViewAppResourcesOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w)
}
} else {
headers := []interface{}{"GROUP", "KIND", "NAMESPACE", "NAME", "ORPHANED"}

View File

@@ -1335,14 +1335,6 @@ func TestFilterAppResources(t *testing.T) {
Namespace: "",
Exclude: true,
}
// apps:ReplicaSet:*
includeAllReplicaSetResource = v1alpha1.SyncOperationResource{
Group: "apps",
Kind: "ReplicaSet",
Name: "*",
Namespace: "",
Exclude: false,
}
// apps:ReplicaSet:replicaSet-name1
includeReplicaSet1Resource = v1alpha1.SyncOperationResource{
Group: "apps",
@@ -1415,13 +1407,13 @@ func TestFilterAppResources(t *testing.T) {
{
testName: "Include ReplicaSet replicaSet-name1 resource and exclude all service resources",
selectedResources: []*v1alpha1.SyncOperationResource{&excludeAllServiceResources, &includeReplicaSet1Resource},
expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1},
expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &deployment},
},
// --resource !apps:ReplicaSet:replicaSet-name2 --resource !*:Service:*
{
testName: "Exclude ReplicaSet replicaSet-name2 resource and all service resources",
selectedResources: []*v1alpha1.SyncOperationResource{&excludeReplicaSet2Resource, &excludeAllServiceResources},
expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &job, &deployment},
expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &service1, &service2, &deployment},
},
// --resource !apps:ReplicaSet:replicaSet-name2
{
@@ -1435,12 +1427,6 @@ func TestFilterAppResources(t *testing.T) {
selectedResources: []*v1alpha1.SyncOperationResource{&includeReplicaSet1Resource},
expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1},
},
// --resource apps:ReplicaSet:* --resource !apps:ReplicaSet:replicaSet-name2
{
testName: "Include All ReplicaSet resource and exclude replicaSet-name1 resource",
selectedResources: []*v1alpha1.SyncOperationResource{&includeAllReplicaSetResource, &excludeReplicaSet2Resource},
expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1},
},
// --resource !*:Service:*
{
testName: "Exclude Service resources",

View File

@@ -11,7 +11,6 @@ import (
"github.com/spf13/cobra"
"google.golang.org/grpc/codes"
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/admin"
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless"
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
@@ -54,7 +53,6 @@ func NewAppSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
command.AddCommand(NewApplicationSetCreateCommand(clientOpts))
command.AddCommand(NewApplicationSetListCommand(clientOpts))
command.AddCommand(NewApplicationSetDeleteCommand(clientOpts))
command.AddCommand(NewApplicationSetGenerateCommand(clientOpts))
return command
}
@@ -210,75 +208,6 @@ func NewApplicationSetCreateCommand(clientOpts *argocdclient.ClientOptions) *cob
return command
}
// NewApplicationSetGenerateCommand returns a new instance of an `argocd appset generate` command
func NewApplicationSetGenerateCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var output string
command := &cobra.Command{
Use: "generate",
Short: "Generate apps of ApplicationSet rendered templates",
Example: templates.Examples(`
# Generate apps of ApplicationSet rendered templates
argocd appset generate <filename or URL> (<filename or URL>...)
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
if len(args) == 0 {
c.HelpFunc()(c, args)
os.Exit(1)
}
argocdClient := headless.NewClientOrDie(clientOpts, c)
fileUrl := args[0]
appsets, err := cmdutil.ConstructApplicationSet(fileUrl)
errors.CheckError(err)
if len(appsets) != 1 {
fmt.Printf("Input file must contain one ApplicationSet")
os.Exit(1)
}
appset := appsets[0]
if appset.Name == "" {
err := fmt.Errorf("Error generating apps for ApplicationSet %s. ApplicationSet does not have Name field set", appset)
errors.CheckError(err)
}
conn, appIf := argocdClient.NewApplicationSetClientOrDie()
defer argoio.Close(conn)
req := applicationset.ApplicationSetGenerateRequest{
ApplicationSet: appset,
}
resp, err := appIf.Generate(ctx, &req)
errors.CheckError(err)
var appsList []arogappsetv1.Application
for i := range resp.Applications {
appsList = append(appsList, *resp.Applications[i])
}
switch output {
case "yaml", "json":
var resources []interface{}
for i := range appsList {
app := appsList[i]
// backfill api version and kind because k8s client always return empty values for these fields
app.APIVersion = arogappsetv1.ApplicationSchemaGroupVersionKind.GroupVersion().String()
app.Kind = arogappsetv1.ApplicationSchemaGroupVersionKind.Kind
resources = append(resources, app)
}
cobra.CheckErr(admin.PrintResources(output, os.Stdout, resources...))
case "wide", "":
printApplicationTable(appsList, &output)
default:
errors.CheckError(fmt.Errorf("unknown output format: %s", output))
}
},
}
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide")
return command
}
// NewApplicationSetListCommand returns a new instance of an `argocd appset list` command
func NewApplicationSetListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (

View File

@@ -34,10 +34,6 @@ const (
clusterFieldName = "name"
// cluster field is 'namespaces'
clusterFieldNamespaces = "namespaces"
// cluster field is 'labels'
clusterFieldLabel = "labels"
// cluster field is 'annotations'
clusterFieldAnnotation = "annotations"
// indicates managing all namespaces
allNamespaces = "*"
)
@@ -224,8 +220,6 @@ func NewClusterSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
var (
clusterOptions cmdutil.ClusterOptions
clusterName string
labels []string
annotations []string
)
command := &cobra.Command{
Use: "set NAME",
@@ -244,25 +238,17 @@ func NewClusterSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
conn, clusterIf := headless.NewClientOrDie(clientOpts, c).NewClusterClientOrDie()
defer io.Close(conn)
// checks the fields that needs to be updated
updatedFields := checkFieldsToUpdate(clusterOptions, labels, annotations)
updatedFields := checkFieldsToUpdate(clusterOptions)
namespaces := clusterOptions.Namespaces
// check if all namespaces have to be considered
if len(namespaces) == 1 && strings.EqualFold(namespaces[0], allNamespaces) {
namespaces[0] = ""
}
// parse the labels you're receiving from the label flag
labelsMap, err := label.Parse(labels)
errors.CheckError(err)
// parse the annotations you're receiving from the annotation flag
annotationsMap, err := label.Parse(annotations)
errors.CheckError(err)
if updatedFields != nil {
clusterUpdateRequest := clusterpkg.ClusterUpdateRequest{
Cluster: &argoappv1.Cluster{
Name: clusterOptions.Name,
Namespaces: namespaces,
Labels: labelsMap,
Annotations: annotationsMap,
Name: clusterOptions.Name,
Namespaces: namespaces,
},
UpdatedFields: updatedFields,
Id: &clusterpkg.ClusterID{
@@ -280,13 +266,11 @@ func NewClusterSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
}
command.Flags().StringVar(&clusterOptions.Name, "name", "", "Overwrite the cluster name")
command.Flags().StringArrayVar(&clusterOptions.Namespaces, "namespace", nil, "List of namespaces which are allowed to manage. Specify '*' to manage all namespaces")
command.Flags().StringArrayVar(&labels, "label", nil, "Set metadata labels (e.g. --label key=value)")
command.Flags().StringArrayVar(&annotations, "annotation", nil, "Set metadata annotations (e.g. --annotation key=value)")
return command
}
// checkFieldsToUpdate returns the fields that needs to be updated
func checkFieldsToUpdate(clusterOptions cmdutil.ClusterOptions, labels []string, annotations []string) []string {
func checkFieldsToUpdate(clusterOptions cmdutil.ClusterOptions) []string {
var updatedFields []string
if clusterOptions.Name != "" {
updatedFields = append(updatedFields, clusterFieldName)
@@ -294,12 +278,6 @@ func checkFieldsToUpdate(clusterOptions cmdutil.ClusterOptions, labels []string,
if clusterOptions.Namespaces != nil {
updatedFields = append(updatedFields, clusterFieldNamespaces)
}
if labels != nil {
updatedFields = append(updatedFields, clusterFieldLabel)
}
if annotations != nil {
updatedFields = append(updatedFields, clusterFieldAnnotation)
}
return updatedFields
}
@@ -363,7 +341,6 @@ func printClusterDetails(clusters []argoappv1.Cluster) {
fmt.Printf("Cluster information\n\n")
fmt.Printf(" Server URL: %s\n", cluster.Server)
fmt.Printf(" Server Name: %s\n", strWithDefault(cluster.Name, "-"))
// nolint:staticcheck
fmt.Printf(" Server Version: %s\n", cluster.ServerVersion)
fmt.Printf(" Namespaces: %s\n", formatNamespaces(cluster))
fmt.Printf("\nTLS configuration\n\n")
@@ -456,7 +433,6 @@ func printClusterTable(clusters []argoappv1.Cluster) {
if len(c.Namespaces) > 0 {
server = fmt.Sprintf("%s (%d namespaces)", c.Server, len(c.Namespaces))
}
// nolint:staticcheck
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", server, c.Name, c.ServerVersion, c.ConnectionState.Status, c.ConnectionState.Message, c.Project)
}
_ = w.Flush()

View File

@@ -178,7 +178,6 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
repoOpts.Repo.GithubAppInstallationId = repoOpts.GithubAppInstallationId
repoOpts.Repo.GitHubAppEnterpriseBaseURL = repoOpts.GitHubAppEnterpriseBaseURL
repoOpts.Repo.Proxy = repoOpts.Proxy
repoOpts.Repo.NoProxy = repoOpts.NoProxy
repoOpts.Repo.ForceHttpBasicAuth = repoOpts.ForceHttpBasicAuth
if repoOpts.Repo.Type == "helm" && repoOpts.Repo.Name == "" {

View File

@@ -80,7 +80,6 @@ func NewCommand() *cobra.Command {
command.PersistentFlags().StringVar(&clientOpts.PortForwardNamespace, "port-forward-namespace", config.GetFlag("port-forward-namespace", ""), "Namespace name which should be used for port forwarding")
command.PersistentFlags().IntVar(&clientOpts.HttpRetryMax, "http-retry-max", config.GetIntFlag("http-retry-max", 0), "Maximum number of retries to establish http connection to Argo CD server")
command.PersistentFlags().BoolVar(&clientOpts.Core, "core", config.GetBoolFlag("core"), "If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server")
command.PersistentFlags().StringVar(&clientOpts.Context, "argocd-context", "", "The name of the Argo-CD server context to use")
command.PersistentFlags().StringVar(&clientOpts.ServerName, "server-name", env.StringFromEnv(common.EnvServerName, common.DefaultServerName), fmt.Sprintf("Name of the Argo CD API server; set this or the %s environment variable when the server's name label differs from the default, for example when installing via the Helm chart", common.EnvServerName))
command.PersistentFlags().StringVar(&clientOpts.AppControllerName, "controller-name", env.StringFromEnv(common.EnvAppControllerName, common.DefaultApplicationControllerName), fmt.Sprintf("Name of the Argo CD Application controller; set this or the %s environment variable when the controller's name label differs from the default, for example when installing via the Helm chart", common.EnvAppControllerName))
command.PersistentFlags().StringVar(&clientOpts.RedisHaProxyName, "redis-haproxy-name", env.StringFromEnv(common.EnvRedisHaProxyName, common.DefaultRedisHaProxyName), fmt.Sprintf("Name of the Redis HA Proxy; set this or the %s environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart", common.EnvRedisHaProxyName))

View File

@@ -6,8 +6,6 @@ import (
"github.com/spf13/cobra"
_ "go.uber.org/automaxprocs"
appcontroller "github.com/argoproj/argo-cd/v2/cmd/argocd-application-controller/commands"
applicationset "github.com/argoproj/argo-cd/v2/cmd/argocd-applicationset-controller/commands"
cmpserver "github.com/argoproj/argo-cd/v2/cmd/argocd-cmp-server/commands"

View File

@@ -22,7 +22,6 @@ type RepoOptions struct {
GithubAppPrivateKeyPath string
GitHubAppEnterpriseBaseURL string
Proxy string
NoProxy string
GCPServiceAccountKeyPath string
ForceHttpBasicAuth bool
}
@@ -45,7 +44,6 @@ func AddRepoFlags(command *cobra.Command, opts *RepoOptions) {
command.Flags().StringVar(&opts.GithubAppPrivateKeyPath, "github-app-private-key-path", "", "private key of the GitHub Application")
command.Flags().StringVar(&opts.GitHubAppEnterpriseBaseURL, "github-app-enterprise-base-url", "", "base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3")
command.Flags().StringVar(&opts.Proxy, "proxy", "", "use proxy to access repository")
command.Flags().StringVar(&opts.Proxy, "no-proxy", "", "don't access these targets via proxy")
command.Flags().StringVar(&opts.GCPServiceAccountKeyPath, "gcp-service-account-key-path", "", "service account key for the Google Cloud Platform")
command.Flags().BoolVar(&opts.ForceHttpBasicAuth, "force-http-basic-auth", false, "whether to force use of basic auth when connecting repository via HTTP")
}

View File

@@ -46,7 +46,6 @@ const (
ArgoCDGPGKeysConfigMapName = "argocd-gpg-keys-cm"
// ArgoCDAppControllerShardConfigMapName contains the application controller to shard mapping
ArgoCDAppControllerShardConfigMapName = "argocd-app-controller-shard-cm"
ArgoCDCmdParamsConfigMapName = "argocd-cmd-params-cm"
)
// Some default configurables
@@ -222,7 +221,7 @@ const (
EnvGitRetryMaxDuration = "ARGOCD_GIT_RETRY_MAX_DURATION"
// EnvGitRetryDuration specifies duration of git remote operation retry
EnvGitRetryDuration = "ARGOCD_GIT_RETRY_DURATION"
// EnvGitRetryFactor specifies factor of git remote operation retry
// EnvGitRetryFactor specifies fator of git remote operation retry
EnvGitRetryFactor = "ARGOCD_GIT_RETRY_FACTOR"
// EnvGitSubmoduleEnabled overrides git submodule support, true by default
EnvGitSubmoduleEnabled = "ARGOCD_GIT_MODULES_ENABLED"

View File

@@ -116,11 +116,11 @@ type ApplicationController struct {
applicationClientset appclientset.Interface
auditLogger *argo.AuditLogger
// queue contains app namespace/name
appRefreshQueue workqueue.TypedRateLimitingInterface[string]
appRefreshQueue workqueue.RateLimitingInterface
// queue contains app namespace/name/comparisonType and used to request app refresh with the predefined comparison type
appComparisonTypeRefreshQueue workqueue.TypedRateLimitingInterface[string]
appOperationQueue workqueue.TypedRateLimitingInterface[string]
projectRefreshQueue workqueue.TypedRateLimitingInterface[string]
appComparisonTypeRefreshQueue workqueue.RateLimitingInterface
appOperationQueue workqueue.RateLimitingInterface
projectRefreshQueue workqueue.RateLimitingInterface
appInformer cache.SharedIndexInformer
appLister applisters.ApplicationLister
projInformer cache.SharedIndexInformer
@@ -186,10 +186,10 @@ func NewApplicationController(
kubectl: kubectl,
applicationClientset: applicationClientset,
repoClientset: repoClientset,
appRefreshQueue: workqueue.NewTypedRateLimitingQueueWithConfig(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), workqueue.TypedRateLimitingQueueConfig[string]{Name: "app_reconciliation_queue"}),
appOperationQueue: workqueue.NewTypedRateLimitingQueueWithConfig(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), workqueue.TypedRateLimitingQueueConfig[string]{Name: "app_operation_processing_queue"}),
projectRefreshQueue: workqueue.NewTypedRateLimitingQueueWithConfig(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), workqueue.TypedRateLimitingQueueConfig[string]{Name: "project_reconciliation_queue"}),
appComparisonTypeRefreshQueue: workqueue.NewTypedRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig)),
appRefreshQueue: workqueue.NewRateLimitingQueueWithConfig(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), workqueue.RateLimitingQueueConfig{Name: "app_reconciliation_queue"}),
appOperationQueue: workqueue.NewRateLimitingQueueWithConfig(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), workqueue.RateLimitingQueueConfig{Name: "app_operation_processing_queue"}),
projectRefreshQueue: workqueue.NewRateLimitingQueueWithConfig(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), workqueue.RateLimitingQueueConfig{Name: "project_reconciliation_queue"}),
appComparisonTypeRefreshQueue: workqueue.NewRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig)),
db: db,
statusRefreshTimeout: appResyncPeriod,
statusHardRefreshTimeout: appHardResyncPeriod,
@@ -940,7 +940,7 @@ func (ctrl *ApplicationController) processAppOperationQueueItem() (processNext b
ctrl.appOperationQueue.Done(appKey)
}()
obj, exists, err := ctrl.appInformer.GetIndexer().GetByKey(appKey)
obj, exists, err := ctrl.appInformer.GetIndexer().GetByKey(appKey.(string))
if err != nil {
log.Errorf("Failed to get application '%s' from informer index: %+v", appKey, err)
return
@@ -1012,8 +1012,8 @@ func (ctrl *ApplicationController) processAppComparisonTypeQueueItem() (processN
return
}
if parts := strings.Split(key, "/"); len(parts) != 3 {
log.Warnf("Unexpected key format in appComparisonTypeRefreshTypeQueue. Key should consists of namespace/name/comparisonType but got: %s", key)
if parts := strings.Split(key.(string), "/"); len(parts) != 3 {
log.Warnf("Unexpected key format in appComparisonTypeRefreshTypeQueue. Key should consists of namespace/name/comparisonType but got: %s", key.(string))
} else {
if compareWith, err := strconv.Atoi(parts[2]); err != nil {
log.Warnf("Unable to parse comparison type: %v", err)
@@ -1039,7 +1039,7 @@ func (ctrl *ApplicationController) processProjectQueueItem() (processNext bool)
processNext = false
return
}
obj, exists, err := ctrl.projInformer.GetIndexer().GetByKey(key)
obj, exists, err := ctrl.projInformer.GetIndexer().GetByKey(key.(string))
if err != nil {
log.Errorf("Failed to get project '%s' from informer index: %+v", key, err)
return
@@ -1551,7 +1551,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
ctrl.appOperationQueue.AddRateLimited(appKey)
ctrl.appRefreshQueue.Done(appKey)
}()
obj, exists, err := ctrl.appInformer.GetIndexer().GetByKey(appKey)
obj, exists, err := ctrl.appInformer.GetIndexer().GetByKey(appKey.(string))
if err != nil {
log.Errorf("Failed to get application '%s' from informer index: %+v", appKey, err)
return
@@ -1957,18 +1957,11 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *
}
}
selfHeal := app.Spec.SyncPolicy.Automated.SelfHeal
// Multi-Source Apps with selfHeal disabled should not trigger an autosync if
// the last sync revision and the new sync revision is the same.
if app.Spec.HasMultipleSources() && !selfHeal && reflect.DeepEqual(app.Status.Sync.Revisions, syncStatus.Revisions) {
logCtx.Infof("Skipping auto-sync: selfHeal disabled and sync caused by object update")
return nil, 0
}
desiredCommitSHA := syncStatus.Revision
desiredCommitSHAsMS := syncStatus.Revisions
alreadyAttempted, attemptPhase := alreadyAttemptedSync(app, desiredCommitSHA, desiredCommitSHAsMS, app.Spec.HasMultipleSources())
ts.AddCheckpoint("already_attempted_sync_ms")
selfHeal := app.Spec.SyncPolicy.Automated.SelfHeal
op := appv1.Operation{
Sync: &appv1.SyncOperation{
Revision: desiredCommitSHA,
@@ -2116,7 +2109,7 @@ func (ctrl *ApplicationController) shouldSelfHeal(app *appv1.Application) (bool,
// isAppNamespaceAllowed returns whether the application is allowed in the
// namespace it's residing in.
func (ctrl *ApplicationController) isAppNamespaceAllowed(app *appv1.Application) bool {
return app.Namespace == ctrl.namespace || glob.MatchStringInList(ctrl.applicationNamespaces, app.Namespace, glob.REGEXP)
return app.Namespace == ctrl.namespace || glob.MatchStringInList(ctrl.applicationNamespaces, app.Namespace, false)
}
func (ctrl *ApplicationController) canProcessApp(obj interface{}) bool {

View File

@@ -563,42 +563,6 @@ func TestAutoSync(t *testing.T) {
assert.False(t, app.Operation.Sync.Prune)
}
func TestMultiSourceSelfHeal(t *testing.T) {
// Simulate OutOfSync caused by object change in cluster
// So our Sync Revisions and SyncStatus Revisions should deep equal
t.Run("ClusterObjectChangeShouldNotTriggerAutoSync", func(t *testing.T) {
app := newFakeMultiSourceApp()
app.Spec.SyncPolicy.Automated.SelfHeal = false
app.Status.Sync.Revisions = []string{"z", "x", "v"}
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil)
syncStatus := v1alpha1.SyncStatus{
Status: v1alpha1.SyncStatusCodeOutOfSync,
Revisions: []string{"z", "x", "v"},
}
cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{{Name: "guestbook-1", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync}})
assert.Nil(t, cond)
app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get(context.Background(), "my-app", metav1.GetOptions{})
require.NoError(t, err)
assert.Nil(t, app.Operation)
})
t.Run("NewRevisionChangeShouldTriggerAutoSync", func(t *testing.T) {
app := newFakeMultiSourceApp()
app.Spec.SyncPolicy.Automated.SelfHeal = false
app.Status.Sync.Revisions = []string{"a", "b", "c"}
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil)
syncStatus := v1alpha1.SyncStatus{
Status: v1alpha1.SyncStatusCodeOutOfSync,
Revisions: []string{"z", "x", "v"},
}
cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{{Name: "guestbook-1", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync}})
assert.Nil(t, cond)
app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get(context.Background(), "my-app", metav1.GetOptions{})
require.NoError(t, err)
assert.NotNil(t, app.Operation)
})
}
func TestAutoSyncNotAllowEmpty(t *testing.T) {
app := newFakeApp()
app.Spec.SyncPolicy.Automated.Prune = true

View File

@@ -6,6 +6,7 @@ import (
"fmt"
"net/http"
"os"
"regexp"
"strconv"
"time"
@@ -21,7 +22,6 @@ import (
applister "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/git"
"github.com/argoproj/argo-cd/v2/util/healthz"
metricsutil "github.com/argoproj/argo-cd/v2/util/metrics"
"github.com/argoproj/argo-cd/v2/util/profile"
ctrl_metrics "sigs.k8s.io/controller-runtime/pkg/metrics"
@@ -151,7 +151,7 @@ func NewMetricsServer(addr string, appLister applister.ApplicationLister, appFil
}
if len(appLabels) > 0 {
normalizedLabels := metricsutil.NormalizeLabels("label", appLabels)
normalizedLabels := normalizeLabels("label", appLabels)
descAppLabels = prometheus.NewDesc(
"argocd_app_labels",
"Argo Application labels converted to Prometheus labels",
@@ -203,6 +203,20 @@ func NewMetricsServer(addr string, appLister applister.ApplicationLister, appFil
}, nil
}
// Prometheus invalid labels, more info: https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels.
var invalidPromLabelChars = regexp.MustCompile(`[^a-zA-Z0-9_]`)
func normalizeLabels(prefix string, appLabels []string) []string {
results := []string{}
for _, label := range appLabels {
// prometheus labels don't accept dash in their name
curr := invalidPromLabelChars.ReplaceAllString(label, "_")
result := fmt.Sprintf("%s_%s", prefix, curr)
results = append(results, result)
}
return results
}
func (m *MetricsServer) RegisterClustersInfoSource(ctx context.Context, source HasClustersInfo) {
collector := &clusterCollector{infoSource: source}
go collector.Run(ctx)

View File

@@ -462,18 +462,18 @@ func TestWorkqueueMetrics(t *testing.T) {
expectedMetrics := `
# TYPE workqueue_adds_total counter
workqueue_adds_total{controller="test",name="test"}
workqueue_adds_total{name="test"}
# TYPE workqueue_depth gauge
workqueue_depth{controller="test",name="test"}
workqueue_depth{name="test"}
# TYPE workqueue_longest_running_processor_seconds gauge
workqueue_longest_running_processor_seconds{controller="test",name="test"}
workqueue_longest_running_processor_seconds{name="test"}
# TYPE workqueue_queue_duration_seconds histogram
# TYPE workqueue_unfinished_work_seconds gauge
workqueue_unfinished_work_seconds{controller="test",name="test"}
workqueue_unfinished_work_seconds{name="test"}
# TYPE workqueue_work_duration_seconds histogram
`

View File

@@ -186,6 +186,7 @@ func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alp
if len(revisions) < len(sources) || revisions[i] == "" {
revisions[i] = source.TargetRevision
}
ts.AddCheckpoint("helm_ms")
repo, err := m.db.GetRepository(context.Background(), source.RepoURL, proj.Name)
if err != nil {
return nil, nil, fmt.Errorf("failed to get repo %q: %w", source.RepoURL, err)
@@ -227,6 +228,7 @@ func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alp
}
}
ts.AddCheckpoint("version_ms")
log.Debugf("Generating Manifest for source %s revision %s", source, revisions[i])
manifestInfo, err := repoClient.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
Repo: repo,
@@ -263,7 +265,7 @@ func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alp
manifestInfos = append(manifestInfos, manifestInfo)
}
ts.AddCheckpoint("manifests_ms")
ts.AddCheckpoint("unmarshal_ms")
logCtx := log.WithField("application", app.QualifiedName())
for k, v := range ts.Timings() {
logCtx = logCtx.WithField(k, v.Milliseconds())
@@ -589,7 +591,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1
}
// No need to care about the return value here, we just want the modified managedNs
_, err = syncNamespace(app.Spec.SyncPolicy)(managedNs, liveObj)
_, err = syncNamespace(m.resourceTracking, appLabelKey, trackingMethod, app.Name, app.Spec.SyncPolicy)(managedNs, liveObj)
if err != nil {
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now})
failedToLoadObjs = true

View File

@@ -324,7 +324,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
}
if syncOp.SyncOptions.HasOption("CreateNamespace=true") {
opts = append(opts, sync.WithNamespaceModifier(syncNamespace(app.Spec.SyncPolicy)))
opts = append(opts, sync.WithNamespaceModifier(syncNamespace(m.resourceTracking, appLabelKey, trackingMethod, app.Name, app.Spec.SyncPolicy)))
}
syncCtx, cleanup, err := sync.NewSyncContext(

View File

@@ -5,11 +5,12 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/argo"
)
// syncNamespace determine if Argo CD should create and/or manage the namespace
// where the application will be deployed.
func syncNamespace(syncPolicy *v1alpha1.SyncPolicy) func(m *unstructured.Unstructured, l *unstructured.Unstructured) (bool, error) {
func syncNamespace(resourceTracking argo.ResourceTracking, appLabelKey string, trackingMethod v1alpha1.TrackingMethod, appName string, syncPolicy *v1alpha1.SyncPolicy) func(m, l *unstructured.Unstructured) (bool, error) {
// This function must return true for the managed namespace to be synced.
return func(managedNs, liveNs *unstructured.Unstructured) (bool, error) {
if managedNs == nil {

View File

@@ -8,7 +8,9 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/types"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/argo"
)
func createFakeNamespace(uid string, resourceVersion string, labels map[string]string, annotations map[string]string) *unstructured.Unstructured {
@@ -248,7 +250,7 @@ func Test_shouldNamespaceSync(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
actual, err := syncNamespace(tt.syncPolicy)(tt.managedNs, tt.liveNs)
actual, err := syncNamespace(argo.NewResourceTracking(), common.LabelKeyAppInstance, argo.TrackingMethodAnnotation, "some-app", tt.syncPolicy)(tt.managedNs, tt.liveNs)
require.NoError(t, err)
if tt.managedNs != nil {

View File

@@ -61,17 +61,23 @@ window.addEventListener("DOMContentLoaded", function() {
var margin = 30;
var headerHeight = document.getElementsByClassName("md-header")[0].offsetHeight;
const currentVersion = getCurrentVersion();
if (currentVersion && currentVersion !== "stable") {
if (currentVersion) {
if (currentVersion === "latest") {
document.querySelector("div[data-md-component=announce]").innerHTML = "<div id='announce-msg'>You are viewing the docs for an unreleased version of Argo CD, <a href='https://argo-cd.readthedocs.io/en/stable/'>click here to go to the latest stable version.</a></div>";
} else {
var bannerHeight = document.getElementById('announce-msg').offsetHeight + margin;
document.querySelector("header.md-header").style.top = bannerHeight + "px";
document.querySelector('style').textContent +=
"@media screen and (min-width: 76.25em){ .md-sidebar { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}";
document.querySelector('style').textContent +=
"@media screen and (min-width: 60em){ .md-sidebar--secondary { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}";
} else if (currentVersion !== "stable") {
document.querySelector("div[data-md-component=announce]").innerHTML = "<div id='announce-msg'>You are viewing the docs for a previous version of Argo CD, <a href='https://argo-cd.readthedocs.io/en/stable/'>click here to go to the latest stable version.</a></div>";
var bannerHeight = document.getElementById('announce-msg').offsetHeight + margin;
document.querySelector("header.md-header").style.top = bannerHeight + "px";
document.querySelector('style').textContent +=
"@media screen and (min-width: 76.25em){ .md-sidebar { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}";
document.querySelector('style').textContent +=
"@media screen and (min-width: 60em){ .md-sidebar--secondary { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}";
}
var bannerHeight = document.getElementById('announce-msg').offsetHeight + margin;
document.querySelector("header.md-header").style.top = bannerHeight + "px";
document.querySelector('style').textContent +=
"@media screen and (min-width: 76.25em){ .md-sidebar { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}";
document.querySelector('style').textContent +=
"@media screen and (min-width: 60em){ .md-sidebar--secondary { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}";
}
});

View File

@@ -6,7 +6,7 @@ in the `argocd-server` Pods that are placed in the `/tmp/extensions` directory a
```
/tmp/extensions
├── example1
└── extension-1.js
   └── extension-1.js
└── example2
└── extension-2.js
```
@@ -73,7 +73,7 @@ registerSystemLevelExtension(component: ExtensionComponent, title: string, optio
Below is an example of a simple system level extension:
```javascript
```typescript
((window) => {
const component = () => {
return React.createElement(
@@ -106,7 +106,7 @@ registerStatusPanelExtension(component: StatusPanelExtensionComponent, title: st
Below is an example of a simple extension:
```javascript
```typescript
((window) => {
const component = () => {
return React.createElement(
@@ -129,95 +129,32 @@ It is also possible to add an optional flyout widget to your extension. It can b
Below is an example of an extension using the flyout widget:
```javascript
```typescript
((window) => {
const component = (props: {
openFlyout: () => any
}) => {
openFlyout: () => any
}) => {
return React.createElement(
"div",
{
style: { padding: "10px" },
onClick: () => props.openFlyout()
},
"Hello World"
"div",
{
style: { padding: "10px" },
onClick: () => props.openFlyout()
},
"Hello World"
);
};
const flyout = () => {
return React.createElement(
"div",
{ style: { padding: "10px" } },
"This is a flyout"
"div",
{ style: { padding: "10px" } },
"This is a flyout"
);
};
window.extensionsAPI.registerStatusPanelExtension(
component,
"My Extension",
"my_extension",
flyout
component,
"My Extension",
"my_extension",
flyout
);
})(window);
```
## Top Bar Action Menu Extensions
The top bar panel is the action menu at the top of the application view where the action buttons are displayed like Details, Sync, Refresh. Argo CD allows you to add new button to the top bar action menu of an application.
When the extension button is clicked, the custom widget will be rendered in a flyout panel.
The extension should be registered using the `extensionsAPI.registerTopBarActionMenuExt` method:
```typescript
registerTopBarActionMenuExt(
component: TopBarActionMenuExtComponent,
title: string,
id: string,
flyout?: ExtensionComponent,
shouldDisplay: (app?: Application) => boolean = () => true,
iconClassName?: string,
isMiddle = false
)
```
The callback function `shouldDisplay` should return true if the extension should be displayed and false otherwise:
```typescript
const shouldDisplay = (app: Application) => {
return application.metadata?.labels?.['application.environmentLabelKey'] === "prd";
};
```
Below is an example of a simple extension with a flyout widget:
```javascript
((window) => {
const shouldDisplay = () => {
return true;
};
const flyout = () => {
return React.createElement(
"div",
{ style: { padding: "10px" } },
"This is a flyout"
);
};
const component = () => {
return React.createElement(
"div",
{
onClick: () => flyout()
},
"Toolbar Extension Test"
);
};
window.extensionsAPI.registerTopBarActionMenuExt(
component,
"Toolbar Extension Test",
"Toolbar_Extension_Test",
flyout,
shouldDisplay,
'',
true
);
})(window);
```

View File

@@ -15,7 +15,7 @@ These are the upcoming releases dates:
| v2.10 | Monday, Dec. 18, 2023 | Monday, Feb. 5, 2024 | [Katie Lamkin](https://github.com/kmlamkin9) | | [checklist](https://github.com/argoproj/argo-cd/issues/16339) |
| v2.11 | Friday, Apr. 5, 2024 | Monday, May 6, 2024 | [Pavel Kostohrys](https://github.com/pasha-codefresh) | [Pavel Kostohrys](https://github.com/pasha-codefresh) | [checklist](https://github.com/argoproj/argo-cd/issues/17726) |
| v2.12 | Monday, Jun. 17, 2024 | Monday, Aug. 5, 2024 | [Ishita Sequeira](https://github.com/ishitasequeira) | [Pavel Kostohrys](https://github.com/pasha-codefresh) | [checklist](https://github.com/argoproj/argo-cd/issues/19063) |
| v2.13 | Monday, Sep. 16, 2024 | Monday, Nov. 4, 2024 | [Regina Voloshin](https://github.com/reggie-k) | [Pavel Kostohrys](https://github.com/pasha-codefresh) | [checklist](https://github.com/argoproj/argo-cd/issues/19513) |
| v2.13 | Monday, Sep. 16, 2024 | Monday, Nov. 4, 2024 | | | |
| v2.14 | Monday, Dec. 16, 2024 | Monday, Feb. 3, 2025 | | | |
| v2.15 | Monday, Mar. 17, 2025 | Monday, May 5, 2025 | | | |

View File

@@ -42,11 +42,8 @@ In order for an application to be managed and reconciled outside the Argo CD's c
In order to enable this feature, the Argo CD administrator must reconfigure the `argocd-server` and `argocd-application-controller` workloads to add the `--application-namespaces` parameter to the container's startup command.
The `--application-namespaces` parameter takes a comma-separated list of namespaces where `Applications` are to be allowed in. Each entry of the list supports:
The `--application-namespaces` parameter takes a comma-separated list of namespaces where `Applications` are to be allowed in. Each entry of the list supports shell-style wildcards such as `*`, so for example the entry `app-team-*` would match `app-team-one` and `app-team-two`. To enable all namespaces on the cluster where Argo CD is running on, you can just specify `*`, i.e. `--application-namespaces=*`.
- shell-style wildcards such as `*`, so for example the entry `app-team-*` would match `app-team-one` and `app-team-two`. To enable all namespaces on the cluster where Argo CD is running on, you can just specify `*`, i.e. `--application-namespaces=*`.
- regex, requires wrapping the string in ```/```, example to allow all namespaces except a particular one: ```/^((?!not-allowed).)*$/```.
The startup parameters for both, the `argocd-server` and the `argocd-application-controller` can also be conveniently set up and kept in sync by specifying the `application.namespaces` settings in the `argocd-cmd-params-cm` ConfigMap _instead_ of changing the manifests for the respective workloads. For example:
```yaml

View File

@@ -25,9 +25,7 @@ This feature can only be enabled and used when your Argo CD ApplicationSet contr
### SCM Providers secrets consideration
By allowing ApplicationSet in any namespace you must be aware that any secrets can be exfiltrated using `scmProvider` or `pullRequest` generators. This means if ApplicationSet controller is configured to allow namespace `appNs` and some user is allowed to create
an ApplicationSet in `appNs` namespace, then the user can install a malicious Pod into the `appNs` namespace as described below
and read out the content of the secret indirectly, thus exfiltrating the secret value.
By allowing ApplicationSet in any namespace you must be aware that any secrets can be exfiltrated using `scmProvider` or `pullRequest` generators.
Here is an example:
@@ -36,7 +34,6 @@ apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: myapps
namespace: appNs
spec:
goTemplate: true
goTemplateOptions: ["missingkey=error"]
@@ -46,7 +43,7 @@ spec:
# The Gitea owner to scan.
owner: myorg
# With this malicious setting, user can send all request to a Pod that will log incoming requests including headers with tokens
api: http://my-service.appNs.svc.cluster.local
api: http://my-service.my-namespace.svc.cluster.local
# If true, scan every branch of every repository. If false, scan only the default branch. Defaults to false.
allBranches: true
# By changing this token reference, user can exfiltrate any secrets
@@ -56,7 +53,7 @@ spec:
template:
```
In order to prevent the scenario above administrator must restrict the urls of the allowed SCM Providers (example: `https://git.mydomain.com/,https://gitlab.mydomain.com/`) by setting the environment variable `ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS` to argocd-cmd-params-cm `applicationsetcontroller.allowed.scm.providers`. If another url is used, it will be rejected by the applicationset controller.
Therefore administrator must restrict the urls of the allowed SCM Providers (example: `https://git.mydomain.com/,https://gitlab.mydomain.com/`) by setting the environment variable `ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS` to argocd-cmd-params-cm `applicationsetcontroller.allowed.scm.providers`. If another url is used, it will be rejected by the applicationset controller.
For example:
```yaml

View File

@@ -7,8 +7,6 @@ The Git generator contains two subtypes: the Git directory generator, and Git fi
If the `project` field in your ApplicationSet is templated, developers may be able to create Applications under Projects with excessive permissions.
For ApplicationSets with a templated `project` field, [the source of truth _must_ be controlled by admins](./Security.md#templated-project-field)
- in the case of git generators, PRs must require admin approval.
- Git generator does not support Signature Verification For ApplicationSets with a templated `project` field.
## Git Generator: Directories

View File

@@ -100,17 +100,6 @@ possible with Go text templates:
- name: throw-away
value: "{{end}}"
- Signature verification is not supported for the templated `project` field when using the Git generator.
::yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
spec:
goTemplate: true
template:
spec:
project: {{.project}}
## Migration guide

View File

@@ -43,7 +43,6 @@ Note:
- Referenced clusters must already be defined in Argo CD, for the ApplicationSet controller to use them
- Only **one** of `name` or `server` may be specified: if both are specified, an error is returned.
- Signature Verification does not work with the templated `project` field when using git generator.
The `metadata` field of template may also be used to set an Application `name`, or to add labels or annotations to the Application.

View File

@@ -188,11 +188,6 @@ data:
# Include hidden directories from Git
reposerver.include.hidden.directories: "false"
# Set the logging format. One of: text|json (default "text")
dexserver.log.format: "text"
# Set the logging level. One of: debug|info|warn|error (default "info")
dexserver.log.level: "info"
# Disable TLS on the HTTP endpoint
dexserver.disable.tls: "false"
@@ -247,5 +242,3 @@ data:
notificationscontroller.log.format: "text"
# Enable self-service notifications config. Used in conjunction with apps-in-any-namespace. (default "false")
notificationscontroller.selfservice.enabled: "false"
# Disable TLS on connections to repo server
notificationscontroller.repo.server.plaintext: "false"

View File

@@ -468,9 +468,9 @@ data:
### Configure repositories with proxy
Proxy for your repository can be specified in the `proxy` field of the repository secret, along with a corresponding `noProxy` config. Argo CD uses this proxy/noProxy config to access the repository and do related helm/kustomize operations. Argo CD looks for the standard proxy environment variables in the repository server if the custom proxy config is absent.
Proxy for your repository can be specified in the `proxy` field of the repository secret, along with other repository configurations. Argo CD uses this proxy to access the repository. Argo CD looks for the standard proxy environment variables in the repository server if the custom proxy is absent.
An example repository with proxy and noProxy:
An example repository with proxy:
```yaml
apiVersion: v1
@@ -484,13 +484,10 @@ stringData:
type: git
url: https://github.com/argoproj/private-repo
proxy: https://proxy-server-url:8888
noProxy: ".internal.example.com,company.org,10.123.0.0/16"
password: my-password
username: my-username
```
A note on noProxy: Argo CD uses exec to interact with different tools such as helm and kustomize. Not all of these tools support the same noProxy syntax as the [httpproxy go package](https://cs.opensource.google/go/x/net/+/internal-branch.go1.21-vendor:http/httpproxy/proxy.go;l=38-50) does. In case you run in trouble with noProxy not beeing respected you might want to try using the full domain instead of a wildcard pattern or IP range to find a common syntax that all tools support.
### Legacy behaviour
In Argo CD version 2.0 and earlier, repositories were stored as part of the `argocd-cm` config map. For

View File

@@ -130,10 +130,6 @@ stringData:
count (grouped by k8s api version, the granule of parallelism for list operations). In this case, all resources will
be buffered in memory -- no api server request will be blocked by processing.
* `ARGOCD_APPLICATION_TREE_SHARD_SIZE` - environment variable controlling the max number of resources stored in one Redis
key. Splitting application tree into multiple keys helps to reduce the amount of traffic between the controller and Redis.
The default value is 0, which means that the application tree is stored in a single Redis key. The reasonable value is 100.
**metrics**
* `argocd_app_reconcile` - reports application reconciliation duration in seconds. Can be used to build reconciliation duration heat map to get a high-level reconciliation performance picture.

View File

@@ -12,44 +12,6 @@ Golang [Time](https://golang.org/pkg/time/#Time).
Parses specified string using RFC3339 layout. Returns an instance of Golang [Time](https://golang.org/pkg/time/#Time).
<hr>
Time related constants.
**Durations**
```
time.Nanosecond = 1
time.Microsecond = 1000 * Nanosecond
time.Millisecond = 1000 * Microsecond
time.Second = 1000 * Millisecond
time.Minute = 60 * Second
time.Hour = 60 * Minute
```
**Timestamps**
Used when formatting time instances as strings (e.g. `time.Now().Format(time.RFC3339)`).
```
time.Layout = "01/02 03:04:05PM '06 -0700" // The reference time, in numerical order.
time.ANSIC = "Mon Jan _2 15:04:05 2006"
time.UnixDate = "Mon Jan _2 15:04:05 MST 2006"
time.RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
time.RFC822 = "02 Jan 06 15:04 MST"
time.RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
time.RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
time.RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
time.RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
time.RFC3339 = "2006-01-02T15:04:05Z07:00"
time.RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
time.Kitchen = "3:04PM"
// Handy time stamps.
time.Stamp = "Jan _2 15:04:05"
time.StampMilli = "Jan _2 15:04:05.000"
time.StampMicro = "Jan _2 15:04:05.000000"
time.StampNano = "Jan _2 15:04:05.000000000"
```
### **strings**
String related functions.

View File

@@ -1,18 +1,5 @@
# v2.11 to 2.12
## Cluster secret scoping changes
From Argo CD 2.12, there have been some changes to the use of cluster secrets where a `project` is a non-empty value.
Previously, an `Application` or `ApplicationSet` would use any cluster secret matching the URL of the `repoUrl` field.
From 2.12, we now check to see whether the project field of an application _also_ matches the project field of the cluster
secret. What this means is that if you have a cluster secret scoped to `project-a`, an application scoped to `project-b`
can no longer make use of the secret. If you have a cluster secret that's intended to be used by applications in multiple
projects, you need to **unset** the `project` field.
This also applies when using the Git generator in applicationsets; since an applicationset is not scoped to a particular
project any cluster secrets it makes use of also needs to be globally scoped (i.e. any secret needs to have an unset
`project`).
## Upgraded Helm Version
Note that bundled Helm version has been upgraded from 3.14.4 to 3.15.2.

View File

@@ -64,6 +64,3 @@ The default extension for log files generated by Argo CD when using the "Downloa
- Consistency with standard log file conventions.
If you have any custom scripts or tools that depend on the `.txt` extension, please update them accordingly.
## Added proxy to kustomize
Proxy config set on repository credentials / repository templates is now passed down to the `kustomie build` command.

View File

@@ -125,9 +125,3 @@ In this example we give the role _role:admin_ to all users in the group _ArgoCDA
You can now login using our new Keycloak OIDC authentication:
![Keycloak ArgoCD login](../../assets/keycloak-login.png "Keycloak ArgoCD login")
## Troubleshoot
If ArgoCD auth returns 401 or when the login attempt leads to the loop, then restart the argocd-server pod.
```
kubectl rollout restart deployment argocd-server -n argocd
```

View File

@@ -133,7 +133,6 @@
requestedIDTokenClaims:
groups:
essential: true
value: "SecurityGroup"
requestedScopes:
- openid
- profile

View File

@@ -13,20 +13,10 @@ Kubernetes), then the user effectively has the same privileges as that ServiceAc
## Enabling the terminal
<!-- Use indented code blocks for the numbered list to prevent breaking the numbering. See #11590 -->
1. In the `argocd-cm` ConfigMap, set the `exec.enabled` key to `"true"`. This enables the exec feature in Argo CD.
```
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cm
namespace: <namespace> # Replace <namespace> with your actual namespace
data:
exec.enabled: "true"
```
1. Set the `exec.enabled` key to `"true"` on the `argocd-cm` ConfigMap.
2. Patch the `argocd-server` Role (if using namespaced Argo) or ClusterRole (if using clustered Argo) to allow `argocd-server`
to `exec` into pods
to exec into pods
- apiGroups:
- ""
@@ -34,24 +24,14 @@ to `exec` into pods
- pods/exec
verbs:
- create
If you'd like to perform the patch imperatively, you can use the following command:
- For namespaced Argo
```
kubectl patch role <argocd-server-role-name> -n argocd - type='json' -p='[{"op": "add", "path": "/rules/-", "value": {"apiGroups": ["*"], "resources": ["pods/exec"], "verbs": ["create"]}}]'
```
- For clustered Argo
````
kubectl patch clusterrole <argocd-server-clusterrole-name> - type='json' -p='[{"op": "add", "path": "/rules/-", "value": {"apiGroups": ["*"], "resources": ["pods/exec"], "verbs": ["create"]}}]'
```
3. Add RBAC rules to allow your users to `create` the `exec` resource i.e.
p, role:myrole, exec, create, */*, allow
3. Add RBAC rules to allow your users to `create` the `exec` resource, i.e.
This can be added either to the `argocd-cm` `Configmap` manifest or an `AppProject` manifest.
p, role:myrole, exec, create, */*, allow
See [RBAC Configuration](rbac.md#exec-resource) for more info.
See [RBAC Configuration](rbac.md#exec-resource) for more info.
## Changing allowed shells

View File

@@ -21,7 +21,6 @@ for using additional tools such as cdk8s, Tanka, jkcfg, QBEC, Dhall, pulumi, etc
## Summary
Currently, Argo CD provides first-class support for Helm, Kustomize and Jsonnet/YAML. The support includes:
- Bundled binaries (maintainers periodically upgrade binaries)
- An ability to override parameters using UI/CLI
- The applications are discovered in Git repository and auto-suggested during application creation in UI
@@ -42,7 +41,6 @@ The goals for config management plugin enhancement are,
#### Improve Installation Experience
The current Config Management plugin installation experience requires two changes:
- An entry in configManagementPlugins in the Argo CD configmap (i.e. argocd-cm)
- Either an init container with a volume mount that adds a new binary into Argo CD repo server pod, or a rebuild of the argocd image, which contains the necessary tooling
@@ -68,14 +66,13 @@ to additional config management tools.
### Non-Goals
- We aren't planning on changing the existing support for native plugins as of now.
* We aren't planning on changing the existing support for native plugins as of now.
## Proposal
We have drafted the solution to the problem statement as **running configuration management plugin tools as sidecar in the argocd-repo-server**.
All it means that Argo CD Config Management Plugin 2.0 will be,
- A user-supplied container image with all the necessary tooling installed in it.
- It will run as a sidecar in the repo server deployment and will have shared access to the git repositories.
- It will contain a CMP YAML specification file describing how to render manifests.
@@ -83,7 +80,6 @@ All it means that Argo CD Config Management Plugin 2.0 will be,
based on the CMP specification file.
This mechanism will provide the following benefits over the existing solution,
- Plugin owners control their execution environment, packaging whatever dependent binaries required.
- An Argo CD user who wants to use additional config management tools does not have to go through the hassle of building
a customized argocd-repo-server in order to install required dependencies.
@@ -91,9 +87,9 @@ a customized argocd-repo-server in order to install required dependencies.
### Use cases
- UC1: As an Argo CD user, I would like to use first-class support provided for additional tools to generate and manage deployable kubernetes manifests
- UC2: As an Argo CD operator, I want to have smooth experience while installing additional tools such as cdk8s, Tanka, jkcfg, QBEC, Dhall, pulumi, etc.
- UC3: As a plugin owner, I want to have some control over the execution environment as I want to package whatever dependent binaries required.
* UC1: As an Argo CD user, I would like to use first-class support provided for additional tools to generate and manage deployable kubernetes manifests
* UC2: As an Argo CD operator, I want to have smooth experience while installing additional tools such as cdk8s, Tanka, jkcfg, QBEC, Dhall, pulumi, etc.
* UC3: As a plugin owner, I want to have some control over the execution environment as I want to package whatever dependent binaries required.
### Implementation Details
@@ -147,7 +143,6 @@ volumes:
Plugins will be configured via a ConfigManagementPlugin manifest located inside the plugin container, placed at a
well-known location (e.g. /home/argocd/plugins/plugin.yaml). Argo CD is agnostic to the mechanism of how the plugin.yaml would be placed,
but various options can be used on how to place this file, including:
- Baking the file into the plugin image as part of docker build
- Volume mapping the file through a configmap.
@@ -267,27 +262,26 @@ volumes:
After upgrading to CMP v2, an Argo CD operator will have to make following changes,
- In order to install a plugin, an Argo CD operator will simply have to patch argocd-repo-server
* In order to install a plugin, an Argo CD operator will simply have to patch argocd-repo-server
to run config management plugin container as a sidecar, with argocd-cmp-server as its entrypoint:
```bash
# A plugin is a container image which runs as a sidecar, with the execution environment
# necessary to render manifests. To install a plugin,
containers:
- name: cdk8s
command: [/var/run/argocd/argocd-cmp-server]
image: docker.ui/cdk8s/cdk8s:latest
volumeMounts:
- mountPath: /var/run/argocd
name: var-files
```
```bash
# A plugin is a container image which runs as a sidecar, with the execution environment
# necessary to render manifests. To install a plugin,
containers:
- name: cdk8s
command: [/var/run/argocd/argocd-cmp-server]
image: docker.ui/cdk8s/cdk8s:latest
volumeMounts:
- mountPath: /var/run/argocd
name: var-files
```
- Plugins will be configured via a ConfigManagementPlugin manifest located inside the plugin container, placed at a
* Plugins will be configured via a ConfigManagementPlugin manifest located inside the plugin container, placed at a
well-known location (e.g. /plugin.yaml). Argo CD is agnostic to the mechanism of how the plugin.yaml would be placed,
but various options can be used on how to place this file, including:
- Baking the file into the plugin image as part of docker build
- Volume mapping the file through a configmap.
(For more details please refer to [implementation details](#configuration))
## Drawbacks
@@ -296,34 +290,34 @@ There aren't any major drawbacks to this proposal. Also, the advantages supersed
However following are few minor drawbacks,
- With addition of plugin.yaml, there will be more yamls to manage
- Operators need to be aware of the modified Kubernetes manifests in the subsequent version.
- The format of the CMP manifest is a new "contract" that would need to adhere the usual Argo CD compatibility promises in future.
* With addition of plugin.yaml, there will be more yamls to manage
* Operators need to be aware of the modified Kubernetes manifests in the subsequent version.
* The format of the CMP manifest is a new "contract" that would need to adhere the usual Argo CD compatibility promises in future.
## Alternatives
1. ConfigManagementPlugin as CRD. Have a CR which the human operator creates:
```bash
apiVersion: argoproj.io/v1alpha1
kind: ConfigManagementPlugin
metadata:
name: cdk8s
spec:
name: cdk8s
image: docker.ui/cdk8s/cdk8s:latest
version: v1.0
init:
command: [cdk8s, init]
generate:
command: [sh, -c, "cdk8s synth && cat dist/*.yaml"]
discovery:
find:
- command: [find . -name main.ts]
glob: "**/*/main.ts"
check:
- command: [-f ./main.ts]
glob: "main.ts"
```
```bash
apiVersion: argoproj.io/v1alpha1
kind: ConfigManagementPlugin
metadata:
name: cdk8s
spec:
name: cdk8s
image: docker.ui/cdk8s/cdk8s:latest
version: v1.0
init:
command: [cdk8s, init]
generate:
command: [sh, -c, "cdk8s synth && cat dist/*.yaml"]
discovery:
find:
- command: [find . -name main.ts]
glob: "**/*/main.ts"
check:
- command: [-f ./main.ts]
glob: "main.ts"
```
2. Something magically patches the relevant manifest to add the sidecar.

View File

@@ -3,7 +3,7 @@ mkdocs==1.3.0
# Thus pointing to the older version of mkdocs-material.
mkdocs-material==7.1.8
markdown_include==0.6.0
pygments==2.18.0
pygments==2.15.0
jinja2==3.1.4
markdown==3.3.7
pymdown-extensions==10.2.1

View File

@@ -15,27 +15,27 @@ recent minor releases.
|---:|:--------:|:----:|:------:|:---:|
| [go.mod](master/argocd-test.html) | 0 | 0 | 7 | 0 |
| [ui/yarn.lock](master/argocd-test.html) | 0 | 0 | 1 | 0 |
| [dex:v2.41.1](master/ghcr.io_dexidp_dex_v2.41.1.html) | 0 | 0 | 0 | 0 |
| [dex:v2.40.0](master/ghcr.io_dexidp_dex_v2.40.0.html) | 0 | 0 | 2 | 2 |
| [haproxy:2.6.17-alpine](master/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html) | 0 | 0 | 2 | 2 |
| [redis:7.0.15-alpine](master/public.ecr.aws_docker_library_redis_7.0.15-alpine.html) | 0 | 0 | 0 | 0 |
| [argocd:latest](master/quay.io_argoproj_argocd_latest.html) | 0 | 0 | 2 | 8 |
| [argocd:latest](master/quay.io_argoproj_argocd_latest.html) | 0 | 0 | 3 | 8 |
| [redis:7.0.15-alpine](master/redis_7.0.15-alpine.html) | 0 | 0 | 0 | 0 |
| [install.yaml](master/argocd-iac-install.html) | - | - | - | - |
| [namespace-install.yaml](master/argocd-iac-namespace-install.html) | - | - | - | - |
### v2.12.3
### v2.12.0-rc5
| | Critical | High | Medium | Low |
|---:|:--------:|:----:|:------:|:---:|
| [go.mod](v2.12.3/argocd-test.html) | 0 | 0 | 8 | 0 |
| [ui/yarn.lock](v2.12.3/argocd-test.html) | 0 | 0 | 1 | 0 |
| [dex:v2.38.0](v2.12.3/ghcr.io_dexidp_dex_v2.38.0.html) | 0 | 0 | 6 | 5 |
| [haproxy:2.6.17-alpine](v2.12.3/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html) | 0 | 0 | 2 | 2 |
| [redis:7.0.15-alpine](v2.12.3/public.ecr.aws_docker_library_redis_7.0.15-alpine.html) | 0 | 0 | 0 | 0 |
| [argocd:v2.12.3](v2.12.3/quay.io_argoproj_argocd_v2.12.3.html) | 0 | 0 | 2 | 8 |
| [redis:7.0.15-alpine](v2.12.3/redis_7.0.15-alpine.html) | 0 | 0 | 0 | 0 |
| [install.yaml](v2.12.3/argocd-iac-install.html) | - | - | - | - |
| [namespace-install.yaml](v2.12.3/argocd-iac-namespace-install.html) | - | - | - | - |
| [go.mod](v2.12.0-rc5/argocd-test.html) | 0 | 0 | 8 | 0 |
| [ui/yarn.lock](v2.12.0-rc5/argocd-test.html) | 0 | 0 | 1 | 0 |
| [dex:v2.38.0](v2.12.0-rc5/ghcr.io_dexidp_dex_v2.38.0.html) | 0 | 0 | 6 | 5 |
| [haproxy:2.6.17-alpine](v2.12.0-rc5/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html) | 0 | 0 | 2 | 2 |
| [redis:7.0.15-alpine](v2.12.0-rc5/public.ecr.aws_docker_library_redis_7.0.15-alpine.html) | 0 | 0 | 0 | 0 |
| [argocd:v2.12.0-rc5](v2.12.0-rc5/quay.io_argoproj_argocd_v2.12.0-rc5.html) | 0 | 0 | 6 | 8 |
| [redis:7.0.15-alpine](v2.12.0-rc5/redis_7.0.15-alpine.html) | 0 | 0 | 0 | 0 |
| [install.yaml](v2.12.0-rc5/argocd-iac-install.html) | - | - | - | - |
| [namespace-install.yaml](v2.12.0-rc5/argocd-iac-namespace-install.html) | - | - | - | - |
### v2.11.7
@@ -45,7 +45,7 @@ recent minor releases.
| [ui/yarn.lock](v2.11.7/argocd-test.html) | 0 | 0 | 1 | 0 |
| [dex:v2.38.0](v2.11.7/ghcr.io_dexidp_dex_v2.38.0.html) | 0 | 0 | 6 | 5 |
| [haproxy:2.6.14-alpine](v2.11.7/haproxy_2.6.14-alpine.html) | 0 | 1 | 7 | 5 |
| [argocd:v2.11.7](v2.11.7/quay.io_argoproj_argocd_v2.11.7.html) | 0 | 0 | 5 | 20 |
| [argocd:v2.11.7](v2.11.7/quay.io_argoproj_argocd_v2.11.7.html) | 0 | 0 | 6 | 20 |
| [redis:7.0.15-alpine](v2.11.7/redis_7.0.15-alpine.html) | 0 | 0 | 0 | 0 |
| [install.yaml](v2.11.7/argocd-iac-install.html) | - | - | - | - |
| [namespace-install.yaml](v2.11.7/argocd-iac-namespace-install.html) | - | - | - | - |
@@ -58,7 +58,20 @@ recent minor releases.
| [ui/yarn.lock](v2.10.16/argocd-test.html) | 0 | 0 | 1 | 0 |
| [dex:v2.37.0](v2.10.16/ghcr.io_dexidp_dex_v2.37.0.html) | 1 | 1 | 10 | 5 |
| [haproxy:2.6.14-alpine](v2.10.16/haproxy_2.6.14-alpine.html) | 0 | 1 | 7 | 5 |
| [argocd:v2.10.16](v2.10.16/quay.io_argoproj_argocd_v2.10.16.html) | 0 | 0 | 5 | 20 |
| [argocd:v2.10.16](v2.10.16/quay.io_argoproj_argocd_v2.10.16.html) | 0 | 0 | 6 | 20 |
| [redis:7.0.15-alpine](v2.10.16/redis_7.0.15-alpine.html) | 0 | 0 | 0 | 0 |
| [install.yaml](v2.10.16/argocd-iac-install.html) | - | - | - | - |
| [namespace-install.yaml](v2.10.16/argocd-iac-namespace-install.html) | - | - | - | - |
### v2.9.21
| | Critical | High | Medium | Low |
|---:|:--------:|:----:|:------:|:---:|
| [go.mod](v2.9.21/argocd-test.html) | 0 | 2 | 9 | 0 |
| [ui/yarn.lock](v2.9.21/argocd-test.html) | 0 | 0 | 1 | 0 |
| [dex:v2.37.0](v2.9.21/ghcr.io_dexidp_dex_v2.37.0.html) | 1 | 1 | 10 | 5 |
| [haproxy:2.6.14-alpine](v2.9.21/haproxy_2.6.14-alpine.html) | 0 | 1 | 7 | 5 |
| [argocd:v2.9.21](v2.9.21/quay.io_argoproj_argocd_v2.9.21.html) | 0 | 0 | 6 | 20 |
| [redis:7.0.15-alpine](v2.9.21/redis_7.0.15-alpine.html) | 0 | 0 | 0 | 0 |
| [install.yaml](v2.9.21/argocd-iac-install.html) | - | - | - | - |
| [namespace-install.yaml](v2.9.21/argocd-iac-namespace-install.html) | - | - | - | - |

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:21:49 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:19:35 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>
@@ -507,7 +507,7 @@
</li>
<li class="card__meta__item">
Line number: 22366
Line number: 22348
</li>
</ul>
@@ -553,7 +553,7 @@
</li>
<li class="card__meta__item">
Line number: 22047
Line number: 22029
</li>
</ul>
@@ -599,7 +599,7 @@
</li>
<li class="card__meta__item">
Line number: 22134
Line number: 22116
</li>
</ul>
@@ -645,7 +645,7 @@
</li>
<li class="card__meta__item">
Line number: 22162
Line number: 22144
</li>
</ul>
@@ -691,7 +691,7 @@
</li>
<li class="card__meta__item">
Line number: 22192
Line number: 22174
</li>
</ul>
@@ -737,7 +737,7 @@
</li>
<li class="card__meta__item">
Line number: 22210
Line number: 22192
</li>
</ul>
@@ -783,7 +783,7 @@
</li>
<li class="card__meta__item">
Line number: 22228
Line number: 22210
</li>
</ul>
@@ -829,7 +829,7 @@
</li>
<li class="card__meta__item">
Line number: 22250
Line number: 22232
</li>
</ul>
@@ -881,7 +881,7 @@
</li>
<li class="card__meta__item">
Line number: 23322
Line number: 23286
</li>
</ul>
@@ -933,7 +933,7 @@
</li>
<li class="card__meta__item">
Line number: 23621
Line number: 23585
</li>
</ul>
@@ -991,7 +991,7 @@
</li>
<li class="card__meta__item">
Line number: 22859
Line number: 22841
</li>
</ul>
@@ -1049,7 +1049,7 @@
</li>
<li class="card__meta__item">
Line number: 23128
Line number: 23098
</li>
</ul>
@@ -1107,7 +1107,7 @@
</li>
<li class="card__meta__item">
Line number: 23082
Line number: 23064
</li>
</ul>
@@ -1165,7 +1165,7 @@
</li>
<li class="card__meta__item">
Line number: 23188
Line number: 23158
</li>
</ul>
@@ -1223,7 +1223,7 @@
</li>
<li class="card__meta__item">
Line number: 23293
Line number: 23257
</li>
</ul>
@@ -1281,7 +1281,7 @@
</li>
<li class="card__meta__item">
Line number: 23317
Line number: 23281
</li>
</ul>
@@ -1339,7 +1339,7 @@
</li>
<li class="card__meta__item">
Line number: 23621
Line number: 23585
</li>
</ul>
@@ -1397,7 +1397,7 @@
</li>
<li class="card__meta__item">
Line number: 23374
Line number: 23338
</li>
</ul>
@@ -1455,7 +1455,7 @@
</li>
<li class="card__meta__item">
Line number: 23706
Line number: 23670
</li>
</ul>
@@ -1513,7 +1513,7 @@
</li>
<li class="card__meta__item">
Line number: 24096
Line number: 24060
</li>
</ul>
@@ -1565,7 +1565,7 @@
</li>
<li class="card__meta__item">
Line number: 23108
Line number: 23078
</li>
</ul>
@@ -1617,7 +1617,7 @@
</li>
<li class="card__meta__item">
Line number: 22859
Line number: 22841
</li>
</ul>
@@ -1669,7 +1669,7 @@
</li>
<li class="card__meta__item">
Line number: 23082
Line number: 23064
</li>
</ul>
@@ -1721,7 +1721,7 @@
</li>
<li class="card__meta__item">
Line number: 23293
Line number: 23257
</li>
</ul>
@@ -1779,7 +1779,7 @@
</li>
<li class="card__meta__item">
Line number: 22859
Line number: 22841
</li>
</ul>
@@ -1837,7 +1837,7 @@
</li>
<li class="card__meta__item">
Line number: 23082
Line number: 23064
</li>
</ul>
@@ -1895,7 +1895,7 @@
</li>
<li class="card__meta__item">
Line number: 23128
Line number: 23098
</li>
</ul>
@@ -1953,7 +1953,7 @@
</li>
<li class="card__meta__item">
Line number: 23188
Line number: 23158
</li>
</ul>
@@ -2011,7 +2011,7 @@
</li>
<li class="card__meta__item">
Line number: 23293
Line number: 23257
</li>
</ul>
@@ -2069,7 +2069,7 @@
</li>
<li class="card__meta__item">
Line number: 23317
Line number: 23281
</li>
</ul>
@@ -2127,7 +2127,7 @@
</li>
<li class="card__meta__item">
Line number: 23621
Line number: 23585
</li>
</ul>
@@ -2185,7 +2185,7 @@
</li>
<li class="card__meta__item">
Line number: 23374
Line number: 23338
</li>
</ul>
@@ -2243,7 +2243,7 @@
</li>
<li class="card__meta__item">
Line number: 23706
Line number: 23670
</li>
</ul>
@@ -2301,7 +2301,7 @@
</li>
<li class="card__meta__item">
Line number: 24096
Line number: 24060
</li>
</ul>
@@ -2357,7 +2357,7 @@
</li>
<li class="card__meta__item">
Line number: 23006
Line number: 22988
</li>
</ul>
@@ -2413,7 +2413,7 @@
</li>
<li class="card__meta__item">
Line number: 23136
Line number: 23106
</li>
</ul>
@@ -2469,7 +2469,7 @@
</li>
<li class="card__meta__item">
Line number: 23111
Line number: 23081
</li>
</ul>
@@ -2525,7 +2525,7 @@
</li>
<li class="card__meta__item">
Line number: 23227
Line number: 23191
</li>
</ul>
@@ -2581,7 +2581,7 @@
</li>
<li class="card__meta__item">
Line number: 23310
Line number: 23274
</li>
</ul>
@@ -2637,7 +2637,7 @@
</li>
<li class="card__meta__item">
Line number: 23324
Line number: 23288
</li>
</ul>
@@ -2693,7 +2693,7 @@
</li>
<li class="card__meta__item">
Line number: 23628
Line number: 23592
</li>
</ul>
@@ -2749,7 +2749,7 @@
</li>
<li class="card__meta__item">
Line number: 23594
Line number: 23558
</li>
</ul>
@@ -2805,7 +2805,7 @@
</li>
<li class="card__meta__item">
Line number: 23997
Line number: 23961
</li>
</ul>
@@ -2861,7 +2861,7 @@
</li>
<li class="card__meta__item">
Line number: 24297
Line number: 24261
</li>
</ul>

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:21:57 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:19:44 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>
@@ -835,7 +835,7 @@
</li>
<li class="card__meta__item">
Line number: 1138
Line number: 1120
</li>
</ul>
@@ -887,7 +887,7 @@
</li>
<li class="card__meta__item">
Line number: 1437
Line number: 1419
</li>
</ul>
@@ -1003,7 +1003,7 @@
</li>
<li class="card__meta__item">
Line number: 944
Line number: 932
</li>
</ul>
@@ -1119,7 +1119,7 @@
</li>
<li class="card__meta__item">
Line number: 1004
Line number: 992
</li>
</ul>
@@ -1177,7 +1177,7 @@
</li>
<li class="card__meta__item">
Line number: 1109
Line number: 1091
</li>
</ul>
@@ -1235,7 +1235,7 @@
</li>
<li class="card__meta__item">
Line number: 1133
Line number: 1115
</li>
</ul>
@@ -1293,7 +1293,7 @@
</li>
<li class="card__meta__item">
Line number: 1437
Line number: 1419
</li>
</ul>
@@ -1351,7 +1351,7 @@
</li>
<li class="card__meta__item">
Line number: 1190
Line number: 1172
</li>
</ul>
@@ -1409,7 +1409,7 @@
</li>
<li class="card__meta__item">
Line number: 1522
Line number: 1504
</li>
</ul>
@@ -1467,7 +1467,7 @@
</li>
<li class="card__meta__item">
Line number: 1912
Line number: 1894
</li>
</ul>
@@ -1519,7 +1519,7 @@
</li>
<li class="card__meta__item">
Line number: 924
Line number: 912
</li>
</ul>
@@ -1675,7 +1675,7 @@
</li>
<li class="card__meta__item">
Line number: 1109
Line number: 1091
</li>
</ul>
@@ -1849,7 +1849,7 @@
</li>
<li class="card__meta__item">
Line number: 944
Line number: 932
</li>
</ul>
@@ -1907,7 +1907,7 @@
</li>
<li class="card__meta__item">
Line number: 1004
Line number: 992
</li>
</ul>
@@ -1965,7 +1965,7 @@
</li>
<li class="card__meta__item">
Line number: 1109
Line number: 1091
</li>
</ul>
@@ -2023,7 +2023,7 @@
</li>
<li class="card__meta__item">
Line number: 1133
Line number: 1115
</li>
</ul>
@@ -2081,7 +2081,7 @@
</li>
<li class="card__meta__item">
Line number: 1437
Line number: 1419
</li>
</ul>
@@ -2139,7 +2139,7 @@
</li>
<li class="card__meta__item">
Line number: 1190
Line number: 1172
</li>
</ul>
@@ -2197,7 +2197,7 @@
</li>
<li class="card__meta__item">
Line number: 1522
Line number: 1504
</li>
</ul>
@@ -2255,7 +2255,7 @@
</li>
<li class="card__meta__item">
Line number: 1912
Line number: 1894
</li>
</ul>
@@ -2367,7 +2367,7 @@
</li>
<li class="card__meta__item">
Line number: 952
Line number: 940
</li>
</ul>
@@ -2423,7 +2423,7 @@
</li>
<li class="card__meta__item">
Line number: 927
Line number: 915
</li>
</ul>
@@ -2479,7 +2479,7 @@
</li>
<li class="card__meta__item">
Line number: 1043
Line number: 1025
</li>
</ul>
@@ -2535,7 +2535,7 @@
</li>
<li class="card__meta__item">
Line number: 1126
Line number: 1108
</li>
</ul>
@@ -2591,7 +2591,7 @@
</li>
<li class="card__meta__item">
Line number: 1140
Line number: 1122
</li>
</ul>
@@ -2647,7 +2647,7 @@
</li>
<li class="card__meta__item">
Line number: 1444
Line number: 1426
</li>
</ul>
@@ -2703,7 +2703,7 @@
</li>
<li class="card__meta__item">
Line number: 1410
Line number: 1392
</li>
</ul>
@@ -2759,7 +2759,7 @@
</li>
<li class="card__meta__item">
Line number: 1813
Line number: 1795
</li>
</ul>
@@ -2815,7 +2815,7 @@
</li>
<li class="card__meta__item">
Line number: 2113
Line number: 2095
</li>
</ul>

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:19:46 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:17:29 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>
@@ -469,7 +469,7 @@
<div class="meta-counts">
<div class="meta-count"><span>8</span> <span>known vulnerabilities</span></div>
<div class="meta-count"><span>26 vulnerable dependency paths</span></div>
<div class="meta-count"><span>2128</span> <span>dependencies</span></div>
<div class="meta-count"><span>2085</span> <span>dependencies</span></div>
</div><!-- .meta-counts -->
</div><!-- .layout-container--short -->
</header><!-- .project__header -->

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:20:01 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:17:43 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:20:05 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:17:48 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>

View File

@@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Snyk test report</title>
<meta name="description" content="17 known vulnerabilities found in 61 vulnerable dependency paths.">
<meta name="description" content="18 known vulnerabilities found in 65 vulnerable dependency paths.">
<base target="_blank">
<link rel="icon" type="image/png" href="https://res.cloudinary.com/snyk/image/upload/v1468845142/favicon/favicon.png"
sizes="194x194">
@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:20:22 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:18:05 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>
@@ -470,9 +470,9 @@
</div>
<div class="meta-counts">
<div class="meta-count"><span>17</span> <span>known vulnerabilities</span></div>
<div class="meta-count"><span>61 vulnerable dependency paths</span></div>
<div class="meta-count"><span>2350</span> <span>dependencies</span></div>
<div class="meta-count"><span>18</span> <span>known vulnerabilities</span></div>
<div class="meta-count"><span>65 vulnerable dependency paths</span></div>
<div class="meta-count"><span>2310</span> <span>dependencies</span></div>
</div><!-- .meta-counts -->
</div><!-- .layout-container--short -->
</header><!-- .project__header -->
@@ -480,6 +480,127 @@
<div class="layout-container" style="padding-top: 35px;">
<div class="cards--vuln filter--patch filter--ignore">
<div class="card card--vuln disclosure--not-new severity--medium" data-snyk-test="medium">
<h2 class="card__title">CVE-2020-22916</h2>
<div class="card__section">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
<ul class="card__meta">
<li class="card__meta__item">
Manifest file: quay.io/argoproj/argocd:latest/argoproj/argocd <span class="list-paths__item__arrow"></span> Dockerfile
</li>
<li class="card__meta__item">
Package Manager: ubuntu:24.04
</li>
<li class="card__meta__item">
Vulnerable module:
xz-utils/liblzma5
</li>
<li class="card__meta__item">Introduced through:
docker-image|quay.io/argoproj/argocd@latest and xz-utils/liblzma5@5.6.1+really5.4.5-1
</li>
</ul>
<hr/>
<h3 class="card__section__title">Detailed paths</h3>
<ul class="card__meta__paths">
<li>
<span class="list-paths__item__introduced"><em>Introduced through</em>:
docker-image|quay.io/argoproj/argocd@latest
<span class="list-paths__item__arrow"></span>
xz-utils/liblzma5@5.6.1+really5.4.5-1
</span>
</li>
<li>
<span class="list-paths__item__introduced"><em>Introduced through</em>:
docker-image|quay.io/argoproj/argocd@latest
<span class="list-paths__item__arrow"></span>
apt@2.7.14build2
<span class="list-paths__item__arrow"></span>
apt/libapt-pkg6.0t64@2.7.14build2
<span class="list-paths__item__arrow"></span>
xz-utils/liblzma5@5.6.1+really5.4.5-1
</span>
</li>
<li>
<span class="list-paths__item__introduced"><em>Introduced through</em>:
docker-image|quay.io/argoproj/argocd@latest
<span class="list-paths__item__arrow"></span>
dash@0.5.12-6ubuntu5
<span class="list-paths__item__arrow"></span>
dpkg@1.22.6ubuntu6
<span class="list-paths__item__arrow"></span>
xz-utils/liblzma5@5.6.1+really5.4.5-1
</span>
</li>
<li>
<span class="list-paths__item__introduced"><em>Introduced through</em>:
docker-image|quay.io/argoproj/argocd@latest
<span class="list-paths__item__arrow"></span>
apt@2.7.14build2
<span class="list-paths__item__arrow"></span>
adduser@3.137ubuntu1
<span class="list-paths__item__arrow"></span>
shadow/passwd@1:4.13+dfsg1-4ubuntu3
<span class="list-paths__item__arrow"></span>
pam/libpam-modules@1.5.3-5ubuntu5.1
<span class="list-paths__item__arrow"></span>
systemd/libsystemd0@255.4-1ubuntu8.2
<span class="list-paths__item__arrow"></span>
xz-utils/liblzma5@5.6.1+really5.4.5-1
</span>
</li>
</ul><!-- .list-paths -->
</div><!-- .card__section -->
<hr/>
<!-- Overview -->
<h2 id="nvd-description">NVD Description</h2>
<p><strong><em>Note:</em></strong> <em>Versions mentioned in the description apply only to the upstream <code>xz-utils</code> package and not the <code>xz-utils</code> package as distributed by <code>Ubuntu</code>.</em>
<em>See <code>How to fix?</code> for <code>Ubuntu:24.04</code> relevant fixed versions and status.</em></p>
<p>An issue discovered in XZ 5.2.5 allows attackers to cause a denial of service via decompression of a crafted file. NOTE: the vendor disputes the claims of &#34;endless output&#34; and &#34;denial of service&#34; because decompression of the 17,486 bytes always results in 114,881,179 bytes, which is often a reasonable size increase.</p>
<h2 id="remediation">Remediation</h2>
<p>There is no fixed version for <code>Ubuntu:24.04</code> <code>xz-utils</code>.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="http://people.ubuntu.com/~ubuntu-security/cve/CVE-2020-22916">http://people.ubuntu.com/~ubuntu-security/cve/CVE-2020-22916</a></li>
<li><a href="https://github.com/snappyJack/CVE-request-XZ-5.2.5-has-denial-of-service-vulnerability">https://github.com/snappyJack/CVE-request-XZ-5.2.5-has-denial-of-service-vulnerability</a></li>
<li><a href="https://tukaani.org/xz/">https://tukaani.org/xz/</a></li>
<li><a href="https://bugzilla.redhat.com/show_bug.cgi?id=2234987">https://bugzilla.redhat.com/show_bug.cgi?id=2234987</a></li>
<li><a href="https://bugzilla.suse.com/show_bug.cgi?id=1214590">https://bugzilla.suse.com/show_bug.cgi?id=1214590</a></li>
<li><a href="https://github.com/tukaani-project/xz/issues/61">https://github.com/tukaani-project/xz/issues/61</a></li>
<li><a href="https://security-tracker.debian.org/tracker/CVE-2020-22916">https://security-tracker.debian.org/tracker/CVE-2020-22916</a></li>
<li><a href="http://web.archive.org/web/20230918084612/https://github.com/snappyJack/CVE-request-XZ-5.2.5-has-denial-of-service-vulnerability">http://web.archive.org/web/20230918084612/https://github.com/snappyJack/CVE-request-XZ-5.2.5-has-denial-of-service-vulnerability</a></li>
</ul>
<hr/>
<div class="cta card__cta">
<p><a href="https://snyk.io/vuln/SNYK-UBUNTU2404-XZUTILS-6714568">More about this vulnerability</a></p>
</div>
</div><!-- .card -->
<div class="card card--vuln disclosure--not-new severity--medium" data-snyk-test="medium">
<h2 class="card__title">Information Exposure</h2>
<div class="card__section">
@@ -609,7 +730,7 @@
<span class="list-paths__item__arrow"></span>
pam/libpam-modules@1.5.3-5ubuntu5.1
<span class="list-paths__item__arrow"></span>
systemd/libsystemd0@255.4-1ubuntu8.4
systemd/libsystemd0@255.4-1ubuntu8.2
<span class="list-paths__item__arrow"></span>
libgcrypt20@1.10.3-2build1
@@ -685,7 +806,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
<span class="list-paths__item__arrow"></span>
@@ -700,7 +821,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
<span class="list-paths__item__arrow"></span>
@@ -717,7 +838,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
<span class="list-paths__item__arrow"></span>
@@ -732,7 +853,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
<span class="list-paths__item__arrow"></span>
@@ -749,7 +870,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
<span class="list-paths__item__arrow"></span>
@@ -768,7 +889,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
<span class="list-paths__item__arrow"></span>
@@ -781,7 +902,7 @@
<span class="list-paths__item__introduced"><em>Introduced through</em>:
docker-image|quay.io/argoproj/argocd@latest
<span class="list-paths__item__arrow"></span>
openssh/openssh-client@1:9.6p1-3ubuntu13.5
openssh/openssh-client@1:9.6p1-3ubuntu13.4
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
@@ -794,7 +915,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
@@ -807,7 +928,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
libssh/libssh-4@0.10.6-2build2
<span class="list-paths__item__arrow"></span>
@@ -1458,7 +1579,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
<span class="list-paths__item__arrow"></span>
@@ -1473,7 +1594,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
<span class="list-paths__item__arrow"></span>
@@ -1490,7 +1611,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
<span class="list-paths__item__arrow"></span>
@@ -1505,7 +1626,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
<span class="list-paths__item__arrow"></span>
@@ -1522,7 +1643,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
<span class="list-paths__item__arrow"></span>
@@ -1541,7 +1662,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
<span class="list-paths__item__arrow"></span>
@@ -1554,7 +1675,7 @@
<span class="list-paths__item__introduced"><em>Introduced through</em>:
docker-image|quay.io/argoproj/argocd@latest
<span class="list-paths__item__arrow"></span>
openssh/openssh-client@1:9.6p1-3ubuntu13.5
openssh/openssh-client@1:9.6p1-3ubuntu13.4
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
@@ -1567,7 +1688,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
@@ -1580,7 +1701,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
libssh/libssh-4@0.10.6-2build2
<span class="list-paths__item__arrow"></span>
@@ -1666,7 +1787,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
<span class="list-paths__item__arrow"></span>
@@ -1681,7 +1802,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
<span class="list-paths__item__arrow"></span>
@@ -1698,7 +1819,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
<span class="list-paths__item__arrow"></span>
@@ -1713,7 +1834,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
<span class="list-paths__item__arrow"></span>
@@ -1730,7 +1851,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
<span class="list-paths__item__arrow"></span>
@@ -1749,7 +1870,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
<span class="list-paths__item__arrow"></span>
@@ -1762,7 +1883,7 @@
<span class="list-paths__item__introduced"><em>Introduced through</em>:
docker-image|quay.io/argoproj/argocd@latest
<span class="list-paths__item__arrow"></span>
openssh/openssh-client@1:9.6p1-3ubuntu13.5
openssh/openssh-client@1:9.6p1-3ubuntu13.4
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
@@ -1775,7 +1896,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
@@ -1788,7 +1909,7 @@
<span class="list-paths__item__arrow"></span>
git@1:2.43.0-1ubuntu7.1
<span class="list-paths__item__arrow"></span>
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.2
<span class="list-paths__item__arrow"></span>
libssh/libssh-4@0.10.6-2build2
<span class="list-paths__item__arrow"></span>
@@ -2003,7 +2124,7 @@
<li class="card__meta__item">Introduced through:
docker-image|quay.io/argoproj/argocd@latest and glibc/libc-bin@2.39-0ubuntu8.3
docker-image|quay.io/argoproj/argocd@latest and glibc/libc-bin@2.39-0ubuntu8.2
</li>
</ul>
@@ -2018,7 +2139,7 @@
<span class="list-paths__item__introduced"><em>Introduced through</em>:
docker-image|quay.io/argoproj/argocd@latest
<span class="list-paths__item__arrow"></span>
glibc/libc-bin@2.39-0ubuntu8.3
glibc/libc-bin@2.39-0ubuntu8.2
</span>
@@ -2027,7 +2148,7 @@
<span class="list-paths__item__introduced"><em>Introduced through</em>:
docker-image|quay.io/argoproj/argocd@latest
<span class="list-paths__item__arrow"></span>
glibc/libc6@2.39-0ubuntu8.3
glibc/libc6@2.39-0ubuntu8.2
</span>

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:20:25 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:18:09 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:28:24 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:26:21 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:28:33 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:26:29 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:26:34 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:24:28 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:26:41 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:24:35 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:26:45 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:24:39 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>

View File

@@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Snyk test report</title>
<meta name="description" content="35 known vulnerabilities found in 221 vulnerable dependency paths.">
<meta name="description" content="36 known vulnerabilities found in 222 vulnerable dependency paths.">
<base target="_blank">
<link rel="icon" type="image/png" href="https://res.cloudinary.com/snyk/image/upload/v1468845142/favicon/favicon.png"
sizes="194x194">
@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:27:02 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:24:56 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>
@@ -470,8 +470,8 @@
</div>
<div class="meta-counts">
<div class="meta-count"><span>35</span> <span>known vulnerabilities</span></div>
<div class="meta-count"><span>221 vulnerable dependency paths</span></div>
<div class="meta-count"><span>36</span> <span>known vulnerabilities</span></div>
<div class="meta-count"><span>222 vulnerable dependency paths</span></div>
<div class="meta-count"><span>2278</span> <span>dependencies</span></div>
</div><!-- .meta-counts -->
</div><!-- .layout-container--short -->
@@ -559,6 +559,82 @@
<p><a href="https://snyk.io/vuln/SNYK-GOLANG-GOLANGORGXNETHTTP2-6531285">More about this vulnerability</a></p>
</div>
</div><!-- .card -->
<div class="card card--vuln disclosure--not-new severity--medium" data-snyk-test="medium">
<h2 class="card__title">CVE-2020-22916</h2>
<div class="card__section">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
<ul class="card__meta">
<li class="card__meta__item">
Manifest file: quay.io/argoproj/argocd:v2.10.16/argoproj/argocd <span class="list-paths__item__arrow"></span> Dockerfile
</li>
<li class="card__meta__item">
Package Manager: ubuntu:22.04
</li>
<li class="card__meta__item">
Vulnerable module:
xz-utils/liblzma5
</li>
<li class="card__meta__item">Introduced through:
docker-image|quay.io/argoproj/argocd@v2.10.16 and xz-utils/liblzma5@5.2.5-2ubuntu1
</li>
</ul>
<hr/>
<h3 class="card__section__title">Detailed paths</h3>
<ul class="card__meta__paths">
<li>
<span class="list-paths__item__introduced"><em>Introduced through</em>:
docker-image|quay.io/argoproj/argocd@v2.10.16
<span class="list-paths__item__arrow"></span>
xz-utils/liblzma5@5.2.5-2ubuntu1
</span>
</li>
</ul><!-- .list-paths -->
</div><!-- .card__section -->
<hr/>
<!-- Overview -->
<h2 id="nvd-description">NVD Description</h2>
<p><strong><em>Note:</em></strong> <em>Versions mentioned in the description apply only to the upstream <code>xz-utils</code> package and not the <code>xz-utils</code> package as distributed by <code>Ubuntu</code>.</em>
<em>See <code>How to fix?</code> for <code>Ubuntu:22.04</code> relevant fixed versions and status.</em></p>
<p>An issue discovered in XZ 5.2.5 allows attackers to cause a denial of service via decompression of a crafted file. NOTE: the vendor disputes the claims of &#34;endless output&#34; and &#34;denial of service&#34; because decompression of the 17,486 bytes always results in 114,881,179 bytes, which is often a reasonable size increase.</p>
<h2 id="remediation">Remediation</h2>
<p>There is no fixed version for <code>Ubuntu:22.04</code> <code>xz-utils</code>.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="http://people.ubuntu.com/~ubuntu-security/cve/CVE-2020-22916">http://people.ubuntu.com/~ubuntu-security/cve/CVE-2020-22916</a></li>
<li><a href="https://github.com/snappyJack/CVE-request-XZ-5.2.5-has-denial-of-service-vulnerability">https://github.com/snappyJack/CVE-request-XZ-5.2.5-has-denial-of-service-vulnerability</a></li>
<li><a href="https://tukaani.org/xz/">https://tukaani.org/xz/</a></li>
<li><a href="https://bugzilla.redhat.com/show_bug.cgi?id=2234987">https://bugzilla.redhat.com/show_bug.cgi?id=2234987</a></li>
<li><a href="https://bugzilla.suse.com/show_bug.cgi?id=1214590">https://bugzilla.suse.com/show_bug.cgi?id=1214590</a></li>
<li><a href="https://github.com/tukaani-project/xz/issues/61">https://github.com/tukaani-project/xz/issues/61</a></li>
<li><a href="https://security-tracker.debian.org/tracker/CVE-2020-22916">https://security-tracker.debian.org/tracker/CVE-2020-22916</a></li>
<li><a href="http://web.archive.org/web/20230918084612/https://github.com/snappyJack/CVE-request-XZ-5.2.5-has-denial-of-service-vulnerability">http://web.archive.org/web/20230918084612/https://github.com/snappyJack/CVE-request-XZ-5.2.5-has-denial-of-service-vulnerability</a></li>
</ul>
<hr/>
<div class="cta card__cta">
<p><a href="https://snyk.io/vuln/SNYK-UBUNTU2204-XZUTILS-5854647">More about this vulnerability</a></p>
</div>
</div><!-- .card -->
<div class="card card--vuln disclosure--not-new severity--medium" data-snyk-test="medium">
<h2 class="card__title">Information Exposure</h2>
@@ -2035,7 +2111,7 @@
</div><!-- .card -->
<div class="card card--vuln disclosure--not-new severity--medium" data-snyk-test="medium">
<h2 class="card__title">Out-of-bounds Read</h2>
<h2 class="card__title">CVE-2024-7264</h2>
<div class="card__section">
<div class="label label--medium">
@@ -2103,10 +2179,10 @@
<h2 id="references">References</h2>
<ul>
<li><a href="http://people.ubuntu.com/~ubuntu-security/cve/CVE-2024-7264">http://people.ubuntu.com/~ubuntu-security/cve/CVE-2024-7264</a></li>
<li><a href="http://www.openwall.com/lists/oss-security/2024/07/31/1">http://www.openwall.com/lists/oss-security/2024/07/31/1</a></li>
<li><a href="https://curl.se/docs/CVE-2024-7264.html">https://curl.se/docs/CVE-2024-7264.html</a></li>
<li><a href="https://curl.se/docs/CVE-2024-7264.json">https://curl.se/docs/CVE-2024-7264.json</a></li>
<li><a href="https://hackerone.com/reports/2629968">https://hackerone.com/reports/2629968</a></li>
<li><a href="http://www.openwall.com/lists/oss-security/2024/07/31/1">http://www.openwall.com/lists/oss-security/2024/07/31/1</a></li>
</ul>
<hr/>

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:27:05 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:25:01 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:26:17 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:24:10 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:26:25 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:24:19 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:24:27 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:22:17 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:24:34 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:22:23 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:24:38 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:22:29 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>

View File

@@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Snyk test report</title>
<meta name="description" content="35 known vulnerabilities found in 221 vulnerable dependency paths.">
<meta name="description" content="36 known vulnerabilities found in 222 vulnerable dependency paths.">
<base target="_blank">
<link rel="icon" type="image/png" href="https://res.cloudinary.com/snyk/image/upload/v1468845142/favicon/favicon.png"
sizes="194x194">
@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:24:56 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:22:47 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>
@@ -470,8 +470,8 @@
</div>
<div class="meta-counts">
<div class="meta-count"><span>35</span> <span>known vulnerabilities</span></div>
<div class="meta-count"><span>221 vulnerable dependency paths</span></div>
<div class="meta-count"><span>36</span> <span>known vulnerabilities</span></div>
<div class="meta-count"><span>222 vulnerable dependency paths</span></div>
<div class="meta-count"><span>2280</span> <span>dependencies</span></div>
</div><!-- .meta-counts -->
</div><!-- .layout-container--short -->
@@ -559,6 +559,82 @@
<p><a href="https://snyk.io/vuln/SNYK-GOLANG-GOLANGORGXNETHTTP2-6531285">More about this vulnerability</a></p>
</div>
</div><!-- .card -->
<div class="card card--vuln disclosure--not-new severity--medium" data-snyk-test="medium">
<h2 class="card__title">CVE-2020-22916</h2>
<div class="card__section">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
<ul class="card__meta">
<li class="card__meta__item">
Manifest file: quay.io/argoproj/argocd:v2.11.7/argoproj/argocd <span class="list-paths__item__arrow"></span> Dockerfile
</li>
<li class="card__meta__item">
Package Manager: ubuntu:22.04
</li>
<li class="card__meta__item">
Vulnerable module:
xz-utils/liblzma5
</li>
<li class="card__meta__item">Introduced through:
docker-image|quay.io/argoproj/argocd@v2.11.7 and xz-utils/liblzma5@5.2.5-2ubuntu1
</li>
</ul>
<hr/>
<h3 class="card__section__title">Detailed paths</h3>
<ul class="card__meta__paths">
<li>
<span class="list-paths__item__introduced"><em>Introduced through</em>:
docker-image|quay.io/argoproj/argocd@v2.11.7
<span class="list-paths__item__arrow"></span>
xz-utils/liblzma5@5.2.5-2ubuntu1
</span>
</li>
</ul><!-- .list-paths -->
</div><!-- .card__section -->
<hr/>
<!-- Overview -->
<h2 id="nvd-description">NVD Description</h2>
<p><strong><em>Note:</em></strong> <em>Versions mentioned in the description apply only to the upstream <code>xz-utils</code> package and not the <code>xz-utils</code> package as distributed by <code>Ubuntu</code>.</em>
<em>See <code>How to fix?</code> for <code>Ubuntu:22.04</code> relevant fixed versions and status.</em></p>
<p>An issue discovered in XZ 5.2.5 allows attackers to cause a denial of service via decompression of a crafted file. NOTE: the vendor disputes the claims of &#34;endless output&#34; and &#34;denial of service&#34; because decompression of the 17,486 bytes always results in 114,881,179 bytes, which is often a reasonable size increase.</p>
<h2 id="remediation">Remediation</h2>
<p>There is no fixed version for <code>Ubuntu:22.04</code> <code>xz-utils</code>.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="http://people.ubuntu.com/~ubuntu-security/cve/CVE-2020-22916">http://people.ubuntu.com/~ubuntu-security/cve/CVE-2020-22916</a></li>
<li><a href="https://github.com/snappyJack/CVE-request-XZ-5.2.5-has-denial-of-service-vulnerability">https://github.com/snappyJack/CVE-request-XZ-5.2.5-has-denial-of-service-vulnerability</a></li>
<li><a href="https://tukaani.org/xz/">https://tukaani.org/xz/</a></li>
<li><a href="https://bugzilla.redhat.com/show_bug.cgi?id=2234987">https://bugzilla.redhat.com/show_bug.cgi?id=2234987</a></li>
<li><a href="https://bugzilla.suse.com/show_bug.cgi?id=1214590">https://bugzilla.suse.com/show_bug.cgi?id=1214590</a></li>
<li><a href="https://github.com/tukaani-project/xz/issues/61">https://github.com/tukaani-project/xz/issues/61</a></li>
<li><a href="https://security-tracker.debian.org/tracker/CVE-2020-22916">https://security-tracker.debian.org/tracker/CVE-2020-22916</a></li>
<li><a href="http://web.archive.org/web/20230918084612/https://github.com/snappyJack/CVE-request-XZ-5.2.5-has-denial-of-service-vulnerability">http://web.archive.org/web/20230918084612/https://github.com/snappyJack/CVE-request-XZ-5.2.5-has-denial-of-service-vulnerability</a></li>
</ul>
<hr/>
<div class="cta card__cta">
<p><a href="https://snyk.io/vuln/SNYK-UBUNTU2204-XZUTILS-5854647">More about this vulnerability</a></p>
</div>
</div><!-- .card -->
<div class="card card--vuln disclosure--not-new severity--medium" data-snyk-test="medium">
<h2 class="card__title">Information Exposure</h2>
@@ -2035,7 +2111,7 @@
</div><!-- .card -->
<div class="card card--vuln disclosure--not-new severity--medium" data-snyk-test="medium">
<h2 class="card__title">Out-of-bounds Read</h2>
<h2 class="card__title">CVE-2024-7264</h2>
<div class="card__section">
<div class="label label--medium">
@@ -2103,10 +2179,10 @@
<h2 id="references">References</h2>
<ul>
<li><a href="http://people.ubuntu.com/~ubuntu-security/cve/CVE-2024-7264">http://people.ubuntu.com/~ubuntu-security/cve/CVE-2024-7264</a></li>
<li><a href="http://www.openwall.com/lists/oss-security/2024/07/31/1">http://www.openwall.com/lists/oss-security/2024/07/31/1</a></li>
<li><a href="https://curl.se/docs/CVE-2024-7264.html">https://curl.se/docs/CVE-2024-7264.html</a></li>
<li><a href="https://curl.se/docs/CVE-2024-7264.json">https://curl.se/docs/CVE-2024-7264.json</a></li>
<li><a href="https://hackerone.com/reports/2629968">https://hackerone.com/reports/2629968</a></li>
<li><a href="http://www.openwall.com/lists/oss-security/2024/07/31/1">http://www.openwall.com/lists/oss-security/2024/07/31/1</a></li>
</ul>
<hr/>

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">September 1st 2024, 12:24:59 am (UTC+00:00)</p>
<p class="timestamp">August 11th 2024, 12:22:51 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>

Some files were not shown because too many files have changed in this diff Show More