mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-21 01:58:46 +01:00
Compare commits
119 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b52b24cea | ||
|
|
8eb00f51df | ||
|
|
6cdba1e536 | ||
|
|
a6d11354bb | ||
|
|
b9bd45b059 | ||
|
|
da43a20c6a | ||
|
|
de8ed2b9a7 | ||
|
|
f3b90ee517 | ||
|
|
f651ce7169 | ||
|
|
2b929ef2b6 | ||
|
|
06e85eed36 | ||
|
|
09e44e5f21 | ||
|
|
047f709c50 | ||
|
|
566e1d2ada | ||
|
|
5e294d84b7 | ||
|
|
da345ee5f6 | ||
|
|
b574cdc714 | ||
|
|
79b1e4bfc7 | ||
|
|
1f8add5907 | ||
|
|
843a2cdb09 | ||
|
|
3ee71e92dc | ||
|
|
f8dc8b470d | ||
|
|
8b2542ac76 | ||
|
|
a9095fe4e7 | ||
|
|
430f315924 | ||
|
|
9fbe05c7ae | ||
|
|
2a63a8b3d9 | ||
|
|
a43559d796 | ||
|
|
b656b9efb8 | ||
|
|
5834175dba | ||
|
|
0d0db3c110 | ||
|
|
aa14f76d38 | ||
|
|
1bbe17e988 | ||
|
|
0acecad8f8 | ||
|
|
00466c3094 | ||
|
|
2c8a574fff | ||
|
|
3feab7a668 | ||
|
|
233a14bb8b | ||
|
|
3a5b653fb1 | ||
|
|
ef573498c5 | ||
|
|
f597912a6f | ||
|
|
bb3d7730ec | ||
|
|
40186209cd | ||
|
|
60af76fd46 | ||
|
|
c1d3373b8f | ||
|
|
beb71a889d | ||
|
|
3cbb1522dd | ||
|
|
32ee00f1f4 | ||
|
|
4ec26ce399 | ||
|
|
9af0ff5233 | ||
|
|
0361fcb1e4 | ||
|
|
81444474d6 | ||
|
|
7b21eeefee | ||
|
|
e612199c68 | ||
|
|
f49a71c728 | ||
|
|
7fd9d02de7 | ||
|
|
843329174b | ||
|
|
ddd9d6a9f0 | ||
|
|
a0a5a186d9 | ||
|
|
42c2349d7c | ||
|
|
457bb1f23c | ||
|
|
58bab92294 | ||
|
|
e0eb80a45c | ||
|
|
59e0091130 | ||
|
|
86322b5621 | ||
|
|
615dc90999 | ||
|
|
7315033efc | ||
|
|
9b8b044874 | ||
|
|
3a267b8f24 | ||
|
|
3cdce8300c | ||
|
|
b6551cdce9 | ||
|
|
9fc5f14f40 | ||
|
|
085cc6cd73 | ||
|
|
5dd6020dda | ||
|
|
01798055e8 | ||
|
|
d1c74ac984 | ||
|
|
80f2043978 | ||
|
|
69ef68f7a8 | ||
|
|
7d53ef1c40 | ||
|
|
ea2527987c | ||
|
|
f28323b37d | ||
|
|
effbdc936c | ||
|
|
f97024965f | ||
|
|
44072bbdbf | ||
|
|
82003a2c3c | ||
|
|
bacdceda79 | ||
|
|
74af92fd95 | ||
|
|
de53d8eb61 | ||
|
|
d82a746dce | ||
|
|
e7beda0ecc | ||
|
|
014f4424b7 | ||
|
|
a0624f03e4 | ||
|
|
47bcb09c4a | ||
|
|
ab1d5b6f94 | ||
|
|
116180b182 | ||
|
|
6ed7410cf6 | ||
|
|
1ce5824ff4 | ||
|
|
5a7bf2ef66 | ||
|
|
6aa4de62c8 | ||
|
|
fe8dcb60fc | ||
|
|
b2c92c7b6e | ||
|
|
39cb06281f | ||
|
|
f7840c73b3 | ||
|
|
4835e5008a | ||
|
|
9fc93c670a | ||
|
|
afc2fe7172 | ||
|
|
31249489bf | ||
|
|
58543f3835 | ||
|
|
6296b178df | ||
|
|
ea362766db | ||
|
|
159da3c936 | ||
|
|
7601833527 | ||
|
|
b34628db9d | ||
|
|
d2231577c7 | ||
|
|
2cb7616d51 | ||
|
|
6dc559a3e5 | ||
|
|
33e0dda53c | ||
|
|
588b251acc | ||
|
|
3ef05b31ff |
17
.github/workflows/ci-build.yaml
vendored
17
.github/workflows/ci-build.yaml
vendored
@@ -172,7 +172,7 @@ jobs:
|
||||
- name: Run all unit tests
|
||||
run: make test-local
|
||||
- name: Generate test results artifacts
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
||||
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@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
||||
with:
|
||||
name: race-results
|
||||
path: test-results/
|
||||
@@ -323,6 +323,8 @@ 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
|
||||
@@ -377,6 +379,13 @@ 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 }}
|
||||
@@ -497,13 +506,13 @@ jobs:
|
||||
goreman run stop-all || echo "goreman trouble"
|
||||
sleep 30
|
||||
- name: Upload e2e coverage report
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
||||
with:
|
||||
name: e2e-code-coverage
|
||||
path: /tmp/coverage
|
||||
if: ${{ matrix.k3s.latest }}
|
||||
- name: Upload e2e-server logs
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
||||
with:
|
||||
name: e2e-server-k8s${{ matrix.k3s.version }}.log
|
||||
path: /tmp/e2e-server.log
|
||||
|
||||
2
.github/workflows/image-reuse.yaml
vendored
2
.github/workflows/image-reuse.yaml
vendored
@@ -143,7 +143,7 @@ jobs:
|
||||
|
||||
- name: Build and push container image
|
||||
id: image
|
||||
uses: docker/build-push-action@16ebe778df0e7752d2cfcbd924afdbbd89c1a755 #v6.6.1
|
||||
uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 #v6.7.0
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ inputs.platforms }}
|
||||
|
||||
2
.github/workflows/init-release.yaml
vendored
2
.github/workflows/init-release.yaml
vendored
@@ -64,7 +64,7 @@ jobs:
|
||||
git stash pop
|
||||
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6.1.0
|
||||
uses: peter-evans/create-pull-request@4320041ed380b20e97d388d56a7fb4f9b8c20e79 # v7.0.0
|
||||
with:
|
||||
commit-message: "Bump version to ${{ inputs.TARGET_VERSION }}"
|
||||
title: "Bump version to ${{ inputs.TARGET_VERSION }} on ${{ inputs.TARGET_BRANCH }} branch"
|
||||
|
||||
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@@ -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@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6.1.0
|
||||
uses: peter-evans/create-pull-request@4320041ed380b20e97d388d56a7fb4f9b8c20e79 # v7.0.0
|
||||
with:
|
||||
commit-message: Bump version in master
|
||||
title: "chore: Bump version in master"
|
||||
|
||||
2
.github/workflows/scorecard.yaml
vendored
2
.github/workflows/scorecard.yaml
vendored
@@ -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@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
issues:
|
||||
exclude:
|
||||
- SA1019
|
||||
- SA5011
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
exclude-rules:
|
||||
- path: '(.+)_test\.go'
|
||||
linters:
|
||||
- unparam
|
||||
linters:
|
||||
enable:
|
||||
- errcheck
|
||||
@@ -17,6 +20,7 @@ linters:
|
||||
- misspell
|
||||
- staticcheck
|
||||
- testifylint
|
||||
- unparam
|
||||
- unused
|
||||
- whitespace
|
||||
linters-settings:
|
||||
|
||||
@@ -83,7 +83,7 @@ WORKDIR /home/argocd
|
||||
####################################################################################################
|
||||
# Argo CD UI stage
|
||||
####################################################################################################
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/node:22.3.0@sha256:5e4044ff6001d06e7748e35bfa4f80c73cf5f5a7360a1b782995e038a01b0585 AS argocd-ui
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/node:22.8.0@sha256:8ec02324cb37718197de92e51677781be9f1345c709f31a1f44440c6036d24a2 AS argocd-ui
|
||||
|
||||
WORKDIR /src
|
||||
COPY ["ui/package.json", "ui/yarn.lock", "./"]
|
||||
|
||||
2
Makefile
2
Makefile
@@ -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 $(COVERAGE_FLAG) -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd
|
||||
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
|
||||
|
||||
.PHONY: gen-resources-cli-local
|
||||
gen-resources-cli-local: clean-debug
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
Test for DCO. do not merge
|
||||
|
||||
**Releases:**
|
||||
[](https://github.com/argoproj/argo-cd/releases/latest)
|
||||
[](https://artifacthub.io/packages/helm/argo/argo-cd)
|
||||
|
||||
@@ -31,11 +31,9 @@ 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"
|
||||
@@ -45,11 +43,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"
|
||||
@@ -90,7 +88,7 @@ type ApplicationSetReconciler struct {
|
||||
SCMRootCAPath string
|
||||
GlobalPreservedAnnotations []string
|
||||
GlobalPreservedLabels []string
|
||||
Cache cache.Cache
|
||||
Metrics *metrics.ApplicationsetMetrics
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=argoproj.io,resources=applicationsets,verbs=get;list;watch;create;update;patch;delete
|
||||
@@ -101,7 +99,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)
|
||||
@@ -109,6 +107,10 @@ 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
|
||||
@@ -242,20 +244,8 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
|
||||
if r.EnableProgressiveSyncs {
|
||||
// trigger appropriate application syncs if RollingSync strategy is enabled
|
||||
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
|
||||
}
|
||||
if progressiveSyncsRollingSyncStrategyEnabled(&applicationSetInfo) {
|
||||
validApps = r.syncValidApplications(logCtx, &applicationSetInfo, appSyncMap, appMap, validApps)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,8 +399,21 @@ 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
|
||||
@@ -421,13 +424,8 @@ 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) < 3 {
|
||||
if needToUpdateConditions || len(applicationSet.Status.Conditions) < len(newConditions) {
|
||||
// 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 {
|
||||
@@ -503,7 +501,7 @@ func (r *ApplicationSetReconciler) getMinRequeueAfter(applicationSetInfo *argov1
|
||||
func ignoreNotAllowedNamespaces(namespaces []string) predicate.Predicate {
|
||||
return predicate.Funcs{
|
||||
CreateFunc: func(e event.CreateEvent) bool {
|
||||
return glob.MatchStringInList(namespaces, e.Object.GetNamespace(), false)
|
||||
return utils.IsNamespaceAllowed(namespaces, e.Object.GetNamespace())
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -546,25 +544,6 @@ 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
|
||||
@@ -662,7 +641,6 @@ 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
|
||||
@@ -829,7 +807,6 @@ 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)
|
||||
|
||||
@@ -859,12 +836,9 @@ 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, err := r.buildAppDependencyList(logCtx, appset, desiredApplications)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build app dependency list: %w", err)
|
||||
}
|
||||
appDependencyList, appStepMap := r.buildAppDependencyList(logCtx, appset, desiredApplications)
|
||||
|
||||
_, 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)
|
||||
}
|
||||
@@ -874,34 +848,27 @@ func (r *ApplicationSetReconciler) performProgressiveSyncs(ctx context.Context,
|
||||
logCtx.Infof("step %v: %+v", i+1, step)
|
||||
}
|
||||
|
||||
appSyncMap, err := r.buildAppSyncMap(ctx, appset, appDependencyList, appMap)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build app sync map: %w", err)
|
||||
}
|
||||
|
||||
appSyncMap := r.buildAppSyncMap(appset, appDependencyList, appMap)
|
||||
logCtx.Infof("Application allowed to sync before maxUpdate?: %+v", appSyncMap)
|
||||
|
||||
_, err = r.updateApplicationSetApplicationStatusProgress(ctx, logCtx, &appset, appSyncMap, appStepMap, appMap)
|
||||
_, err = r.updateApplicationSetApplicationStatusProgress(ctx, logCtx, &appset, appSyncMap, appStepMap)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update applicationset application status progress: %w", err)
|
||||
}
|
||||
|
||||
_, err = r.updateApplicationSetApplicationStatusConditions(ctx, &appset)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update applicationset application status conditions: %w", err)
|
||||
}
|
||||
_ = r.updateApplicationSetApplicationStatusConditions(ctx, &appset)
|
||||
|
||||
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, error) {
|
||||
func (r *ApplicationSetReconciler) buildAppDependencyList(logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, applications []argov1alpha1.Application) ([][]string, map[string]int) {
|
||||
if applicationSet.Spec.Strategy == nil || applicationSet.Spec.Strategy.Type == "" || applicationSet.Spec.Strategy.Type == "AllAtOnce" {
|
||||
return [][]string{}, map[string]int{}, nil
|
||||
return [][]string{}, map[string]int{}
|
||||
}
|
||||
|
||||
steps := []argov1alpha1.ApplicationSetRolloutStep{}
|
||||
if progressiveSyncsStrategyEnabled(&applicationSet, "RollingSync") {
|
||||
if progressiveSyncsRollingSyncStrategyEnabled(&applicationSet) {
|
||||
steps = applicationSet.Spec.Strategy.RollingSync.Steps
|
||||
}
|
||||
|
||||
@@ -942,7 +909,7 @@ func (r *ApplicationSetReconciler) buildAppDependencyList(logCtx *log.Entry, app
|
||||
}
|
||||
}
|
||||
|
||||
return appDependencyList, appStepMap, nil
|
||||
return appDependencyList, appStepMap
|
||||
}
|
||||
|
||||
func labelMatchedExpression(logCtx *log.Entry, val string, matchExpression argov1alpha1.ApplicationMatchExpression) bool {
|
||||
@@ -966,7 +933,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(ctx context.Context, applicationSet argov1alpha1.ApplicationSet, appDependencyList [][]string, appMap map[string]argov1alpha1.Application) (map[string]bool, error) {
|
||||
func (r *ApplicationSetReconciler) buildAppSyncMap(applicationSet argov1alpha1.ApplicationSet, appDependencyList [][]string, appMap map[string]argov1alpha1.Application) map[string]bool {
|
||||
appSyncMap := map[string]bool{}
|
||||
syncEnabled := true
|
||||
|
||||
@@ -1003,11 +970,11 @@ func (r *ApplicationSetReconciler) buildAppSyncMap(ctx context.Context, applicat
|
||||
}
|
||||
}
|
||||
|
||||
return appSyncMap, nil
|
||||
return appSyncMap
|
||||
}
|
||||
|
||||
func appSyncEnabledForNextStep(appset *argov1alpha1.ApplicationSet, app argov1alpha1.Application, appStatus argov1alpha1.ApplicationSetApplicationStatus) bool {
|
||||
if progressiveSyncsStrategyEnabled(appset, "RollingSync") {
|
||||
if progressiveSyncsRollingSyncStrategyEnabled(appset) {
|
||||
// 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"
|
||||
}
|
||||
@@ -1015,16 +982,8 @@ func appSyncEnabledForNextStep(appset *argov1alpha1.ApplicationSet, app argov1al
|
||||
return true
|
||||
}
|
||||
|
||||
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 progressiveSyncsRollingSyncStrategyEnabled(appset *argov1alpha1.ApplicationSet) bool {
|
||||
return appset.Spec.Strategy != nil && appset.Spec.Strategy.RollingSync != nil && appset.Spec.Strategy.Type == "RollingSync"
|
||||
}
|
||||
|
||||
func isApplicationHealthy(app argov1alpha1.Application) bool {
|
||||
@@ -1082,7 +1041,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
|
||||
}
|
||||
|
||||
appOutdated := false
|
||||
if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") {
|
||||
if progressiveSyncsRollingSyncStrategyEnabled(applicationSet) {
|
||||
appOutdated = syncStatusString == "OutOfSync"
|
||||
}
|
||||
|
||||
@@ -1148,7 +1107,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, appMap map[string]argov1alpha1.Application) ([]argov1alpha1.ApplicationSetApplicationStatus, error) {
|
||||
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress(ctx context.Context, logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appStepMap map[string]int) ([]argov1alpha1.ApplicationSetApplicationStatus, error) {
|
||||
now := metav1.Now()
|
||||
|
||||
appStatuses := make([]argov1alpha1.ApplicationSetApplicationStatus, 0, len(applicationSet.Status.ApplicationStatus))
|
||||
@@ -1159,7 +1118,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress
|
||||
totalCountMap := []int{}
|
||||
|
||||
length := 0
|
||||
if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") {
|
||||
if progressiveSyncsRollingSyncStrategyEnabled(applicationSet) {
|
||||
length = len(applicationSet.Spec.Strategy.RollingSync.Steps)
|
||||
}
|
||||
for s := 0; s < length; s++ {
|
||||
@@ -1171,7 +1130,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress
|
||||
for _, appStatus := range applicationSet.Status.ApplicationStatus {
|
||||
totalCountMap[appStepMap[appStatus.Application]] += 1
|
||||
|
||||
if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") {
|
||||
if progressiveSyncsRollingSyncStrategyEnabled(applicationSet) {
|
||||
if appStatus.Status == "Pending" || appStatus.Status == "Progressing" {
|
||||
updateCountMap[appStepMap[appStatus.Application]] += 1
|
||||
}
|
||||
@@ -1181,7 +1140,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress
|
||||
for _, appStatus := range applicationSet.Status.ApplicationStatus {
|
||||
maxUpdateAllowed := true
|
||||
maxUpdate := &intstr.IntOrString{}
|
||||
if progressiveSyncsStrategyEnabled(applicationSet, "RollingSync") {
|
||||
if progressiveSyncsRollingSyncStrategyEnabled(applicationSet) {
|
||||
maxUpdate = applicationSet.Spec.Strategy.RollingSync.Steps[appStepMap[appStatus.Application]].MaxUpdate
|
||||
}
|
||||
|
||||
@@ -1225,7 +1184,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress
|
||||
return appStatuses, nil
|
||||
}
|
||||
|
||||
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusConditions(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet) ([]argov1alpha1.ApplicationSetCondition, error) {
|
||||
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusConditions(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet) []argov1alpha1.ApplicationSetCondition {
|
||||
appSetProgressing := false
|
||||
for _, appStatus := range applicationSet.Status.ApplicationStatus {
|
||||
if appStatus.Status != "Healthy" {
|
||||
@@ -1250,7 +1209,7 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusConditio
|
||||
Message: "ApplicationSet Rollout Rollout started",
|
||||
Reason: argov1alpha1.ApplicationSetReasonApplicationSetModified,
|
||||
Status: argov1alpha1.ApplicationSetConditionStatusTrue,
|
||||
}, false,
|
||||
}, true,
|
||||
)
|
||||
} else if !appSetProgressing && appSetConditionProgressing {
|
||||
_ = r.setApplicationSetStatusCondition(ctx,
|
||||
@@ -1260,11 +1219,11 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusConditio
|
||||
Message: "ApplicationSet Rollout Rollout complete",
|
||||
Reason: argov1alpha1.ApplicationSetReasonApplicationSetRolloutComplete,
|
||||
Status: argov1alpha1.ApplicationSetConditionStatusFalse,
|
||||
}, false,
|
||||
}, true,
|
||||
)
|
||||
}
|
||||
|
||||
return applicationSet.Status.Conditions, nil
|
||||
return applicationSet.Status.Conditions
|
||||
}
|
||||
|
||||
func findApplicationStatusIndex(appStatuses []argov1alpha1.ApplicationSetApplicationStatus, application string) int {
|
||||
@@ -1290,9 +1249,16 @@ 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
|
||||
}
|
||||
@@ -1374,7 +1340,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, error) {
|
||||
func (r *ApplicationSetReconciler) syncValidApplications(logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, appSyncMap map[string]bool, appMap map[string]argov1alpha1.Application, validApps []argov1alpha1.Application) []argov1alpha1.Application {
|
||||
rolloutApps := []argov1alpha1.Application{}
|
||||
for i := range validApps {
|
||||
pruneEnabled := false
|
||||
@@ -1395,15 +1361,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, nil
|
||||
return rolloutApps
|
||||
}
|
||||
|
||||
// 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, error) {
|
||||
func syncApplication(application argov1alpha1.Application, prune bool) argov1alpha1.Application {
|
||||
operation := argov1alpha1.Operation{
|
||||
InitiatedBy: argov1alpha1.OperationInitiator{
|
||||
Username: "applicationset-controller",
|
||||
@@ -1429,7 +1395,7 @@ func syncApplication(application argov1alpha1.Application, prune bool) (argov1al
|
||||
}
|
||||
application.Operation = &operation
|
||||
|
||||
return application, nil
|
||||
return application
|
||||
}
|
||||
|
||||
func getOwnsHandlerPredicates(enableProgressiveSyncs bool) predicate.Funcs {
|
||||
|
||||
@@ -20,11 +20,8 @@ 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"
|
||||
@@ -37,6 +34,7 @@ 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"
|
||||
@@ -44,34 +42,6 @@ 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)
|
||||
@@ -1089,12 +1059,13 @@ 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)),
|
||||
Cache: &fakeCache{},
|
||||
Metrics: metrics,
|
||||
}
|
||||
|
||||
err = r.createOrUpdateInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.desiredApps)
|
||||
@@ -1199,13 +1170,14 @@ 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,
|
||||
Cache: &fakeCache{},
|
||||
Metrics: metrics,
|
||||
}
|
||||
// settingsMgr := settings.NewSettingsManager(context.TODO(), kubeclientset, "namespace")
|
||||
// argoDB := db.NewDB("namespace", settingsMgr, r.KubeClientset)
|
||||
@@ -1357,13 +1329,14 @@ 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,
|
||||
Cache: &fakeCache{},
|
||||
Metrics: metrics,
|
||||
}
|
||||
// settingsMgr := settings.NewSettingsManager(context.TODO(), kubeclientset, "argocd")
|
||||
// argoDB := db.NewDB("argocd", settingsMgr, r.KubeClientset)
|
||||
@@ -1445,13 +1418,14 @@ 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,
|
||||
Cache: &fakeCache{},
|
||||
Metrics: metrics,
|
||||
}
|
||||
|
||||
err = r.removeOwnerReferencesOnDeleteAppSet(context.Background(), appSet)
|
||||
@@ -1646,12 +1620,13 @@ 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)),
|
||||
Cache: &fakeCache{},
|
||||
Metrics: metrics,
|
||||
}
|
||||
|
||||
err = r.createInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.apps)
|
||||
@@ -1789,12 +1764,14 @@ 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)
|
||||
@@ -1835,6 +1812,7 @@ 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{},
|
||||
@@ -1858,7 +1836,7 @@ func TestGetMinRequeueAfter(t *testing.T) {
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(0),
|
||||
Cache: &fakeCache{},
|
||||
Metrics: metrics,
|
||||
Generators: map[string]generators.Generator{
|
||||
"List": &generatorMock10,
|
||||
"Git": &generatorMock1,
|
||||
@@ -1905,14 +1883,16 @@ 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{
|
||||
@@ -1936,6 +1916,7 @@ func TestValidateGeneratedApplications(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).Build()
|
||||
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
|
||||
|
||||
// Valid cluster
|
||||
myCluster := v1alpha1.Cluster{
|
||||
@@ -2121,12 +2102,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{}
|
||||
@@ -2211,6 +2192,7 @@ 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)
|
||||
@@ -2224,7 +2206,6 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
|
||||
Scheme: scheme,
|
||||
Renderer: &utils.Render{},
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
},
|
||||
@@ -2233,6 +2214,7 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
|
||||
KubeClientset: kubeclientset,
|
||||
Policy: v1alpha1.ApplicationsSyncPolicySync,
|
||||
ArgoCDNamespace: "argocd",
|
||||
Metrics: metrics,
|
||||
}
|
||||
|
||||
req := ctrl.Request{
|
||||
@@ -2266,54 +2248,179 @@ func TestSetApplicationSetStatusCondition(t *testing.T) {
|
||||
err = v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
|
||||
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"}`),
|
||||
}},
|
||||
}},
|
||||
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)
|
||||
},
|
||||
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{},
|
||||
},
|
||||
},
|
||||
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)
|
||||
|
||||
appCondition := v1alpha1.ApplicationSetCondition{
|
||||
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
|
||||
Message: "All applications have been generated successfully",
|
||||
Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate,
|
||||
Status: v1alpha1.ApplicationSetConditionStatusTrue,
|
||||
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")
|
||||
},
|
||||
},
|
||||
{
|
||||
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")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...)
|
||||
argoDBMock := dbmocks.ArgoDB{}
|
||||
argoObjs := []runtime.Object{}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
for _, testCase := range testCases {
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&testCase.appset).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).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,
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -2364,6 +2471,7 @@ 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{
|
||||
@@ -2375,7 +2483,6 @@ 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(),
|
||||
},
|
||||
@@ -2385,6 +2492,7 @@ func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
|
||||
KubeClientset: kubeclientset,
|
||||
Policy: v1alpha1.ApplicationsSyncPolicySync,
|
||||
EnablePolicyOverride: allowPolicyOverride,
|
||||
Metrics: metrics,
|
||||
}
|
||||
|
||||
req := ctrl.Request{
|
||||
@@ -2528,6 +2636,7 @@ 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{
|
||||
@@ -2539,7 +2648,6 @@ 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(),
|
||||
},
|
||||
@@ -2549,6 +2657,7 @@ func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
|
||||
KubeClientset: kubeclientset,
|
||||
Policy: v1alpha1.ApplicationsSyncPolicySync,
|
||||
EnablePolicyOverride: allowPolicyOverride,
|
||||
Metrics: metrics,
|
||||
}
|
||||
|
||||
req := ctrl.Request{
|
||||
@@ -2730,13 +2839,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(),
|
||||
},
|
||||
@@ -2745,6 +2854,7 @@ func TestPolicies(t *testing.T) {
|
||||
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
|
||||
KubeClientset: kubeclientset,
|
||||
Policy: policy,
|
||||
Metrics: metrics,
|
||||
}
|
||||
|
||||
req := ctrl.Request{
|
||||
@@ -2891,19 +3001,20 @@ 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)
|
||||
@@ -2923,6 +3034,7 @@ 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
|
||||
@@ -3659,15 +3771,14 @@ 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, err := r.buildAppDependencyList(log.NewEntry(log.StandardLogger()), cc.appSet, cc.apps)
|
||||
require.NoError(t, err, "expected no errors, but errors occurred")
|
||||
appDependencyList, appStepMap := r.buildAppDependencyList(log.NewEntry(log.StandardLogger()), cc.appSet, cc.apps)
|
||||
assert.Equal(t, cc.expectedList, appDependencyList, "expected appDependencyList did not match actual")
|
||||
assert.Equal(t, cc.expectedStepMap, appStepMap, "expected appStepMap did not match actual")
|
||||
})
|
||||
@@ -3683,6 +3794,7 @@ 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
|
||||
@@ -4250,15 +4362,14 @@ 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, err := r.buildAppSyncMap(context.TODO(), cc.appSet, cc.appDependencyList, cc.appMap)
|
||||
require.NoError(t, err, "expected no errors, but errors occurred")
|
||||
appSyncMap := r.buildAppSyncMap(cc.appSet, cc.appDependencyList, cc.appMap)
|
||||
assert.Equal(t, cc.expectedMap, appSyncMap, "expected appSyncMap did not match actual")
|
||||
})
|
||||
}
|
||||
@@ -5035,16 +5146,17 @@ 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)
|
||||
@@ -5788,19 +5900,20 @@ 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, cc.appMap)
|
||||
appStatuses, err := r.updateApplicationSetApplicationStatusProgress(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.appSyncMap, cc.appStepMap)
|
||||
|
||||
// opt out of testing the LastTransitionTime is accurate
|
||||
for i := range appStatuses {
|
||||
@@ -6003,16 +6116,17 @@ 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)
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
@@ -24,29 +26,29 @@ type clusterSecretEventHandler struct {
|
||||
Client client.Client
|
||||
}
|
||||
|
||||
func (h *clusterSecretEventHandler) Create(ctx context.Context, e event.CreateEvent, q workqueue.RateLimitingInterface) {
|
||||
func (h *clusterSecretEventHandler) Create(ctx context.Context, e event.CreateEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
h.queueRelatedAppGenerators(ctx, q, e.Object)
|
||||
}
|
||||
|
||||
func (h *clusterSecretEventHandler) Update(ctx context.Context, e event.UpdateEvent, q workqueue.RateLimitingInterface) {
|
||||
func (h *clusterSecretEventHandler) Update(ctx context.Context, e event.UpdateEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
h.queueRelatedAppGenerators(ctx, q, e.ObjectNew)
|
||||
}
|
||||
|
||||
func (h *clusterSecretEventHandler) Delete(ctx context.Context, e event.DeleteEvent, q workqueue.RateLimitingInterface) {
|
||||
func (h *clusterSecretEventHandler) Delete(ctx context.Context, e event.DeleteEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
h.queueRelatedAppGenerators(ctx, q, e.Object)
|
||||
}
|
||||
|
||||
func (h *clusterSecretEventHandler) Generic(ctx context.Context, e event.GenericEvent, q workqueue.RateLimitingInterface) {
|
||||
func (h *clusterSecretEventHandler) Generic(ctx context.Context, e event.GenericEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
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 interface {
|
||||
Add(item interface{})
|
||||
type addRateLimitingInterface[T comparable] interface {
|
||||
Add(item T)
|
||||
}
|
||||
|
||||
func (h *clusterSecretEventHandler) queueRelatedAppGenerators(ctx context.Context, q addRateLimitingInterface, object client.Object) {
|
||||
func (h *clusterSecretEventHandler) queueRelatedAppGenerators(ctx context.Context, q addRateLimitingInterface[reconcile.Request], 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
|
||||
|
||||
@@ -551,24 +551,18 @@ 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 interface{}) {
|
||||
if req, ok := item.(ctrl.Request); ok {
|
||||
obj.addedItems = append(obj.addedItems, req)
|
||||
} else {
|
||||
obj.errorOccurred = true
|
||||
}
|
||||
func (obj *mockAddRateLimitingInterface) Add(item reconcile.Request) {
|
||||
obj.addedItems = append(obj.addedItems, item)
|
||||
}
|
||||
|
||||
type mockAddRateLimitingInterface struct {
|
||||
errorOccurred bool
|
||||
addedItems []ctrl.Request
|
||||
addedItems []reconcile.Request
|
||||
}
|
||||
|
||||
func TestNestedGeneratorHasClusterGenerator_NestedClusterGenerator(t *testing.T) {
|
||||
|
||||
@@ -17,6 +17,7 @@ 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"
|
||||
)
|
||||
@@ -60,7 +61,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),
|
||||
"Git": generators.NewGitGenerator(mockServer, "namespace"),
|
||||
"SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), scmConfig),
|
||||
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, fakeDynClient, appClientset, "argocd"),
|
||||
"PullRequest": generators.NewPullRequestGenerator(k8sClient, scmConfig),
|
||||
@@ -89,11 +90,13 @@ 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 {
|
||||
|
||||
@@ -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)
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "namespace")
|
||||
return gitGenerator
|
||||
}
|
||||
|
||||
|
||||
@@ -24,13 +24,16 @@ import (
|
||||
var _ Generator = (*GitGenerator)(nil)
|
||||
|
||||
type GitGenerator struct {
|
||||
repos services.Repos
|
||||
repos services.Repos
|
||||
namespace string
|
||||
}
|
||||
|
||||
func NewGitGenerator(repos services.Repos) Generator {
|
||||
func NewGitGenerator(repos services.Repos, namespace string) Generator {
|
||||
g := &GitGenerator{
|
||||
repos: repos,
|
||||
repos: repos,
|
||||
namespace: namespace,
|
||||
}
|
||||
|
||||
return g
|
||||
}
|
||||
|
||||
@@ -59,21 +62,25 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic
|
||||
|
||||
noRevisionCache := appSet.RefreshRequired()
|
||||
|
||||
var project string
|
||||
if strings.Contains(appSet.Spec.Template.Spec.Project, "{{") {
|
||||
project = appSetGenerator.Git.Template.Spec.Project
|
||||
} else {
|
||||
project = appSet.Spec.Template.Spec.Project
|
||||
}
|
||||
verifyCommit := false
|
||||
|
||||
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)
|
||||
// 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()
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
@@ -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,3 +1383,114 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
"Git": NewGitGenerator(argoCDService, namespace),
|
||||
"SCMProvider": NewSCMProviderGenerator(c, scmConfig),
|
||||
"ClusterDecisionResource": NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, namespace),
|
||||
"PullRequest": NewPullRequestGenerator(c, scmConfig),
|
||||
|
||||
22
applicationset/metrics/fake.go
Normal file
22
applicationset/metrics/fake.go
Normal file
@@ -0,0 +1,22 @@
|
||||
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,
|
||||
}
|
||||
}
|
||||
131
applicationset/metrics/metrics.go
Normal file
131
applicationset/metrics/metrics.go
Normal file
@@ -0,0 +1,131 @@
|
||||
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)
|
||||
}
|
||||
256
applicationset/metrics/metrics_test.go
Normal file
256
applicationset/metrics/metrics_test.go
Normal file
@@ -0,0 +1,256 @@
|
||||
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]
|
||||
}
|
||||
63
applicationset/utils/applicationset_lister.go
Normal file
63
applicationset/utils/applicationset_lister.go
Normal file
@@ -0,0 +1,63 @@
|
||||
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
|
||||
}
|
||||
@@ -132,9 +132,12 @@ 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(),
|
||||
@@ -143,6 +146,7 @@ func getLocalCluster(clientset kubernetes.Interface) *appv1.Cluster {
|
||||
})
|
||||
cluster := localCluster.DeepCopy()
|
||||
now := metav1.Now()
|
||||
// nolint:staticcheck
|
||||
cluster.ConnectionState.ModifiedAt = &now
|
||||
return cluster
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ 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
|
||||
@@ -46,6 +47,10 @@ 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
124
assets/swagger.json
generated
@@ -4489,6 +4489,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
@@ -4514,6 +4535,43 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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",
|
||||
@@ -5464,7 +5522,7 @@
|
||||
"properties": {
|
||||
"matchExpressions": {
|
||||
"type": "array",
|
||||
"title": "matchExpressions is a list of label selector requirements. The requirements are ANDed.\n+optional",
|
||||
"title": "matchExpressions is a list of label selector requirements. The requirements are ANDed.\n+optional\n+listType=atomic",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1LabelSelectorRequirement"
|
||||
}
|
||||
@@ -5492,7 +5550,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",
|
||||
"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",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -5616,7 +5674,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"kubeProxyVersion": {
|
||||
"description": "KubeProxy Version reported by the node.",
|
||||
"description": "Deprecated: KubeProxy Version reported by the node.",
|
||||
"type": "string"
|
||||
},
|
||||
"kubeletVersion": {
|
||||
@@ -5665,7 +5723,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",
|
||||
"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",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -5687,7 +5745,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",
|
||||
"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",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1ManagedFieldsEntry"
|
||||
@@ -5703,7 +5761,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",
|
||||
"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",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1OwnerReference"
|
||||
}
|
||||
@@ -6334,7 +6392,7 @@
|
||||
"description": "Resources is a list of Applications resources managed by this application set.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1alpha1ResourceStatus"
|
||||
"$ref": "#/definitions/applicationv1alpha1ResourceStatus"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6801,7 +6859,7 @@
|
||||
"type": "array",
|
||||
"title": "Resources is a list of Kubernetes resources managed by this application",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1alpha1ResourceStatus"
|
||||
"$ref": "#/definitions/applicationv1alpha1ResourceStatus"
|
||||
}
|
||||
},
|
||||
"sourceType": {
|
||||
@@ -6867,6 +6925,11 @@
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1alpha1ResourceNode"
|
||||
}
|
||||
},
|
||||
"shardsCount": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"title": "ShardsCount contains total number of shards the application tree is split into"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8179,6 +8242,10 @@
|
||||
"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"
|
||||
@@ -8285,6 +8352,10 @@
|
||||
"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"
|
||||
@@ -8682,43 +8753,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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",
|
||||
|
||||
@@ -35,6 +35,7 @@ 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"
|
||||
@@ -69,6 +70,7 @@ func NewCommand() *cobra.Command {
|
||||
allowedScmProviders []string
|
||||
globalPreservedAnnotations []string
|
||||
globalPreservedLabels []string
|
||||
metricsAplicationsetLabels []string
|
||||
enableScmProviders bool
|
||||
webhookParallelism int
|
||||
)
|
||||
@@ -167,7 +169,7 @@ func NewCommand() *cobra.Command {
|
||||
|
||||
tlsConfig := apiclient.TLSConfiguration{
|
||||
DisableTLS: repoServerPlaintext,
|
||||
StrictValidation: repoServerPlaintext,
|
||||
StrictValidation: repoServerStrictTLS,
|
||||
}
|
||||
|
||||
if !repoServerPlaintext && repoServerStrictTLS {
|
||||
@@ -194,6 +196,13 @@ 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(),
|
||||
@@ -211,7 +220,7 @@ func NewCommand() *cobra.Command {
|
||||
SCMRootCAPath: scmRootCAPath,
|
||||
GlobalPreservedAnnotations: globalPreservedAnnotations,
|
||||
GlobalPreservedLabels: globalPreservedLabels,
|
||||
Cache: mgr.GetCache(),
|
||||
Metrics: &metrics,
|
||||
}).SetupWithManager(mgr, enableProgressiveSyncs, maxConcurrentReconciliations); err != nil {
|
||||
log.Error(err, "unable to create controller", "controller", "ApplicationSet")
|
||||
os.Exit(1)
|
||||
@@ -253,6 +262,7 @@ 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
|
||||
}
|
||||
|
||||
|
||||
@@ -136,8 +136,8 @@ func NewRunDexCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
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().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().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", "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().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().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
|
||||
|
||||
@@ -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", false, "Use a plaintext client (non-TLS) to connect to repository server")
|
||||
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(&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")
|
||||
|
||||
@@ -8,6 +8,8 @@ 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"
|
||||
@@ -142,7 +144,11 @@ func NewCommand() *cobra.Command {
|
||||
|
||||
dynamicClient := dynamic.NewForConfigOrDie(config)
|
||||
|
||||
controllerClient, err := client.New(config, client.Options{})
|
||||
scheme := runtime.NewScheme()
|
||||
_ = clientgoscheme.AddToScheme(scheme)
|
||||
_ = v1alpha1.AddToScheme(scheme)
|
||||
|
||||
controllerClient, err := client.New(config, client.Options{Scheme: scheme})
|
||||
errors.CheckError(err)
|
||||
controllerClient = client.NewDryRunClient(controllerClient)
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
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"
|
||||
@@ -83,11 +86,12 @@ 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),
|
||||
applications: dynamicIf.Resource(applicationsResource).Namespace(namespace),
|
||||
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),
|
||||
projects: dynamicIf.Resource(appprojectsResource).Namespace(namespace),
|
||||
applicationSets: dynamicIf.Resource(appplicationSetResource).Namespace(namespace),
|
||||
applicationSets: dynamicIf.Resource(appplicationSetResource),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,3 +222,52 @@ 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,
|
||||
}
|
||||
}
|
||||
|
||||
75
cmd/argocd/commands/admin/admin_test.go
Normal file
75
cmd/argocd/commands/admin/admin_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -20,13 +20,16 @@ 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
|
||||
clientConfig clientcmd.ClientConfig
|
||||
out string
|
||||
applicationNamespaces []string
|
||||
applicationsetNamespaces []string
|
||||
)
|
||||
command := cobra.Command{
|
||||
Use: "export",
|
||||
@@ -58,34 +61,47 @@ func NewExportCommand() *cobra.Command {
|
||||
acdClients := newArgoCDClientsets(config, namespace)
|
||||
acdConfigMap, err := acdClients.configMaps.Get(ctx, common.ArgoCDConfigMapName, v1.GetOptions{})
|
||||
errors.CheckError(err)
|
||||
export(writer, *acdConfigMap)
|
||||
export(writer, *acdConfigMap, namespace)
|
||||
acdRBACConfigMap, err := acdClients.configMaps.Get(ctx, common.ArgoCDRBACConfigMapName, v1.GetOptions{})
|
||||
errors.CheckError(err)
|
||||
export(writer, *acdRBACConfigMap)
|
||||
export(writer, *acdRBACConfigMap, namespace)
|
||||
acdKnownHostsConfigMap, err := acdClients.configMaps.Get(ctx, common.ArgoCDKnownHostsConfigMapName, v1.GetOptions{})
|
||||
errors.CheckError(err)
|
||||
export(writer, *acdKnownHostsConfigMap)
|
||||
export(writer, *acdKnownHostsConfigMap, namespace)
|
||||
acdTLSCertsConfigMap, err := acdClients.configMaps.Get(ctx, common.ArgoCDTLSCertsConfigMapName, v1.GetOptions{})
|
||||
errors.CheckError(err)
|
||||
export(writer, *acdTLSCertsConfigMap)
|
||||
export(writer, *acdTLSCertsConfigMap, namespace)
|
||||
|
||||
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)
|
||||
export(writer, secret, namespace)
|
||||
}
|
||||
}
|
||||
projects, err := acdClients.projects.List(ctx, v1.ListOptions{})
|
||||
errors.CheckError(err)
|
||||
for _, proj := range projects.Items {
|
||||
export(writer, proj)
|
||||
export(writer, proj, namespace)
|
||||
}
|
||||
|
||||
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(writer, app)
|
||||
// Export application only if it is in one of the enabled namespaces
|
||||
if secutil.IsNamespaceEnabled(app.GetNamespace(), namespace, applicationNamespaces) {
|
||||
export(writer, app, namespace)
|
||||
}
|
||||
}
|
||||
applicationSets, err := acdClients.applicationSets.List(ctx, v1.ListOptions{})
|
||||
if err != nil && !apierr.IsNotFound(err) {
|
||||
@@ -97,7 +113,9 @@ func NewExportCommand() *cobra.Command {
|
||||
}
|
||||
if applicationSets != nil {
|
||||
for _, appSet := range applicationSets.Items {
|
||||
export(writer, appSet)
|
||||
if secutil.IsNamespaceEnabled(appSet.GetNamespace(), namespace, applicationsetNamespaces) {
|
||||
export(writer, appSet, namespace)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -105,18 +123,21 @@ 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
|
||||
clientConfig clientcmd.ClientConfig
|
||||
prune bool
|
||||
dryRun bool
|
||||
verbose bool
|
||||
stopOperation bool
|
||||
applicationNamespaces []string
|
||||
applicationsetNamespaces []string
|
||||
)
|
||||
command := cobra.Command{
|
||||
Use: "import SOURCE",
|
||||
@@ -135,6 +156,8 @@ 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 == "-" {
|
||||
@@ -148,6 +171,15 @@ 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
|
||||
@@ -159,7 +191,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()}] = cm
|
||||
pruneObjects[kube.ResourceKey{Group: "", Kind: "ConfigMap", Name: cm.GetName(), Namespace: cm.GetNamespace()}] = cm
|
||||
}
|
||||
if cm.GetName() == common.ArgoCDConfigMapName {
|
||||
referencedSecrets = getReferencedSecrets(cm)
|
||||
@@ -170,18 +202,20 @@ 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()}] = secret
|
||||
pruneObjects[kube.ResourceKey{Group: "", Kind: "Secret", Name: secret.GetName(), Namespace: secret.GetNamespace()}] = secret
|
||||
}
|
||||
}
|
||||
applications, err := acdClients.applications.List(ctx, v1.ListOptions{})
|
||||
errors.CheckError(err)
|
||||
for _, app := range applications.Items {
|
||||
pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.ApplicationKind, Name: app.GetName()}] = app
|
||||
if secutil.IsNamespaceEnabled(app.GetNamespace(), namespace, applicationNamespaces) {
|
||||
pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.ApplicationKind, Name: app.GetName(), Namespace: app.GetNamespace()}] = 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()}] = proj
|
||||
pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.AppProjectKind, Name: proj.GetName(), Namespace: proj.GetNamespace()}] = proj
|
||||
}
|
||||
applicationSets, err := acdClients.applicationSets.List(ctx, v1.ListOptions{})
|
||||
if apierr.IsForbidden(err) || apierr.IsNotFound(err) {
|
||||
@@ -191,7 +225,9 @@ func NewImportCommand() *cobra.Command {
|
||||
}
|
||||
if applicationSets != nil {
|
||||
for _, appSet := range applicationSets.Items {
|
||||
pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.ApplicationSetKind, Name: appSet.GetName()}] = appSet
|
||||
if secutil.IsNamespaceEnabled(appSet.GetNamespace(), namespace, applicationsetNamespaces) {
|
||||
pruneObjects[kube.ResourceKey{Group: application.Group, Kind: application.ApplicationSetKind, Name: appSet.GetName(), Namespace: appSet.GetNamespace()}] = appSet
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,21 +236,33 @@ func NewImportCommand() *cobra.Command {
|
||||
errors.CheckError(err)
|
||||
for _, bakObj := range backupObjects {
|
||||
gvk := bakObj.GroupVersionKind()
|
||||
key := kube.ResourceKey{Group: gvk.Group, Kind: gvk.Kind, Name: bakObj.GetName()}
|
||||
// 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()}
|
||||
liveObj, exists := pruneObjects[key]
|
||||
delete(pruneObjects, key)
|
||||
var dynClient dynamic.ResourceInterface
|
||||
switch bakObj.GetKind() {
|
||||
case "Secret":
|
||||
dynClient = acdClients.secrets
|
||||
dynClient = client.Resource(secretResource).Namespace(bakObj.GetNamespace())
|
||||
case "ConfigMap":
|
||||
dynClient = acdClients.configMaps
|
||||
dynClient = client.Resource(configMapResource).Namespace(bakObj.GetNamespace())
|
||||
case application.AppProjectKind:
|
||||
dynClient = acdClients.projects
|
||||
dynClient = client.Resource(appprojectsResource).Namespace(bakObj.GetNamespace())
|
||||
case application.ApplicationKind:
|
||||
dynClient = acdClients.applications
|
||||
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
|
||||
}
|
||||
case application.ApplicationSetKind:
|
||||
dynClient = acdClients.applicationSets
|
||||
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
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
isForbidden := false
|
||||
@@ -228,7 +276,7 @@ func NewImportCommand() *cobra.Command {
|
||||
}
|
||||
}
|
||||
if !isForbidden {
|
||||
fmt.Printf("%s/%s %s created%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
|
||||
fmt.Printf("%s/%s %s in namespace %s created%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), bakObj.GetNamespace(), dryRunMsg)
|
||||
}
|
||||
} else if specsEqual(*bakObj, liveObj) && checkAppHasNoNeedToStopOperation(liveObj, stopOperation) {
|
||||
if verbose {
|
||||
@@ -247,7 +295,7 @@ func NewImportCommand() *cobra.Command {
|
||||
}
|
||||
}
|
||||
if !isForbidden {
|
||||
fmt.Printf("%s/%s %s updated%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
|
||||
fmt.Printf("%s/%s %s in namespace %s updated%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), bakObj.GetNamespace(), dryRunMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -258,11 +306,11 @@ func NewImportCommand() *cobra.Command {
|
||||
var dynClient dynamic.ResourceInterface
|
||||
switch key.Kind {
|
||||
case "Secret":
|
||||
dynClient = acdClients.secrets
|
||||
dynClient = client.Resource(secretResource).Namespace(liveObj.GetNamespace())
|
||||
case application.AppProjectKind:
|
||||
dynClient = acdClients.projects
|
||||
dynClient = client.Resource(appprojectsResource).Namespace(liveObj.GetNamespace())
|
||||
case application.ApplicationKind:
|
||||
dynClient = acdClients.applications
|
||||
dynClient = client.Resource(applicationsResource).Namespace(liveObj.GetNamespace())
|
||||
if !dryRun {
|
||||
if finalizers := liveObj.GetFinalizers(); len(finalizers) > 0 {
|
||||
newLive := liveObj.DeepCopy()
|
||||
@@ -274,7 +322,7 @@ func NewImportCommand() *cobra.Command {
|
||||
}
|
||||
}
|
||||
case application.ApplicationSetKind:
|
||||
dynClient = acdClients.applicationSets
|
||||
dynClient = client.Resource(appplicationSetResource).Namespace(liveObj.GetNamespace())
|
||||
default:
|
||||
log.Fatalf("Unexpected kind '%s' in prune list", key.Kind)
|
||||
}
|
||||
@@ -303,6 +351,8 @@ 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
|
||||
}
|
||||
@@ -320,13 +370,14 @@ 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) {
|
||||
func export(w io.Writer, un unstructured.Unstructured, argocdNamespace string) {
|
||||
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)
|
||||
@@ -334,6 +385,9 @@ func export(w io.Writer, un unstructured.Unstructured) {
|
||||
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)
|
||||
|
||||
@@ -35,7 +35,8 @@ func NewNotificationsCommand() *cobra.Command {
|
||||
"notifications",
|
||||
"argocd admin notifications",
|
||||
applications,
|
||||
settings.GetFactorySettings(argocdService, "argocd-notifications-secret", "argocd-notifications-cm", false), func(clientConfig clientcmd.ClientConfig) {
|
||||
settings.GetFactorySettingsForCLI(&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)
|
||||
|
||||
@@ -159,7 +159,7 @@ func NewSettingsCommand() *cobra.Command {
|
||||
|
||||
command.AddCommand(NewValidateSettingsCommand(&opts))
|
||||
command.AddCommand(NewResourceOverridesCommand(&opts))
|
||||
command.AddCommand(NewRBACCommand())
|
||||
command.AddCommand(NewRBACCommand(&opts))
|
||||
|
||||
opts.clientConfig = cli.AddKubectlFlagsToCmd(command)
|
||||
command.PersistentFlags().StringVar(&opts.argocdCMPath, "argocd-cm-path", "", "Path to local argocd-cm.yaml file")
|
||||
|
||||
@@ -18,6 +18,7 @@ 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"
|
||||
)
|
||||
|
||||
@@ -109,7 +110,7 @@ var extensionActions = actionTraitMap{
|
||||
}
|
||||
|
||||
// NewRBACCommand is the command for 'rbac'
|
||||
func NewRBACCommand() *cobra.Command {
|
||||
func NewRBACCommand(cmdCtx commandContext) *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
Use: "rbac",
|
||||
Short: "Validate and test RBAC configuration",
|
||||
@@ -117,13 +118,13 @@ func NewRBACCommand() *cobra.Command {
|
||||
c.HelpFunc()(c, args)
|
||||
},
|
||||
}
|
||||
command.AddCommand(NewRBACCanCommand())
|
||||
command.AddCommand(NewRBACCanCommand(cmdCtx))
|
||||
command.AddCommand(NewRBACValidateCommand())
|
||||
return command
|
||||
}
|
||||
|
||||
// NewRBACCanCommand is the command for 'rbac can-role'
|
||||
func NewRBACCanCommand() *cobra.Command {
|
||||
// NewRBACCanCommand is the command for 'rbac can'
|
||||
func NewRBACCanCommand(cmdCtx commandContext) *cobra.Command {
|
||||
var (
|
||||
policyFile string
|
||||
defaultRole string
|
||||
@@ -175,11 +176,6 @@ 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)
|
||||
@@ -203,6 +199,7 @@ 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
|
||||
}
|
||||
@@ -213,7 +210,30 @@ argocd admin settings rbac can someuser create application 'default/app' --defau
|
||||
defaultRole = newDefaultRole
|
||||
}
|
||||
|
||||
res := checkPolicy(subject, action, resource, subResource, builtinPolicy, userPolicy, defaultRole, matchMode, strict)
|
||||
// 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)
|
||||
|
||||
if res {
|
||||
if !quiet {
|
||||
fmt.Println("Yes")
|
||||
@@ -359,20 +379,16 @@ 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 userPolicy, defaultRole, cm.Data[rbac.ConfigMapMatchModeKey]
|
||||
return rbac.PolicyCSV(cm.Data), defaultRole, cm.Data[rbac.ConfigMapMatchModeKey]
|
||||
}
|
||||
|
||||
// getPolicyConfigMap fetches the RBAC config map from K8s cluster
|
||||
@@ -386,7 +402,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) bool {
|
||||
func checkPolicy(subject, action, resource, subResource, builtinPolicy, userPolicy, defaultRole, matchMode string, strict bool, isLogRbacEnforced func() bool) bool {
|
||||
enf := rbac.NewEnforcer(nil, "argocd", "argocd-rbac-cm", nil)
|
||||
enf.SetDefaultRole(defaultRole)
|
||||
enf.SetMatchMode(matchMode)
|
||||
@@ -427,8 +443,11 @@ 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)
|
||||
}
|
||||
|
||||
|
||||
@@ -130,6 +130,16 @@ 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) {
|
||||
@@ -153,43 +163,97 @@ 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)
|
||||
ok := checkPolicy("role:user", "get", "applications", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil)
|
||||
require.True(t, ok)
|
||||
})
|
||||
t.Run("get clusters", func(t *testing.T) {
|
||||
ok := checkPolicy("role:user", "get", "clusters", "*", assets.BuiltinPolicyCSV, uPol, dRole, "", true)
|
||||
ok := checkPolicy("role:user", "get", "clusters", "*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil)
|
||||
require.True(t, ok)
|
||||
})
|
||||
t.Run("get certificates", func(t *testing.T) {
|
||||
ok := checkPolicy("role:user", "get", "certificates", "*", assets.BuiltinPolicyCSV, uPol, dRole, "", true)
|
||||
ok := checkPolicy("role:user", "get", "certificates", "*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil)
|
||||
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)
|
||||
ok := checkPolicy("role:user", "get", "certificates", "*", assets.BuiltinPolicyCSV, uPol, "role:readonly", "glob", true, nil)
|
||||
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)
|
||||
ok := checkPolicy("role:user", "get", "certificates", "*", "", uPol, "role:readonly", "glob", true, nil)
|
||||
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)
|
||||
ok := checkPolicy("role:user", "get", "certificates", ".*", assets.BuiltinPolicyCSV, uPol, "role:readonly", "regex", true, nil)
|
||||
require.False(t, ok)
|
||||
})
|
||||
t.Run("get logs", func(t *testing.T) {
|
||||
ok := checkPolicy("role:test", "get", "logs", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true)
|
||||
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)
|
||||
require.True(t, ok)
|
||||
})
|
||||
t.Run("create exec", func(t *testing.T) {
|
||||
ok := checkPolicy("role:test", "create", "exec", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true)
|
||||
ok := checkPolicy("role:test", "create", "exec", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil)
|
||||
require.True(t, ok)
|
||||
})
|
||||
t.Run("create applicationsets", func(t *testing.T) {
|
||||
ok := checkPolicy("role:user", "create", "applicationsets", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true)
|
||||
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)
|
||||
require.True(t, ok)
|
||||
})
|
||||
t.Run("delete applicationsets", func(t *testing.T) {
|
||||
ok := checkPolicy("role:user", "delete", "applicationsets", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true)
|
||||
ok := checkPolicy("role:user", "delete", "applicationsets", "*/*", assets.BuiltinPolicyCSV, uPol, dRole, "", true, nil)
|
||||
require.True(t, ok)
|
||||
})
|
||||
}
|
||||
@@ -229,49 +293,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)
|
||||
ok := checkPolicy("role:user", "get", "applications", ".*/.*", builtInPolicy, uPol, dRole, "regex", true, nil)
|
||||
require.True(t, ok)
|
||||
})
|
||||
t.Run("get clusters", func(t *testing.T) {
|
||||
ok := checkPolicy("role:user", "get", "clusters", ".*", builtInPolicy, uPol, dRole, "regex", true)
|
||||
ok := checkPolicy("role:user", "get", "clusters", ".*", builtInPolicy, uPol, dRole, "regex", true, nil)
|
||||
require.True(t, ok)
|
||||
})
|
||||
t.Run("get certificates", func(t *testing.T) {
|
||||
ok := checkPolicy("role:user", "get", "certificates", ".*", builtInPolicy, uPol, dRole, "regex", true)
|
||||
ok := checkPolicy("role:user", "get", "certificates", ".*", builtInPolicy, uPol, dRole, "regex", true, nil)
|
||||
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)
|
||||
ok := checkPolicy("role:user", "get", "certificates", ".*", builtInPolicy, uPol, "role:readonly", "regex", true, nil)
|
||||
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)
|
||||
ok := checkPolicy("role:user", "get", "certificates", ".*", "", uPol, "role:readonly", "regex", true, nil)
|
||||
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)
|
||||
ok := checkPolicy("role:user", "get", "certificates", ".+", builtInPolicy, uPol, dRole, "glob", true, nil)
|
||||
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)
|
||||
ok := checkPolicy("role:user", "get", "logs", ".*/.*", builtInPolicy, uPol, dRole, "glob", true, nil)
|
||||
require.True(t, ok)
|
||||
})
|
||||
t.Run("create exec", func(t *testing.T) {
|
||||
ok := checkPolicy("role:user", "create", "exec", ".*/.*", builtInPolicy, uPol, dRole, "regex", true)
|
||||
ok := checkPolicy("role:user", "create", "exec", ".*/.*", builtInPolicy, uPol, dRole, "regex", true, nil)
|
||||
require.True(t, ok)
|
||||
})
|
||||
t.Run("create applicationsets", func(t *testing.T) {
|
||||
ok := checkPolicy("role:user", "create", "applicationsets", ".*/.*", builtInPolicy, uPol, dRole, "regex", true)
|
||||
ok := checkPolicy("role:user", "create", "applicationsets", ".*/.*", builtInPolicy, uPol, dRole, "regex", true, nil)
|
||||
require.True(t, ok)
|
||||
})
|
||||
t.Run("delete applicationsets", func(t *testing.T) {
|
||||
ok := checkPolicy("role:user", "delete", "applicationsets", ".*/.*", builtInPolicy, uPol, dRole, "regex", true)
|
||||
ok := checkPolicy("role:user", "delete", "applicationsets", ".*/.*", builtInPolicy, uPol, dRole, "regex", true, nil)
|
||||
require.True(t, ok)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewRBACCanCommand(t *testing.T) {
|
||||
command := NewRBACCanCommand()
|
||||
command := NewRBACCanCommand(&settingsOpts{})
|
||||
|
||||
require.NotNil(t, command)
|
||||
assert.Equal(t, "can", command.Name())
|
||||
|
||||
@@ -12,6 +12,10 @@ 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:
|
||||
|
||||
@@ -10,4 +10,6 @@ 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
|
||||
|
||||
|
@@ -2849,6 +2849,7 @@ 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{
|
||||
|
||||
@@ -36,7 +36,7 @@ func TestPrintTreeViewAppResources(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0)
|
||||
|
||||
printTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, false, false, w)
|
||||
printTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, 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, false, false, w)
|
||||
printDetailedTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, w)
|
||||
if err := w.Flush(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -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{}, orphaned bool, listAll bool, w *tabwriter.Writer) {
|
||||
func printDetailedTreeViewAppResourcesNotOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, 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{}, orphaned bool, listAll bool, w *tabwriter.Writer) {
|
||||
func printDetailedTreeViewAppResourcesOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, 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{}, orphaned bool, listAll bool, w *tabwriter.Writer) {
|
||||
func printTreeViewAppResourcesNotOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, 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{}, orphaned bool, listAll bool, w *tabwriter.Writer) {
|
||||
func printTreeViewAppResourcesOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, 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, orphaned, listAll, w)
|
||||
printDetailedTreeViewAppResourcesNotOrphaned(mapUidToNode, mapParentToChild, parentNode, w)
|
||||
}
|
||||
|
||||
if orphaned || listAll {
|
||||
mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.OrphanedNodes)
|
||||
printDetailedTreeViewAppResourcesOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w)
|
||||
printDetailedTreeViewAppResourcesOrphaned(mapUidToNode, mapParentToChild, parentNode, 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, orphaned, listAll, w)
|
||||
printTreeViewAppResourcesNotOrphaned(mapUidToNode, mapParentToChild, parentNode, w)
|
||||
}
|
||||
|
||||
if orphaned || listAll {
|
||||
mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.OrphanedNodes)
|
||||
printTreeViewAppResourcesOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w)
|
||||
printTreeViewAppResourcesOrphaned(mapUidToNode, mapParentToChild, parentNode, w)
|
||||
}
|
||||
} else {
|
||||
headers := []interface{}{"GROUP", "KIND", "NAMESPACE", "NAME", "ORPHANED"}
|
||||
|
||||
@@ -1335,6 +1335,14 @@ 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",
|
||||
@@ -1407,13 +1415,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, &replicaSet2, &job, &deployment},
|
||||
expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1},
|
||||
},
|
||||
// --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, &replicaSet2, &job, &service1, &service2, &deployment},
|
||||
expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &job, &deployment},
|
||||
},
|
||||
// --resource !apps:ReplicaSet:replicaSet-name2
|
||||
{
|
||||
@@ -1427,6 +1435,12 @@ 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",
|
||||
|
||||
@@ -11,6 +11,7 @@ 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"
|
||||
@@ -53,6 +54,7 @@ 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
|
||||
}
|
||||
|
||||
@@ -208,6 +210,75 @@ 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 (
|
||||
|
||||
@@ -34,6 +34,10 @@ 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 = "*"
|
||||
)
|
||||
@@ -220,6 +224,8 @@ func NewClusterSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
var (
|
||||
clusterOptions cmdutil.ClusterOptions
|
||||
clusterName string
|
||||
labels []string
|
||||
annotations []string
|
||||
)
|
||||
command := &cobra.Command{
|
||||
Use: "set NAME",
|
||||
@@ -238,17 +244,25 @@ 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)
|
||||
updatedFields := checkFieldsToUpdate(clusterOptions, labels, annotations)
|
||||
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,
|
||||
Name: clusterOptions.Name,
|
||||
Namespaces: namespaces,
|
||||
Labels: labelsMap,
|
||||
Annotations: annotationsMap,
|
||||
},
|
||||
UpdatedFields: updatedFields,
|
||||
Id: &clusterpkg.ClusterID{
|
||||
@@ -266,11 +280,13 @@ 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) []string {
|
||||
func checkFieldsToUpdate(clusterOptions cmdutil.ClusterOptions, labels []string, annotations []string) []string {
|
||||
var updatedFields []string
|
||||
if clusterOptions.Name != "" {
|
||||
updatedFields = append(updatedFields, clusterFieldName)
|
||||
@@ -278,6 +294,12 @@ func checkFieldsToUpdate(clusterOptions cmdutil.ClusterOptions) []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
|
||||
}
|
||||
|
||||
@@ -341,6 +363,7 @@ 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")
|
||||
@@ -433,6 +456,7 @@ 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()
|
||||
|
||||
@@ -178,6 +178,7 @@ 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 == "" {
|
||||
|
||||
@@ -80,6 +80,7 @@ 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))
|
||||
|
||||
@@ -6,6 +6,8 @@ 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"
|
||||
|
||||
@@ -22,6 +22,7 @@ type RepoOptions struct {
|
||||
GithubAppPrivateKeyPath string
|
||||
GitHubAppEnterpriseBaseURL string
|
||||
Proxy string
|
||||
NoProxy string
|
||||
GCPServiceAccountKeyPath string
|
||||
ForceHttpBasicAuth bool
|
||||
}
|
||||
@@ -44,6 +45,7 @@ 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")
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ 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
|
||||
@@ -221,7 +222,7 @@ const (
|
||||
EnvGitRetryMaxDuration = "ARGOCD_GIT_RETRY_MAX_DURATION"
|
||||
// EnvGitRetryDuration specifies duration of git remote operation retry
|
||||
EnvGitRetryDuration = "ARGOCD_GIT_RETRY_DURATION"
|
||||
// EnvGitRetryFactor specifies fator of git remote operation retry
|
||||
// EnvGitRetryFactor specifies factor of git remote operation retry
|
||||
EnvGitRetryFactor = "ARGOCD_GIT_RETRY_FACTOR"
|
||||
// EnvGitSubmoduleEnabled overrides git submodule support, true by default
|
||||
EnvGitSubmoduleEnabled = "ARGOCD_GIT_MODULES_ENABLED"
|
||||
|
||||
@@ -116,11 +116,11 @@ type ApplicationController struct {
|
||||
applicationClientset appclientset.Interface
|
||||
auditLogger *argo.AuditLogger
|
||||
// queue contains app namespace/name
|
||||
appRefreshQueue workqueue.RateLimitingInterface
|
||||
appRefreshQueue workqueue.TypedRateLimitingInterface[string]
|
||||
// queue contains app namespace/name/comparisonType and used to request app refresh with the predefined comparison type
|
||||
appComparisonTypeRefreshQueue workqueue.RateLimitingInterface
|
||||
appOperationQueue workqueue.RateLimitingInterface
|
||||
projectRefreshQueue workqueue.RateLimitingInterface
|
||||
appComparisonTypeRefreshQueue workqueue.TypedRateLimitingInterface[string]
|
||||
appOperationQueue workqueue.TypedRateLimitingInterface[string]
|
||||
projectRefreshQueue workqueue.TypedRateLimitingInterface[string]
|
||||
appInformer cache.SharedIndexInformer
|
||||
appLister applisters.ApplicationLister
|
||||
projInformer cache.SharedIndexInformer
|
||||
@@ -186,10 +186,10 @@ func NewApplicationController(
|
||||
kubectl: kubectl,
|
||||
applicationClientset: applicationClientset,
|
||||
repoClientset: repoClientset,
|
||||
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)),
|
||||
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)),
|
||||
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.(string))
|
||||
obj, exists, err := ctrl.appInformer.GetIndexer().GetByKey(appKey)
|
||||
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.(string), "/"); len(parts) != 3 {
|
||||
log.Warnf("Unexpected key format in appComparisonTypeRefreshTypeQueue. Key should consists of namespace/name/comparisonType but got: %s", key.(string))
|
||||
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)
|
||||
} 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.(string))
|
||||
obj, exists, err := ctrl.projInformer.GetIndexer().GetByKey(key)
|
||||
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.(string))
|
||||
obj, exists, err := ctrl.appInformer.GetIndexer().GetByKey(appKey)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get application '%s' from informer index: %+v", appKey, err)
|
||||
return
|
||||
@@ -1957,11 +1957,18 @@ 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,
|
||||
@@ -2109,7 +2116,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, false)
|
||||
return app.Namespace == ctrl.namespace || glob.MatchStringInList(ctrl.applicationNamespaces, app.Namespace, glob.REGEXP)
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) canProcessApp(obj interface{}) bool {
|
||||
|
||||
@@ -563,6 +563,42 @@ 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
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -22,6 +21,7 @@ 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 := normalizeLabels("label", appLabels)
|
||||
normalizedLabels := metricsutil.NormalizeLabels("label", appLabels)
|
||||
descAppLabels = prometheus.NewDesc(
|
||||
"argocd_app_labels",
|
||||
"Argo Application labels converted to Prometheus labels",
|
||||
@@ -203,20 +203,6 @@ 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)
|
||||
|
||||
@@ -462,18 +462,18 @@ func TestWorkqueueMetrics(t *testing.T) {
|
||||
|
||||
expectedMetrics := `
|
||||
# TYPE workqueue_adds_total counter
|
||||
workqueue_adds_total{name="test"}
|
||||
workqueue_adds_total{controller="test",name="test"}
|
||||
|
||||
# TYPE workqueue_depth gauge
|
||||
workqueue_depth{name="test"}
|
||||
workqueue_depth{controller="test",name="test"}
|
||||
|
||||
# TYPE workqueue_longest_running_processor_seconds gauge
|
||||
workqueue_longest_running_processor_seconds{name="test"}
|
||||
workqueue_longest_running_processor_seconds{controller="test",name="test"}
|
||||
|
||||
# TYPE workqueue_queue_duration_seconds histogram
|
||||
|
||||
# TYPE workqueue_unfinished_work_seconds gauge
|
||||
workqueue_unfinished_work_seconds{name="test"}
|
||||
workqueue_unfinished_work_seconds{controller="test",name="test"}
|
||||
|
||||
# TYPE workqueue_work_duration_seconds histogram
|
||||
`
|
||||
|
||||
@@ -186,7 +186,6 @@ 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)
|
||||
@@ -228,7 +227,6 @@ 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,
|
||||
@@ -265,7 +263,7 @@ func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alp
|
||||
manifestInfos = append(manifestInfos, manifestInfo)
|
||||
}
|
||||
|
||||
ts.AddCheckpoint("unmarshal_ms")
|
||||
ts.AddCheckpoint("manifests_ms")
|
||||
logCtx := log.WithField("application", app.QualifiedName())
|
||||
for k, v := range ts.Timings() {
|
||||
logCtx = logCtx.WithField(k, v.Milliseconds())
|
||||
@@ -591,7 +589,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(m.resourceTracking, appLabelKey, trackingMethod, app.Name, app.Spec.SyncPolicy)(managedNs, liveObj)
|
||||
_, err = syncNamespace(app.Spec.SyncPolicy)(managedNs, liveObj)
|
||||
if err != nil {
|
||||
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now})
|
||||
failedToLoadObjs = true
|
||||
|
||||
@@ -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(m.resourceTracking, appLabelKey, trackingMethod, app.Name, app.Spec.SyncPolicy)))
|
||||
opts = append(opts, sync.WithNamespaceModifier(syncNamespace(app.Spec.SyncPolicy)))
|
||||
}
|
||||
|
||||
syncCtx, cleanup, err := sync.NewSyncContext(
|
||||
|
||||
@@ -5,12 +5,11 @@ 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(resourceTracking argo.ResourceTracking, appLabelKey string, trackingMethod v1alpha1.TrackingMethod, appName string, syncPolicy *v1alpha1.SyncPolicy) func(m, l *unstructured.Unstructured) (bool, error) {
|
||||
func syncNamespace(syncPolicy *v1alpha1.SyncPolicy) func(m *unstructured.Unstructured, 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 {
|
||||
|
||||
@@ -8,9 +8,7 @@ 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 {
|
||||
@@ -250,7 +248,7 @@ func Test_shouldNamespaceSync(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
actual, err := syncNamespace(argo.NewResourceTracking(), common.LabelKeyAppInstance, argo.TrackingMethodAnnotation, "some-app", tt.syncPolicy)(tt.managedNs, tt.liveNs)
|
||||
actual, err := syncNamespace(tt.syncPolicy)(tt.managedNs, tt.liveNs)
|
||||
require.NoError(t, err)
|
||||
|
||||
if tt.managedNs != nil {
|
||||
|
||||
@@ -61,23 +61,17 @@ window.addEventListener("DOMContentLoaded", function() {
|
||||
var margin = 30;
|
||||
var headerHeight = document.getElementsByClassName("md-header")[0].offsetHeight;
|
||||
const currentVersion = getCurrentVersion();
|
||||
if (currentVersion) {
|
||||
if (currentVersion && currentVersion !== "stable") {
|
||||
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>";
|
||||
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") {
|
||||
} else {
|
||||
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; }}";
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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:
|
||||
|
||||
```typescript
|
||||
```javascript
|
||||
((window) => {
|
||||
const component = () => {
|
||||
return React.createElement(
|
||||
@@ -106,7 +106,7 @@ registerStatusPanelExtension(component: StatusPanelExtensionComponent, title: st
|
||||
|
||||
Below is an example of a simple extension:
|
||||
|
||||
```typescript
|
||||
```javascript
|
||||
((window) => {
|
||||
const component = () => {
|
||||
return React.createElement(
|
||||
@@ -129,32 +129,95 @@ 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:
|
||||
|
||||
```typescript
|
||||
|
||||
```javascript
|
||||
((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);
|
||||
```
|
||||
@@ -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 | | | |
|
||||
| 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.14 | Monday, Dec. 16, 2024 | Monday, Feb. 3, 2025 | | | |
|
||||
| v2.15 | Monday, Mar. 17, 2025 | Monday, May 5, 2025 | | | |
|
||||
|
||||
|
||||
@@ -42,8 +42,11 @@ 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 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=*`.
|
||||
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=*`.
|
||||
- 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
|
||||
|
||||
@@ -25,7 +25,9 @@ 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.
|
||||
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.
|
||||
|
||||
Here is an example:
|
||||
|
||||
@@ -34,6 +36,7 @@ apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: myapps
|
||||
namespace: appNs
|
||||
spec:
|
||||
goTemplate: true
|
||||
goTemplateOptions: ["missingkey=error"]
|
||||
@@ -43,7 +46,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.my-namespace.svc.cluster.local
|
||||
api: http://my-service.appNs.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
|
||||
@@ -53,7 +56,7 @@ spec:
|
||||
template:
|
||||
```
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
For example:
|
||||
```yaml
|
||||
|
||||
@@ -7,6 +7,8 @@ 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
|
||||
|
||||
|
||||
@@ -100,6 +100,17 @@ 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
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ 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.
|
||||
|
||||
|
||||
@@ -188,6 +188,11 @@ 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"
|
||||
|
||||
@@ -242,3 +247,5 @@ 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"
|
||||
|
||||
@@ -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 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.
|
||||
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.
|
||||
|
||||
An example repository with proxy:
|
||||
An example repository with proxy and noProxy:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
@@ -484,10 +484,13 @@ 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
|
||||
|
||||
@@ -130,6 +130,10 @@ 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.
|
||||
|
||||
@@ -12,6 +12,44 @@ 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.
|
||||
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# 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.
|
||||
|
||||
@@ -64,3 +64,6 @@ 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.
|
||||
|
||||
@@ -125,3 +125,9 @@ 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:
|
||||
|
||||

|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
@@ -133,6 +133,7 @@
|
||||
requestedIDTokenClaims:
|
||||
groups:
|
||||
essential: true
|
||||
value: "SecurityGroup"
|
||||
requestedScopes:
|
||||
- openid
|
||||
- profile
|
||||
|
||||
@@ -13,10 +13,20 @@ 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. Set the `exec.enabled` key to `"true"` on the `argocd-cm` ConfigMap.
|
||||
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"
|
||||
```
|
||||
|
||||
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:
|
||||
- ""
|
||||
@@ -24,14 +34,24 @@ 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.
|
||||
|
||||
3. Add RBAC rules to allow your users to `create` the `exec` resource, i.e.
|
||||
p, role:myrole, exec, create, */*, allow
|
||||
|
||||
p, role:myrole, exec, create, */*, allow
|
||||
This can be added either to the `argocd-cm` `Configmap` manifest or an `AppProject` manifest.
|
||||
|
||||
|
||||
See [RBAC Configuration](rbac.md#exec-resource) for more info.
|
||||
See [RBAC Configuration](rbac.md#exec-resource) for more info.
|
||||
|
||||
## Changing allowed shells
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ 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
|
||||
@@ -41,6 +42,7 @@ 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
|
||||
|
||||
@@ -66,13 +68,14 @@ 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.
|
||||
@@ -80,6 +83,7 @@ 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.
|
||||
@@ -87,9 +91,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
|
||||
|
||||
@@ -143,6 +147,7 @@ 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.
|
||||
|
||||
@@ -262,26 +267,27 @@ 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 it’s 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
|
||||
@@ -290,34 +296,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.
|
||||
|
||||
@@ -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.15.0
|
||||
pygments==2.18.0
|
||||
jinja2==3.1.4
|
||||
markdown==3.3.7
|
||||
pymdown-extensions==10.2.1
|
||||
@@ -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.40.0](master/ghcr.io_dexidp_dex_v2.40.0.html) | 0 | 0 | 2 | 2 |
|
||||
| [dex:v2.41.1](master/ghcr.io_dexidp_dex_v2.41.1.html) | 0 | 0 | 0 | 0 |
|
||||
| [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 | 3 | 8 |
|
||||
| [argocd:latest](master/quay.io_argoproj_argocd_latest.html) | 0 | 0 | 2 | 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.0-rc5
|
||||
### v2.12.3
|
||||
|
||||
| | Critical | High | Medium | Low |
|
||||
|---:|:--------:|:----:|:------:|:---:|
|
||||
| [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) | - | - | - | - |
|
||||
| [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) | - | - | - | - |
|
||||
|
||||
### 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 | 6 | 20 |
|
||||
| [argocd:v2.11.7](v2.11.7/quay.io_argoproj_argocd_v2.11.7.html) | 0 | 0 | 5 | 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,20 +58,7 @@ 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 | 6 | 20 |
|
||||
| [argocd:v2.10.16](v2.10.16/quay.io_argoproj_argocd_v2.10.16.html) | 0 | 0 | 5 | 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) | - | - | - | - |
|
||||
|
||||
@@ -456,7 +456,7 @@
|
||||
<div class="header-wrap">
|
||||
<h1 class="project__header__title">Snyk test report</h1>
|
||||
|
||||
<p class="timestamp">August 11th 2024, 12:19:35 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:21:49 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: 22348
|
||||
Line number: 22366
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -553,7 +553,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 22029
|
||||
Line number: 22047
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -599,7 +599,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 22116
|
||||
Line number: 22134
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -645,7 +645,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 22144
|
||||
Line number: 22162
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -691,7 +691,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 22174
|
||||
Line number: 22192
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -737,7 +737,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 22192
|
||||
Line number: 22210
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -783,7 +783,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 22210
|
||||
Line number: 22228
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -829,7 +829,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 22232
|
||||
Line number: 22250
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -881,7 +881,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23286
|
||||
Line number: 23322
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -933,7 +933,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23585
|
||||
Line number: 23621
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -991,7 +991,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 22841
|
||||
Line number: 22859
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1049,7 +1049,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23098
|
||||
Line number: 23128
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1107,7 +1107,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23064
|
||||
Line number: 23082
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1165,7 +1165,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23158
|
||||
Line number: 23188
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1223,7 +1223,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23257
|
||||
Line number: 23293
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1281,7 +1281,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23281
|
||||
Line number: 23317
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1339,7 +1339,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23585
|
||||
Line number: 23621
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1397,7 +1397,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23338
|
||||
Line number: 23374
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1455,7 +1455,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23670
|
||||
Line number: 23706
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1513,7 +1513,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 24060
|
||||
Line number: 24096
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1565,7 +1565,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23078
|
||||
Line number: 23108
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1617,7 +1617,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 22841
|
||||
Line number: 22859
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1669,7 +1669,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23064
|
||||
Line number: 23082
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1721,7 +1721,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23257
|
||||
Line number: 23293
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1779,7 +1779,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 22841
|
||||
Line number: 22859
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1837,7 +1837,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23064
|
||||
Line number: 23082
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1895,7 +1895,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23098
|
||||
Line number: 23128
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1953,7 +1953,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23158
|
||||
Line number: 23188
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2011,7 +2011,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23257
|
||||
Line number: 23293
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2069,7 +2069,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23281
|
||||
Line number: 23317
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2127,7 +2127,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23585
|
||||
Line number: 23621
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2185,7 +2185,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23338
|
||||
Line number: 23374
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2243,7 +2243,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23670
|
||||
Line number: 23706
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2301,7 +2301,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 24060
|
||||
Line number: 24096
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2357,7 +2357,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 22988
|
||||
Line number: 23006
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2413,7 +2413,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23106
|
||||
Line number: 23136
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2469,7 +2469,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23081
|
||||
Line number: 23111
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2525,7 +2525,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23191
|
||||
Line number: 23227
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2581,7 +2581,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23274
|
||||
Line number: 23310
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2637,7 +2637,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23288
|
||||
Line number: 23324
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2693,7 +2693,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23592
|
||||
Line number: 23628
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2749,7 +2749,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23558
|
||||
Line number: 23594
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2805,7 +2805,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 23961
|
||||
Line number: 23997
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2861,7 +2861,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 24261
|
||||
Line number: 24297
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -456,7 +456,7 @@
|
||||
<div class="header-wrap">
|
||||
<h1 class="project__header__title">Snyk test report</h1>
|
||||
|
||||
<p class="timestamp">August 11th 2024, 12:19:44 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:21:57 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: 1120
|
||||
Line number: 1138
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1419
|
||||
Line number: 1437
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1003,7 +1003,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 932
|
||||
Line number: 944
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1119,7 +1119,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 992
|
||||
Line number: 1004
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1177,7 +1177,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1091
|
||||
Line number: 1109
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1235,7 +1235,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1115
|
||||
Line number: 1133
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1293,7 +1293,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1419
|
||||
Line number: 1437
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1351,7 +1351,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1172
|
||||
Line number: 1190
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1409,7 +1409,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1504
|
||||
Line number: 1522
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1467,7 +1467,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1894
|
||||
Line number: 1912
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1519,7 +1519,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 912
|
||||
Line number: 924
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1675,7 +1675,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1091
|
||||
Line number: 1109
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1849,7 +1849,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 932
|
||||
Line number: 944
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1907,7 +1907,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 992
|
||||
Line number: 1004
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1965,7 +1965,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1091
|
||||
Line number: 1109
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2023,7 +2023,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1115
|
||||
Line number: 1133
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2081,7 +2081,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1419
|
||||
Line number: 1437
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2139,7 +2139,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1172
|
||||
Line number: 1190
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2197,7 +2197,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1504
|
||||
Line number: 1522
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2255,7 +2255,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1894
|
||||
Line number: 1912
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2367,7 +2367,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 940
|
||||
Line number: 952
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2423,7 +2423,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 915
|
||||
Line number: 927
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2479,7 +2479,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1025
|
||||
Line number: 1043
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2535,7 +2535,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1108
|
||||
Line number: 1126
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2591,7 +2591,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1122
|
||||
Line number: 1140
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2647,7 +2647,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1426
|
||||
Line number: 1444
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2703,7 +2703,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1392
|
||||
Line number: 1410
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2759,7 +2759,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 1795
|
||||
Line number: 1813
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2815,7 +2815,7 @@
|
||||
</li>
|
||||
|
||||
<li class="card__meta__item">
|
||||
Line number: 2095
|
||||
Line number: 2113
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -456,7 +456,7 @@
|
||||
<div class="header-wrap">
|
||||
<h1 class="project__header__title">Snyk test report</h1>
|
||||
|
||||
<p class="timestamp">August 11th 2024, 12:17:29 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:19:46 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>2085</span> <span>dependencies</span></div>
|
||||
<div class="meta-count"><span>2128</span> <span>dependencies</span></div>
|
||||
</div><!-- .meta-counts -->
|
||||
</div><!-- .layout-container--short -->
|
||||
</header><!-- .project__header -->
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -456,7 +456,7 @@
|
||||
<div class="header-wrap">
|
||||
<h1 class="project__header__title">Snyk test report</h1>
|
||||
|
||||
<p class="timestamp">August 11th 2024, 12:17:43 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:20:01 am (UTC+00:00)</p>
|
||||
</div>
|
||||
<div class="source-panel">
|
||||
<span>Scanned the following path:</span>
|
||||
|
||||
@@ -456,7 +456,7 @@
|
||||
<div class="header-wrap">
|
||||
<h1 class="project__header__title">Snyk test report</h1>
|
||||
|
||||
<p class="timestamp">August 11th 2024, 12:17:48 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:20:05 am (UTC+00:00)</p>
|
||||
</div>
|
||||
<div class="source-panel">
|
||||
<span>Scanned the following paths:</span>
|
||||
|
||||
@@ -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="18 known vulnerabilities found in 65 vulnerable dependency paths.">
|
||||
<meta name="description" content="17 known vulnerabilities found in 61 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">August 11th 2024, 12:18:05 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:20:22 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>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 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><!-- .meta-counts -->
|
||||
</div><!-- .layout-container--short -->
|
||||
</header><!-- .project__header -->
|
||||
@@ -480,127 +480,6 @@
|
||||
|
||||
<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 "endless output" and "denial of service" 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">
|
||||
@@ -730,7 +609,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.2
|
||||
systemd/libsystemd0@255.4-1ubuntu8.4
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
libgcrypt20@1.10.3-2build1
|
||||
|
||||
@@ -806,7 +685,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -821,7 +700,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -838,7 +717,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -853,7 +732,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -870,7 +749,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -889,7 +768,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -902,7 +781,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.4
|
||||
openssh/openssh-client@1:9.6p1-3ubuntu13.5
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
|
||||
@@ -915,7 +794,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
|
||||
@@ -928,7 +807,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
libssh/libssh-4@0.10.6-2build2
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -1579,7 +1458,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -1594,7 +1473,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -1611,7 +1490,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -1626,7 +1505,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -1643,7 +1522,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -1662,7 +1541,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -1675,7 +1554,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.4
|
||||
openssh/openssh-client@1:9.6p1-3ubuntu13.5
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
|
||||
@@ -1688,7 +1567,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
|
||||
@@ -1701,7 +1580,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
libssh/libssh-4@0.10.6-2build2
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -1787,7 +1666,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -1802,7 +1681,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -1819,7 +1698,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -1834,7 +1713,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -1851,7 +1730,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -1870,7 +1749,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -1883,7 +1762,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.4
|
||||
openssh/openssh-client@1:9.6p1-3ubuntu13.5
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
|
||||
@@ -1896,7 +1775,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
krb5/libgssapi-krb5-2@1.20.1-6ubuntu2.1
|
||||
|
||||
@@ -1909,7 +1788,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.2
|
||||
curl/libcurl3t64-gnutls@8.5.0-2ubuntu10.3
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
libssh/libssh-4@0.10.6-2build2
|
||||
<span class="list-paths__item__arrow">›</span>
|
||||
@@ -2124,7 +2003,7 @@
|
||||
|
||||
<li class="card__meta__item">Introduced through:
|
||||
|
||||
docker-image|quay.io/argoproj/argocd@latest and glibc/libc-bin@2.39-0ubuntu8.2
|
||||
docker-image|quay.io/argoproj/argocd@latest and glibc/libc-bin@2.39-0ubuntu8.3
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
@@ -2139,7 +2018,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.2
|
||||
glibc/libc-bin@2.39-0ubuntu8.3
|
||||
|
||||
</span>
|
||||
|
||||
@@ -2148,7 +2027,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.2
|
||||
glibc/libc6@2.39-0ubuntu8.3
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
@@ -456,7 +456,7 @@
|
||||
<div class="header-wrap">
|
||||
<h1 class="project__header__title">Snyk test report</h1>
|
||||
|
||||
<p class="timestamp">August 11th 2024, 12:18:09 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:20:25 am (UTC+00:00)</p>
|
||||
</div>
|
||||
<div class="source-panel">
|
||||
<span>Scanned the following paths:</span>
|
||||
|
||||
@@ -456,7 +456,7 @@
|
||||
<div class="header-wrap">
|
||||
<h1 class="project__header__title">Snyk test report</h1>
|
||||
|
||||
<p class="timestamp">August 11th 2024, 12:26:21 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:28:24 am (UTC+00:00)</p>
|
||||
</div>
|
||||
<div class="source-panel">
|
||||
<span>Scanned the following path:</span>
|
||||
|
||||
@@ -456,7 +456,7 @@
|
||||
<div class="header-wrap">
|
||||
<h1 class="project__header__title">Snyk test report</h1>
|
||||
|
||||
<p class="timestamp">August 11th 2024, 12:26:29 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:28:33 am (UTC+00:00)</p>
|
||||
</div>
|
||||
<div class="source-panel">
|
||||
<span>Scanned the following path:</span>
|
||||
|
||||
@@ -456,7 +456,7 @@
|
||||
<div class="header-wrap">
|
||||
<h1 class="project__header__title">Snyk test report</h1>
|
||||
|
||||
<p class="timestamp">August 11th 2024, 12:24:28 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:26:34 am (UTC+00:00)</p>
|
||||
</div>
|
||||
<div class="source-panel">
|
||||
<span>Scanned the following paths:</span>
|
||||
|
||||
@@ -456,7 +456,7 @@
|
||||
<div class="header-wrap">
|
||||
<h1 class="project__header__title">Snyk test report</h1>
|
||||
|
||||
<p class="timestamp">August 11th 2024, 12:24:35 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:26:41 am (UTC+00:00)</p>
|
||||
</div>
|
||||
<div class="source-panel">
|
||||
<span>Scanned the following paths:</span>
|
||||
|
||||
@@ -456,7 +456,7 @@
|
||||
<div class="header-wrap">
|
||||
<h1 class="project__header__title">Snyk test report</h1>
|
||||
|
||||
<p class="timestamp">August 11th 2024, 12:24:39 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:26:45 am (UTC+00:00)</p>
|
||||
</div>
|
||||
<div class="source-panel">
|
||||
<span>Scanned the following path:</span>
|
||||
|
||||
@@ -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="36 known vulnerabilities found in 222 vulnerable dependency paths.">
|
||||
<meta name="description" content="35 known vulnerabilities found in 221 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">August 11th 2024, 12:24:56 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:27:02 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>36</span> <span>known vulnerabilities</span></div>
|
||||
<div class="meta-count"><span>222 vulnerable dependency paths</span></div>
|
||||
<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>2278</span> <span>dependencies</span></div>
|
||||
</div><!-- .meta-counts -->
|
||||
</div><!-- .layout-container--short -->
|
||||
@@ -559,82 +559,6 @@
|
||||
<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 "endless output" and "denial of service" 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>
|
||||
@@ -2111,7 +2035,7 @@
|
||||
|
||||
</div><!-- .card -->
|
||||
<div class="card card--vuln disclosure--not-new severity--medium" data-snyk-test="medium">
|
||||
<h2 class="card__title">CVE-2024-7264</h2>
|
||||
<h2 class="card__title">Out-of-bounds Read</h2>
|
||||
<div class="card__section">
|
||||
|
||||
<div class="label label--medium">
|
||||
@@ -2179,10 +2103,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/>
|
||||
|
||||
@@ -456,7 +456,7 @@
|
||||
<div class="header-wrap">
|
||||
<h1 class="project__header__title">Snyk test report</h1>
|
||||
|
||||
<p class="timestamp">August 11th 2024, 12:25:01 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:27:05 am (UTC+00:00)</p>
|
||||
</div>
|
||||
<div class="source-panel">
|
||||
<span>Scanned the following paths:</span>
|
||||
|
||||
@@ -456,7 +456,7 @@
|
||||
<div class="header-wrap">
|
||||
<h1 class="project__header__title">Snyk test report</h1>
|
||||
|
||||
<p class="timestamp">August 11th 2024, 12:24:10 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:26:17 am (UTC+00:00)</p>
|
||||
</div>
|
||||
<div class="source-panel">
|
||||
<span>Scanned the following path:</span>
|
||||
|
||||
@@ -456,7 +456,7 @@
|
||||
<div class="header-wrap">
|
||||
<h1 class="project__header__title">Snyk test report</h1>
|
||||
|
||||
<p class="timestamp">August 11th 2024, 12:24:19 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:26:25 am (UTC+00:00)</p>
|
||||
</div>
|
||||
<div class="source-panel">
|
||||
<span>Scanned the following path:</span>
|
||||
|
||||
@@ -456,7 +456,7 @@
|
||||
<div class="header-wrap">
|
||||
<h1 class="project__header__title">Snyk test report</h1>
|
||||
|
||||
<p class="timestamp">August 11th 2024, 12:22:17 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:24:27 am (UTC+00:00)</p>
|
||||
</div>
|
||||
<div class="source-panel">
|
||||
<span>Scanned the following paths:</span>
|
||||
|
||||
@@ -456,7 +456,7 @@
|
||||
<div class="header-wrap">
|
||||
<h1 class="project__header__title">Snyk test report</h1>
|
||||
|
||||
<p class="timestamp">August 11th 2024, 12:22:23 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:24:34 am (UTC+00:00)</p>
|
||||
</div>
|
||||
<div class="source-panel">
|
||||
<span>Scanned the following paths:</span>
|
||||
|
||||
@@ -456,7 +456,7 @@
|
||||
<div class="header-wrap">
|
||||
<h1 class="project__header__title">Snyk test report</h1>
|
||||
|
||||
<p class="timestamp">August 11th 2024, 12:22:29 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:24:38 am (UTC+00:00)</p>
|
||||
</div>
|
||||
<div class="source-panel">
|
||||
<span>Scanned the following path:</span>
|
||||
|
||||
@@ -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="36 known vulnerabilities found in 222 vulnerable dependency paths.">
|
||||
<meta name="description" content="35 known vulnerabilities found in 221 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">August 11th 2024, 12:22:47 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 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>36</span> <span>known vulnerabilities</span></div>
|
||||
<div class="meta-count"><span>222 vulnerable dependency paths</span></div>
|
||||
<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>2280</span> <span>dependencies</span></div>
|
||||
</div><!-- .meta-counts -->
|
||||
</div><!-- .layout-container--short -->
|
||||
@@ -559,82 +559,6 @@
|
||||
<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 "endless output" and "denial of service" 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>
|
||||
@@ -2111,7 +2035,7 @@
|
||||
|
||||
</div><!-- .card -->
|
||||
<div class="card card--vuln disclosure--not-new severity--medium" data-snyk-test="medium">
|
||||
<h2 class="card__title">CVE-2024-7264</h2>
|
||||
<h2 class="card__title">Out-of-bounds Read</h2>
|
||||
<div class="card__section">
|
||||
|
||||
<div class="label label--medium">
|
||||
@@ -2179,10 +2103,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/>
|
||||
|
||||
@@ -456,7 +456,7 @@
|
||||
<div class="header-wrap">
|
||||
<h1 class="project__header__title">Snyk test report</h1>
|
||||
|
||||
<p class="timestamp">August 11th 2024, 12:22:51 am (UTC+00:00)</p>
|
||||
<p class="timestamp">September 1st 2024, 12:24:59 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
Reference in New Issue
Block a user