This commit is contained in:
sangeer 2025-07-17 11:44:17 +00:00 committed by GitHub
parent d83ef2c224
commit 8b49d21bb3
243 changed files with 7199 additions and 28847 deletions

View file

@ -112,7 +112,7 @@ jobs:
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0
with:
# renovate: datasource=go packageName=github.com/golangci/golangci-lint versioning=regex:^v(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?$
version: v2.2.2
version: v2.1.6
args: --verbose
test-go:

View file

@ -73,7 +73,7 @@ jobs:
cache: false
- name: Install cosign
uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3.9.1
uses: sigstore/cosign-installer@fb28c2b6339dcd94da6e4cbcbc5e888961f6f8c3 # v3.9.0
- uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1

View file

@ -80,7 +80,7 @@ release:
All Argo CD container images are signed by cosign. A Provenance is generated for container images and CLI binaries which meet the SLSA Level 3 specifications. See the [documentation](https://argo-cd.readthedocs.io/en/stable/operator-manual/signed-release-assets) on how to verify.
## Release Notes Blog Post
For a detailed breakdown of the key changes and improvements in this release, check out the [official blog post](https://blog.argoproj.io/argo-cd-v3-0-release-candidate-a0b933f4e58f)
For a detailed breakdown of the key changes and improvements in this release, check out the [official blog post](https://blog.argoproj.io/announcing-argo-cd-v3-1-f4389bc783c8)
## Upgrading

View file

@ -32,9 +32,6 @@ packages:
github.com/argoproj/argo-cd/v3/controller/cache:
interfaces:
LiveStateCache: {}
github.com/argoproj/argo-cd/v3/controller/hydrator:
interfaces:
Dependencies: {}
github.com/argoproj/argo-cd/v3/pkg/apiclient/cluster:
interfaces:
ClusterServiceServer: {}

View file

@ -1,4 +1,4 @@
ARG BASE_IMAGE=docker.io/library/ubuntu:25.04@sha256:10bb10bb062de665d4dc3e0ea36715270ead632cfcb74d08ca2273712a0dfb42
ARG BASE_IMAGE=docker.io/library/ubuntu:24.04@sha256:80dd3c3b9c6cecb9f1667e9290b3bc61b78c2678c02cbdae5f0fea92cc6734ab
####################################################################################################
# Builder image
# Initial stage which pulls prepares build dependencies and CLI tooling we need for our final image

View file

@ -604,7 +604,6 @@ install-test-tools-local:
.PHONY: install-codegen-tools-local
install-codegen-tools-local:
./hack/install.sh codegen-tools
./hack/install.sh codegen-go-tools
# Installs all tools required for running codegen (Go packages)
.PHONY: install-go-tools-local

View file

@ -3,9 +3,9 @@ header:
expiration-date: '2024-10-31T00:00:00.000Z' # One year from initial release.
last-updated: '2023-10-27'
last-reviewed: '2023-10-27'
commit-hash: 320f46f06beaf75f9c406e3a47e2e09d36e2047a
commit-hash: 226a670fe6b3c6769ff6d18e6839298a58e4577d
project-url: https://github.com/argoproj/argo-cd
project-release: v3.2.0
project-release: v3.1.0
changelog: https://github.com/argoproj/argo-cd/releases
license: https://github.com/argoproj/argo-cd/blob/master/LICENSE
project-lifecycle:

View file

@ -69,7 +69,7 @@ docker_build_with_restart(
],
platform=platform,
live_update=[
sync('.tilt-bin/argocd_linux', '/usr/local/bin/argocd'),
sync('.tilt-bin/argocd_linux_amd64', '/usr/local/bin/argocd'),
],
only=[
'.tilt-bin',
@ -260,7 +260,6 @@ local_resource(
'make lint-local',
deps = code_deps,
allow_parallel=True,
resource_deps=['vendor']
)
local_resource(

View file

@ -173,7 +173,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Info Support](https://www.infosupport.com/)
1. [InsideBoard](https://www.insideboard.com)
1. [Instruqt](https://www.instruqt.com)
1. [Intel](https://www.intel.com)
1. [Intuit](https://www.intuit.com/)
1. [Jellysmack](https://www.jellysmack.com)
1. [Joblift](https://joblift.com/)
@ -322,7 +321,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [SEKAI](https://www.sekai.io/)
1. [Semgrep](https://semgrep.com)
1. [Shield](https://shield.com)
1. [Shipfox](https://www.shipfox.io)
1. [SI Analytics](https://si-analytics.ai)
1. [Sidewalk Entertainment](https://sidewalkplay.com/)
1. [Skit](https://skit.ai/)

View file

@ -1 +1 @@
3.2.0
3.1.0-rc3

View file

@ -16,7 +16,6 @@ package controllers
import (
"context"
"errors"
"fmt"
"reflect"
"runtime/debug"
@ -68,8 +67,6 @@ const (
// https://github.com/argoproj-labs/argocd-notifications/blob/33d345fa838829bb50fca5c08523aba380d2c12b/pkg/controller/state.go#L17
NotifiedAnnotationKey = "notified.notifications.argoproj.io"
ReconcileRequeueOnValidationError = time.Minute * 3
ReverseDeletionOrder = "Reverse"
AllAtOnceDeletionOrder = "AllAtOnce"
)
var defaultPreservedAnnotations = []string{
@ -77,11 +74,6 @@ var defaultPreservedAnnotations = []string{
argov1alpha1.AnnotationKeyRefresh,
}
type deleteInOrder struct {
AppName string
Step int
}
// ApplicationSetReconciler reconciles a ApplicationSet object
type ApplicationSetReconciler struct {
client.Client
@ -147,19 +139,6 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
}
logCtx.Debugf("ownerReferences referring %s is deleted from generated applications", appsetName)
}
if isProgressiveSyncDeletionOrderReversed(&applicationSetInfo) {
logCtx.Debugf("DeletionOrder is set as Reverse on %s", appsetName)
currentApplications, err := r.getCurrentApplications(ctx, applicationSetInfo)
if err != nil {
return ctrl.Result{}, err
}
requeueTime, err := r.performReverseDeletion(ctx, logCtx, applicationSetInfo, currentApplications)
if err != nil {
return ctrl.Result{}, err
} else if requeueTime > 0 {
return ctrl.Result{RequeueAfter: requeueTime}, err
}
}
controllerutil.RemoveFinalizer(&applicationSetInfo, argov1alpha1.ResourcesFinalizerName)
if err := r.Update(ctx, &applicationSetInfo); err != nil {
return ctrl.Result{}, err
@ -175,7 +154,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
// Log a warning if there are unrecognized generators
_ = utils.CheckInvalidGenerators(&applicationSetInfo)
// desiredApplications is the main list of all expected Applications from all generators in this appset.
generatedApplications, applicationSetReason, err := template.GenerateApplications(logCtx, applicationSetInfo, r.Generators, r.Renderer, r.Client)
desiredApplications, applicationSetReason, err := template.GenerateApplications(logCtx, applicationSetInfo, r.Generators, r.Renderer, r.Client)
if err != nil {
logCtx.Errorf("unable to generate applications: %v", err)
_ = r.setApplicationSetStatusCondition(ctx,
@ -193,7 +172,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
parametersGenerated = true
validateErrors, err := r.validateGeneratedApplications(ctx, generatedApplications, applicationSetInfo)
validateErrors, err := r.validateGeneratedApplications(ctx, desiredApplications, applicationSetInfo)
if err != nil {
// While some generators may return an error that requires user intervention,
// other generators reference external resources that may change to cause
@ -246,7 +225,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
appMap[app.Name] = app
}
appSyncMap, err = r.performProgressiveSyncs(ctx, logCtx, applicationSetInfo, currentApplications, generatedApplications, appMap)
appSyncMap, err = r.performProgressiveSyncs(ctx, logCtx, applicationSetInfo, currentApplications, desiredApplications, appMap)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to perform progressive sync reconciliation for application set: %w", err)
}
@ -254,23 +233,17 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
}
var validApps []argov1alpha1.Application
for i := range generatedApplications {
if validateErrors[generatedApplications[i].QualifiedName()] == nil {
validApps = append(validApps, generatedApplications[i])
for i := range desiredApplications {
if validateErrors[i] == nil {
validApps = append(validApps, desiredApplications[i])
}
}
if len(validateErrors) > 0 {
errorApps := make([]string, 0, len(validateErrors))
for key := range validateErrors {
errorApps = append(errorApps, key)
}
sort.Strings(errorApps)
var message string
for _, appName := range errorApps {
message = validateErrors[appName].Error()
logCtx.WithField("application", appName).Errorf("validation error found during application validation: %s", message)
for _, v := range validateErrors {
message = v.Error()
logCtx.Errorf("validation error found during application validation: %s", message)
}
if len(validateErrors) > 1 {
// Only the last message gets added to the appset status, to keep the size reasonable.
@ -325,12 +298,12 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
}
if utils.DefaultPolicy(applicationSetInfo.Spec.SyncPolicy, r.Policy, r.EnablePolicyOverride).AllowDelete() {
err = r.deleteInCluster(ctx, logCtx, applicationSetInfo, generatedApplications)
err = r.deleteInCluster(ctx, logCtx, applicationSetInfo, desiredApplications)
if err != nil {
_ = r.setApplicationSetStatusCondition(ctx,
&applicationSetInfo,
argov1alpha1.ApplicationSetCondition{
Type: argov1alpha1.ApplicationSetConditionErrorOccurred,
Type: argov1alpha1.ApplicationSetConditionResourcesUpToDate,
Message: err.Error(),
Reason: argov1alpha1.ApplicationSetReasonDeleteApplicationError,
Status: argov1alpha1.ApplicationSetConditionStatusTrue,
@ -384,169 +357,120 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
}, nil
}
func (r *ApplicationSetReconciler) performReverseDeletion(ctx context.Context, logCtx *log.Entry, appset argov1alpha1.ApplicationSet, currentApps []argov1alpha1.Application) (time.Duration, error) {
requeueTime := 10 * time.Second
stepLength := len(appset.Spec.Strategy.RollingSync.Steps)
// map applications by name using current applications
appMap := make(map[string]*argov1alpha1.Application)
for _, app := range currentApps {
appMap[app.Name] = &app
}
// Get Rolling Sync Step Maps
_, appStepMap := r.buildAppDependencyList(logCtx, appset, currentApps)
// reverse the AppStepMap to perform deletion
var reverseDeleteAppSteps []deleteInOrder
for appName, appStep := range appStepMap {
reverseDeleteAppSteps = append(reverseDeleteAppSteps, deleteInOrder{appName, stepLength - appStep - 1})
}
sort.Slice(reverseDeleteAppSteps, func(i, j int) bool {
return reverseDeleteAppSteps[i].Step < reverseDeleteAppSteps[j].Step
})
for _, step := range reverseDeleteAppSteps {
logCtx.Infof("step %v : app %v", step.Step, step.AppName)
app := appMap[step.AppName]
retrievedApp := argov1alpha1.Application{}
if err := r.Get(ctx, types.NamespacedName{Name: app.Name, Namespace: app.Namespace}, &retrievedApp); err != nil {
if apierrors.IsNotFound(err) {
logCtx.Infof("application %s successfully deleted", step.AppName)
continue
}
}
// Check if the application is already being deleted
if retrievedApp.DeletionTimestamp != nil {
logCtx.Infof("application %s has been marked for deletion, but object not removed yet", step.AppName)
if time.Since(retrievedApp.DeletionTimestamp.Time) > 2*time.Minute {
return 0, errors.New("application has not been deleted in over 2 minutes")
}
}
// The application has not been deleted yet, trigger its deletion
if err := r.Delete(ctx, &retrievedApp); err != nil {
return 0, err
}
return requeueTime, nil
}
logCtx.Infof("completed reverse deletion for ApplicationSet %v", appset.Name)
return 0, nil
}
func getParametersGeneratedCondition(parametersGenerated bool, message string) argov1alpha1.ApplicationSetCondition {
var parametersGeneratedCondition argov1alpha1.ApplicationSetCondition
var paramtersGeneratedCondition argov1alpha1.ApplicationSetCondition
if parametersGenerated {
parametersGeneratedCondition = argov1alpha1.ApplicationSetCondition{
paramtersGeneratedCondition = argov1alpha1.ApplicationSetCondition{
Type: argov1alpha1.ApplicationSetConditionParametersGenerated,
Message: "Successfully generated parameters for all Applications",
Reason: argov1alpha1.ApplicationSetReasonParametersGenerated,
Status: argov1alpha1.ApplicationSetConditionStatusTrue,
}
} else {
parametersGeneratedCondition = argov1alpha1.ApplicationSetCondition{
paramtersGeneratedCondition = argov1alpha1.ApplicationSetCondition{
Type: argov1alpha1.ApplicationSetConditionParametersGenerated,
Message: message,
Reason: argov1alpha1.ApplicationSetReasonErrorOccurred,
Status: argov1alpha1.ApplicationSetConditionStatusFalse,
}
}
return parametersGeneratedCondition
return paramtersGeneratedCondition
}
func (r *ApplicationSetReconciler) setApplicationSetStatusCondition(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet, condition argov1alpha1.ApplicationSetCondition, parametersGenerated bool) error {
// Initialize the default condition types that this method evaluates
func getResourceUpToDateCondition(errorOccurred bool, message string, reason string) argov1alpha1.ApplicationSetCondition {
var resourceUpToDateCondition argov1alpha1.ApplicationSetCondition
if errorOccurred {
resourceUpToDateCondition = argov1alpha1.ApplicationSetCondition{
Type: argov1alpha1.ApplicationSetConditionResourcesUpToDate,
Message: message,
Reason: reason,
Status: argov1alpha1.ApplicationSetConditionStatusFalse,
}
} else {
resourceUpToDateCondition = argov1alpha1.ApplicationSetCondition{
Type: argov1alpha1.ApplicationSetConditionResourcesUpToDate,
Message: "ApplicationSet up to date",
Reason: argov1alpha1.ApplicationSetReasonApplicationSetUpToDate,
Status: argov1alpha1.ApplicationSetConditionStatusTrue,
}
}
return resourceUpToDateCondition
}
func (r *ApplicationSetReconciler) setApplicationSetStatusCondition(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet, condition argov1alpha1.ApplicationSetCondition, paramtersGenerated bool) error {
// check if error occurred during reconcile process
errOccurred := condition.Type == argov1alpha1.ApplicationSetConditionErrorOccurred
var errOccurredCondition argov1alpha1.ApplicationSetCondition
if errOccurred {
errOccurredCondition = condition
} else {
errOccurredCondition = argov1alpha1.ApplicationSetCondition{
Type: argov1alpha1.ApplicationSetConditionErrorOccurred,
Message: "Successfully generated parameters for all Applications",
Reason: argov1alpha1.ApplicationSetReasonApplicationSetUpToDate,
Status: argov1alpha1.ApplicationSetConditionStatusFalse,
}
}
paramtersGeneratedCondition := getParametersGeneratedCondition(paramtersGenerated, condition.Message)
resourceUpToDateCondition := getResourceUpToDateCondition(errOccurred, condition.Message, condition.Reason)
evaluatedTypes := map[argov1alpha1.ApplicationSetConditionType]bool{
argov1alpha1.ApplicationSetConditionErrorOccurred: true,
argov1alpha1.ApplicationSetConditionParametersGenerated: true,
argov1alpha1.ApplicationSetConditionErrorOccurred: false,
argov1alpha1.ApplicationSetConditionResourcesUpToDate: false,
argov1alpha1.ApplicationSetConditionRolloutProgressing: false,
argov1alpha1.ApplicationSetConditionResourcesUpToDate: true,
}
// Evaluate current condition
evaluatedTypes[condition.Type] = true
newConditions := []argov1alpha1.ApplicationSetCondition{condition}
newConditions := []argov1alpha1.ApplicationSetCondition{errOccurredCondition, paramtersGeneratedCondition, resourceUpToDateCondition}
if !isRollingSyncStrategy(applicationSet) {
// Progressing sync is always evaluated so conditions are removed when it is not enabled
if progressiveSyncsRollingSyncStrategyEnabled(applicationSet) {
evaluatedTypes[argov1alpha1.ApplicationSetConditionRolloutProgressing] = true
}
// Evaluate ParametersGenerated since it is always provided
if condition.Type != argov1alpha1.ApplicationSetConditionParametersGenerated {
newConditions = append(newConditions, getParametersGeneratedCondition(parametersGenerated, condition.Message))
}
// Evaluate dependencies between conditions.
switch condition.Type {
case argov1alpha1.ApplicationSetConditionResourcesUpToDate:
if condition.Status == argov1alpha1.ApplicationSetConditionStatusTrue {
// If the resources are up to date, we know there was no errors
evaluatedTypes[argov1alpha1.ApplicationSetConditionErrorOccurred] = true
newConditions = append(newConditions, argov1alpha1.ApplicationSetCondition{
Type: argov1alpha1.ApplicationSetConditionErrorOccurred,
Status: argov1alpha1.ApplicationSetConditionStatusFalse,
Reason: condition.Reason,
Message: condition.Message,
})
}
case argov1alpha1.ApplicationSetConditionErrorOccurred:
if condition.Status == argov1alpha1.ApplicationSetConditionStatusTrue {
// If there is an error anywhere in the reconciliation, we cannot consider the resources up to date
evaluatedTypes[argov1alpha1.ApplicationSetConditionResourcesUpToDate] = true
newConditions = append(newConditions, argov1alpha1.ApplicationSetCondition{
Type: argov1alpha1.ApplicationSetConditionResourcesUpToDate,
Status: argov1alpha1.ApplicationSetConditionStatusFalse,
Reason: argov1alpha1.ApplicationSetReasonErrorOccurred,
Message: condition.Message,
})
}
case argov1alpha1.ApplicationSetConditionRolloutProgressing:
if !isRollingSyncStrategy(applicationSet) {
// if the condition is a rolling sync and it is disabled, ignore it
evaluatedTypes[condition.Type] = false
if condition.Type == argov1alpha1.ApplicationSetConditionRolloutProgressing {
newConditions = append(newConditions, condition)
}
}
// Update the applicationSet conditions
previousConditions := applicationSet.Status.Conditions
applicationSet.Status.SetConditions(newConditions, evaluatedTypes)
// Try to not call get/update if nothing has changed
needToUpdateConditions := len(applicationSet.Status.Conditions) != len(previousConditions)
if !needToUpdateConditions {
for i, c := range applicationSet.Status.Conditions {
previous := previousConditions[i]
if c.Type != previous.Type || c.Reason != previous.Reason || c.Status != previous.Status || c.Message != previous.Message {
needToUpdateConditions := false
for _, condition := range newConditions {
// do nothing if appset already has same condition
for _, c := range applicationSet.Status.Conditions {
if c.Type == condition.Type && (c.Reason != condition.Reason || c.Status != condition.Status || c.Message != condition.Message) {
needToUpdateConditions = true
break
}
}
}
if !needToUpdateConditions {
return nil
}
// DefaultRetry will retry 5 times with a backoff factor of 1, jitter of 0.1 and a duration of 10ms
err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
updatedAppset := &argov1alpha1.ApplicationSet{}
if err := r.Get(ctx, types.NamespacedName{Namespace: applicationSet.Namespace, Name: applicationSet.Name}, updatedAppset); err != nil {
if client.IgnoreNotFound(err) != nil {
return nil
if needToUpdateConditions || len(applicationSet.Status.Conditions) < len(newConditions) {
// fetch updated Application Set object before updating it
// DefaultRetry will retry 5 times with a backoff factor of 1, jitter of 0.1 and a duration of 10ms
err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
namespacedName := types.NamespacedName{Namespace: applicationSet.Namespace, Name: applicationSet.Name}
updatedAppset := &argov1alpha1.ApplicationSet{}
if err := r.Get(ctx, namespacedName, updatedAppset); err != nil {
if client.IgnoreNotFound(err) != nil {
return nil
}
return fmt.Errorf("error fetching updated application set: %w", err)
}
return fmt.Errorf("error fetching updated application set: %w", err)
}
updatedAppset.Status.SetConditions(newConditions, evaluatedTypes)
updatedAppset.Status.SetConditions(
newConditions, evaluatedTypes,
)
// Update the newly fetched object with new set of conditions
err := r.Client.Status().Update(ctx, updatedAppset)
if err != nil {
return err
// Update the newly fetched object with new set of conditions
err := r.Client.Status().Update(ctx, updatedAppset)
if err != nil {
return err
}
updatedAppset.DeepCopyInto(applicationSet)
return nil
})
if err != nil && !apierrors.IsNotFound(err) {
return fmt.Errorf("unable to set application set condition: %w", err)
}
updatedAppset.DeepCopyInto(applicationSet)
return nil
})
if err != nil && !apierrors.IsNotFound(err) {
return fmt.Errorf("unable to set application set condition: %w", err)
}
return nil
@ -554,33 +478,33 @@ func (r *ApplicationSetReconciler) setApplicationSetStatusCondition(ctx context.
// validateGeneratedApplications uses the Argo CD validation functions to verify the correctness of the
// generated applications.
func (r *ApplicationSetReconciler) validateGeneratedApplications(ctx context.Context, desiredApplications []argov1alpha1.Application, applicationSetInfo argov1alpha1.ApplicationSet) (map[string]error, error) {
errorsByApp := map[string]error{}
func (r *ApplicationSetReconciler) validateGeneratedApplications(ctx context.Context, desiredApplications []argov1alpha1.Application, applicationSetInfo argov1alpha1.ApplicationSet) (map[int]error, error) {
errorsByIndex := map[int]error{}
namesSet := map[string]bool{}
for i := range desiredApplications {
app := &desiredApplications[i]
for i, app := range desiredApplications {
if namesSet[app.Name] {
errorsByApp[app.QualifiedName()] = fmt.Errorf("ApplicationSet %s contains applications with duplicate name: %s", applicationSetInfo.Name, app.Name)
errorsByIndex[i] = fmt.Errorf("ApplicationSet %s contains applications with duplicate name: %s", applicationSetInfo.Name, app.Name)
continue
}
namesSet[app.Name] = true
appProject := &argov1alpha1.AppProject{}
err := r.Get(ctx, types.NamespacedName{Name: app.Spec.Project, Namespace: r.ArgoCDNamespace}, appProject)
if err != nil {
if apierrors.IsNotFound(err) {
errorsByApp[app.QualifiedName()] = fmt.Errorf("application references project %s which does not exist", app.Spec.Project)
errorsByIndex[i] = fmt.Errorf("application references project %s which does not exist", app.Spec.Project)
continue
}
return nil, err
}
if _, err = argoutil.GetDestinationCluster(ctx, app.Spec.Destination, r.ArgoDB); err != nil {
errorsByApp[app.QualifiedName()] = fmt.Errorf("application destination spec is invalid: %s", err.Error())
errorsByIndex[i] = fmt.Errorf("application destination spec is invalid: %s", err.Error())
continue
}
}
return errorsByApp, nil
return errorsByIndex, nil
}
func (r *ApplicationSetReconciler) getMinRequeueAfter(applicationSetInfo *argov1alpha1.ApplicationSet) time.Duration {
@ -808,7 +732,7 @@ func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, logCtx *
return fmt.Errorf("error getting current applications: %w", err)
}
m := make(map[string]bool) // will hold the app names in appList for the deletion process
m := make(map[string]bool) // Will holds the app names in appList for the deletion process
for _, app := range desiredApplications {
m[app.Name] = true
@ -876,7 +800,7 @@ func (r *ApplicationSetReconciler) removeFinalizerOnInvalidDestination(ctx conte
}
if !matchingCluster {
appLog.Warnf("A match for the destination cluster for %s, by server url, couldn't be found", app.Name)
appLog.Warnf("A match for the destination cluster for %s, by server url, couldn't be found.", app.Name)
}
validDestination = matchingCluster
@ -1087,11 +1011,6 @@ func progressiveSyncsRollingSyncStrategyEnabled(appset *argov1alpha1.Application
return isRollingSyncStrategy(appset) && len(appset.Spec.Strategy.RollingSync.Steps) > 0
}
func isProgressiveSyncDeletionOrderReversed(appset *argov1alpha1.ApplicationSet) bool {
// When progressive sync is enabled + deletionOrder is set to Reverse (case-insensitive)
return progressiveSyncsRollingSyncStrategyEnabled(appset) && strings.EqualFold(appset.Spec.Strategy.DeletionOrder, ReverseDeletionOrder)
}
func isApplicationHealthy(app argov1alpha1.Application) bool {
healthStatusString, syncStatusString, operationPhaseString := statusStrings(app)
@ -1221,10 +1140,15 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress
// if we have no RollingUpdate steps, clear out the existing ApplicationStatus entries
if progressiveSyncsRollingSyncStrategyEnabled(applicationSet) {
updateCountMap := []int{}
totalCountMap := []int{}
length := len(applicationSet.Spec.Strategy.RollingSync.Steps)
updateCountMap := make([]int, length)
totalCountMap := make([]int, length)
for s := 0; s < length; s++ {
updateCountMap = append(updateCountMap, 0)
totalCountMap = append(totalCountMap, 0)
}
// populate updateCountMap with counts of existing Pending and Progressing Applications
for _, appStatus := range applicationSet.Status.ApplicationStatus {
@ -1283,56 +1207,44 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusProgress
}
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatusConditions(ctx context.Context, applicationSet *argov1alpha1.ApplicationSet) []argov1alpha1.ApplicationSetCondition {
if !isRollingSyncStrategy(applicationSet) {
return applicationSet.Status.Conditions
}
completedWaves := map[string]bool{}
appSetProgressing := false
for _, appStatus := range applicationSet.Status.ApplicationStatus {
if v, ok := completedWaves[appStatus.Step]; !ok {
completedWaves[appStatus.Step] = appStatus.Status == "Healthy"
} else {
completedWaves[appStatus.Step] = v && appStatus.Status == "Healthy"
}
}
isProgressing := false
progressingStep := ""
for i := range applicationSet.Spec.Strategy.RollingSync.Steps {
step := strconv.Itoa(i + 1)
isCompleted, ok := completedWaves[step]
if !ok {
// Step has no applications, so it is completed
continue
}
if !isCompleted {
isProgressing = true
progressingStep = step
if appStatus.Status != "Healthy" {
appSetProgressing = true
break
}
}
if isProgressing {
appSetConditionProgressing := false
for _, appSetCondition := range applicationSet.Status.Conditions {
if appSetCondition.Type == argov1alpha1.ApplicationSetConditionRolloutProgressing && appSetCondition.Status == argov1alpha1.ApplicationSetConditionStatusTrue {
appSetConditionProgressing = true
break
}
}
if appSetProgressing && !appSetConditionProgressing {
_ = r.setApplicationSetStatusCondition(ctx,
applicationSet,
argov1alpha1.ApplicationSetCondition{
Type: argov1alpha1.ApplicationSetConditionRolloutProgressing,
Message: "ApplicationSet is performing rollout of step " + progressingStep,
Message: "ApplicationSet Rollout Rollout started",
Reason: argov1alpha1.ApplicationSetReasonApplicationSetModified,
Status: argov1alpha1.ApplicationSetConditionStatusTrue,
}, true,
)
} else {
} else if !appSetProgressing && appSetConditionProgressing {
_ = r.setApplicationSetStatusCondition(ctx,
applicationSet,
argov1alpha1.ApplicationSetCondition{
Type: argov1alpha1.ApplicationSetConditionRolloutProgressing,
Message: "ApplicationSet Rollout has completed",
Message: "ApplicationSet Rollout Rollout complete",
Reason: argov1alpha1.ApplicationSetReasonApplicationSetRolloutComplete,
Status: argov1alpha1.ApplicationSetConditionStatusFalse,
}, true,
)
}
return applicationSet.Status.Conditions
}
@ -1451,17 +1363,17 @@ func (r *ApplicationSetReconciler) setAppSetApplicationStatus(ctx context.Contex
}
if needToUpdateStatus {
// sort to make sure the array is always in the same order
applicationSet.Status.ApplicationStatus = make([]argov1alpha1.ApplicationSetApplicationStatus, len(applicationStatuses))
copy(applicationSet.Status.ApplicationStatus, applicationStatuses)
sort.Slice(applicationSet.Status.ApplicationStatus, func(i, j int) bool {
return applicationSet.Status.ApplicationStatus[i].Application < applicationSet.Status.ApplicationStatus[j].Application
})
namespacedName := types.NamespacedName{Namespace: applicationSet.Namespace, Name: applicationSet.Name}
// rebuild ApplicationStatus from scratch, we don't need any previous status history
applicationSet.Status.ApplicationStatus = []argov1alpha1.ApplicationSetApplicationStatus{}
for i := range applicationStatuses {
applicationSet.Status.SetApplicationStatus(applicationStatuses[i])
}
// DefaultRetry will retry 5 times with a backoff factor of 1, jitter of 0.1 and a duration of 10ms
err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
updatedAppset := &argov1alpha1.ApplicationSet{}
if err := r.Get(ctx, types.NamespacedName{Namespace: applicationSet.Namespace, Name: applicationSet.Name}, updatedAppset); err != nil {
if err := r.Get(ctx, namespacedName, updatedAppset); err != nil {
if client.IgnoreNotFound(err) != nil {
return nil
}
@ -1525,7 +1437,7 @@ func syncApplication(application argov1alpha1.Application, prune bool) argov1alp
Info: []*argov1alpha1.Info{
{
Name: "Reason",
Value: "ApplicationSet RollingSync triggered a sync of this Application resource",
Value: "ApplicationSet RollingSync triggered a sync of this Application resource.",
},
},
Sync: &argov1alpha1.SyncOperation{},
@ -1696,14 +1608,10 @@ func shouldRequeueForApplicationSet(appSetOld, appSetNew *argov1alpha1.Applicati
}
// Requeue if any ApplicationStatus.Status changed for Progressive sync strategy
// Requeue if deletionTimestamp added
if enableProgressiveSyncs {
if !cmp.Equal(appSetOld.Status.ApplicationStatus, appSetNew.Status.ApplicationStatus, cmpopts.EquateEmpty()) {
return true
}
if !cmp.Equal(appSetOld.DeletionTimestamp, appSetNew.DeletionTimestamp, cmpopts.EquateEmpty()) {
return true
}
}
// only compare the applicationset spec, annotations, labels and finalizers, specifically avoiding

View file

@ -1954,15 +1954,14 @@ func TestValidateGeneratedApplications(t *testing.T) {
for _, cc := range []struct {
name string
apps []v1alpha1.Application
validationErrors map[string]error
validationErrors map[int]error
}{
{
name: "valid app should return true",
apps: []v1alpha1.Application{
{
ObjectMeta: metav1.ObjectMeta{
Name: "app",
},
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
@ -1977,15 +1976,14 @@ func TestValidateGeneratedApplications(t *testing.T) {
},
},
},
validationErrors: map[string]error{},
validationErrors: map[int]error{},
},
{
name: "can't have both name and server defined",
apps: []v1alpha1.Application{
{
ObjectMeta: metav1.ObjectMeta{
Name: "app",
},
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
@ -2001,15 +1999,14 @@ func TestValidateGeneratedApplications(t *testing.T) {
},
},
},
validationErrors: map[string]error{"app": errors.New("application destination spec is invalid: application destination can't have both name and server defined: my-cluster my-server")},
validationErrors: map[int]error{0: errors.New("application destination spec is invalid: application destination can't have both name and server defined: my-cluster my-server")},
},
{
name: "project mismatch should return error",
apps: []v1alpha1.Application{
{
ObjectMeta: metav1.ObjectMeta{
Name: "app",
},
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{},
Spec: v1alpha1.ApplicationSpec{
Project: "DOES-NOT-EXIST",
Source: &v1alpha1.ApplicationSource{
@ -2024,15 +2021,14 @@ func TestValidateGeneratedApplications(t *testing.T) {
},
},
},
validationErrors: map[string]error{"app": errors.New("application references project DOES-NOT-EXIST which does not exist")},
validationErrors: map[int]error{0: errors.New("application references project DOES-NOT-EXIST which does not exist")},
},
{
name: "valid app should return true",
apps: []v1alpha1.Application{
{
ObjectMeta: metav1.ObjectMeta{
Name: "app",
},
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
@ -2047,15 +2043,14 @@ func TestValidateGeneratedApplications(t *testing.T) {
},
},
},
validationErrors: map[string]error{},
validationErrors: map[int]error{},
},
{
name: "cluster should match",
apps: []v1alpha1.Application{
{
ObjectMeta: metav1.ObjectMeta{
Name: "app",
},
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{},
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Source: &v1alpha1.ApplicationSource{
@ -2070,7 +2065,7 @@ func TestValidateGeneratedApplications(t *testing.T) {
},
},
},
validationErrors: map[string]error{"app": errors.New("application destination spec is invalid: there are no clusters with this name: nonexistent-cluster")},
validationErrors: map[int]error{0: errors.New("application destination spec is invalid: there are no clusters with this name: nonexistent-cluster")},
},
} {
t.Run(cc.name, func(t *testing.T) {
@ -2203,20 +2198,13 @@ func TestSetApplicationSetStatusCondition(t *testing.T) {
scheme := runtime.NewScheme()
err := v1alpha1.AddToScheme(scheme)
require.NoError(t, err)
kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...)
someTime := &metav1.Time{Time: time.Now().Add(-5 * time.Minute)}
existingParameterGeneratedCondition := getParametersGeneratedCondition(true, "")
existingParameterGeneratedCondition.LastTransitionTime = someTime
for _, c := range []struct {
name string
appset v1alpha1.ApplicationSet
condition v1alpha1.ApplicationSetCondition
parametersGenerated bool
testfunc func(t *testing.T, conditions []v1alpha1.ApplicationSetCondition)
testCases := []struct {
appset v1alpha1.ApplicationSet
conditions []v1alpha1.ApplicationSetCondition
testfunc func(t *testing.T, appset v1alpha1.ApplicationSet)
}{
{
name: "has parameters generated condition when false",
appset: v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "name",
@ -2233,28 +2221,20 @@ func TestSetApplicationSetStatusCondition(t *testing.T) {
Template: v1alpha1.ApplicationSetTemplate{},
},
},
condition: v1alpha1.ApplicationSetCondition{
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
Message: "This is a message",
Reason: "test",
Status: v1alpha1.ApplicationSetConditionStatusFalse,
conditions: []v1alpha1.ApplicationSetCondition{
{
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
Message: "All applications have been generated successfully",
Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate,
Status: v1alpha1.ApplicationSetConditionStatusTrue,
},
},
parametersGenerated: false,
testfunc: func(t *testing.T, conditions []v1alpha1.ApplicationSetCondition) {
testfunc: func(t *testing.T, appset v1alpha1.ApplicationSet) {
t.Helper()
require.Len(t, conditions, 2)
// Conditions are ordered by type, so the order is deterministic
assert.Equal(t, v1alpha1.ApplicationSetConditionParametersGenerated, conditions[0].Type)
assert.Equal(t, v1alpha1.ApplicationSetConditionStatusFalse, conditions[0].Status)
assert.Equal(t, v1alpha1.ApplicationSetConditionResourcesUpToDate, conditions[1].Type)
assert.Equal(t, v1alpha1.ApplicationSetConditionStatusFalse, conditions[1].Status)
assert.Equal(t, "test", conditions[1].Reason)
assert.Len(t, appset.Status.Conditions, 3)
},
},
{
name: "parameters generated condition is used when specified",
appset: v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "name",
@ -2271,268 +2251,37 @@ func TestSetApplicationSetStatusCondition(t *testing.T) {
Template: v1alpha1.ApplicationSetTemplate{},
},
},
condition: v1alpha1.ApplicationSetCondition{
Type: v1alpha1.ApplicationSetConditionParametersGenerated,
Message: "This is a message",
Reason: "test",
Status: v1alpha1.ApplicationSetConditionStatusFalse,
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,
},
},
parametersGenerated: true,
testfunc: func(t *testing.T, conditions []v1alpha1.ApplicationSetCondition) {
testfunc: func(t *testing.T, appset v1alpha1.ApplicationSet) {
t.Helper()
require.Len(t, conditions, 1)
assert.Len(t, appset.Status.Conditions, 3)
assert.Equal(t, v1alpha1.ApplicationSetConditionParametersGenerated, conditions[0].Type)
assert.Equal(t, v1alpha1.ApplicationSetConditionStatusFalse, conditions[0].Status)
assert.Equal(t, "test", conditions[0].Reason)
},
},
{
name: "has parameter conditions when true",
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{},
},
},
condition: v1alpha1.ApplicationSetCondition{
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
Message: "This is a message",
Reason: "test",
Status: v1alpha1.ApplicationSetConditionStatusFalse,
},
parametersGenerated: true,
testfunc: func(t *testing.T, conditions []v1alpha1.ApplicationSetCondition) {
t.Helper()
require.Len(t, conditions, 2)
isProgressingCondition := false
// Conditions are ordered by type, so the order is deterministic
assert.Equal(t, v1alpha1.ApplicationSetConditionParametersGenerated, conditions[0].Type)
assert.Equal(t, v1alpha1.ApplicationSetConditionStatusTrue, conditions[0].Status)
assert.Equal(t, v1alpha1.ApplicationSetConditionResourcesUpToDate, conditions[1].Type)
assert.Equal(t, v1alpha1.ApplicationSetConditionStatusFalse, conditions[1].Status)
assert.Equal(t, "test", conditions[1].Reason)
},
},
{
name: "resource up to date sets error condition to false",
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{},
},
},
condition: v1alpha1.ApplicationSetCondition{
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
Message: "Completed",
Reason: "test",
Status: v1alpha1.ApplicationSetConditionStatusTrue,
},
testfunc: func(t *testing.T, conditions []v1alpha1.ApplicationSetCondition) {
t.Helper()
require.Len(t, conditions, 3)
assert.Equal(t, v1alpha1.ApplicationSetConditionErrorOccurred, conditions[0].Type)
assert.Equal(t, v1alpha1.ApplicationSetConditionStatusFalse, conditions[0].Status)
assert.Equal(t, "test", conditions[0].Reason)
assert.Equal(t, "Completed", conditions[0].Message)
assert.Equal(t, v1alpha1.ApplicationSetConditionParametersGenerated, conditions[1].Type)
assert.Equal(t, v1alpha1.ApplicationSetConditionResourcesUpToDate, conditions[2].Type)
assert.Equal(t, v1alpha1.ApplicationSetConditionStatusTrue, conditions[2].Status)
assert.Equal(t, "test", conditions[2].Reason)
assert.Equal(t, "Completed", conditions[2].Message)
},
},
{
name: "error condition sets resource up to date to false",
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{},
},
},
condition: v1alpha1.ApplicationSetCondition{
Type: v1alpha1.ApplicationSetConditionErrorOccurred,
Message: "Error",
Reason: "test",
Status: v1alpha1.ApplicationSetConditionStatusTrue,
},
testfunc: func(t *testing.T, conditions []v1alpha1.ApplicationSetCondition) {
t.Helper()
require.Len(t, conditions, 3)
assert.Equal(t, v1alpha1.ApplicationSetConditionErrorOccurred, conditions[0].Type)
assert.Equal(t, v1alpha1.ApplicationSetConditionStatusTrue, conditions[0].Status)
assert.Equal(t, "test", conditions[0].Reason)
assert.Equal(t, "Error", conditions[0].Message)
assert.Equal(t, v1alpha1.ApplicationSetConditionParametersGenerated, conditions[1].Type)
assert.Equal(t, v1alpha1.ApplicationSetConditionResourcesUpToDate, conditions[2].Type)
assert.Equal(t, v1alpha1.ApplicationSetConditionStatusFalse, conditions[2].Status)
assert.Equal(t, v1alpha1.ApplicationSetReasonErrorOccurred, conditions[2].Reason)
assert.Equal(t, "Error", conditions[2].Message)
},
},
{
name: "updating an unchanged condition does not mutate existing conditions",
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"}`),
}},
}},
},
Strategy: &v1alpha1.ApplicationSetStrategy{
Type: "RollingSync",
RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{},
},
Template: v1alpha1.ApplicationSetTemplate{},
},
Status: v1alpha1.ApplicationSetStatus{
Conditions: []v1alpha1.ApplicationSetCondition{
{
Type: v1alpha1.ApplicationSetConditionErrorOccurred,
Message: "existing",
LastTransitionTime: someTime,
},
existingParameterGeneratedCondition,
{
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
Message: "existing",
Status: v1alpha1.ApplicationSetConditionStatusFalse,
LastTransitionTime: someTime,
},
{
Type: v1alpha1.ApplicationSetConditionRolloutProgressing,
Message: "existing",
LastTransitionTime: someTime,
},
},
},
},
condition: v1alpha1.ApplicationSetCondition{
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
Message: "existing",
Status: v1alpha1.ApplicationSetConditionStatusFalse,
},
parametersGenerated: true,
testfunc: func(t *testing.T, conditions []v1alpha1.ApplicationSetCondition) {
t.Helper()
require.Len(t, conditions, 4)
assert.Equal(t, v1alpha1.ApplicationSetConditionErrorOccurred, conditions[0].Type)
assert.Equal(t, someTime, conditions[0].LastTransitionTime)
assert.Equal(t, v1alpha1.ApplicationSetConditionParametersGenerated, conditions[1].Type)
assert.Equal(t, someTime, conditions[1].LastTransitionTime)
assert.Equal(t, v1alpha1.ApplicationSetConditionResourcesUpToDate, conditions[2].Type)
assert.Equal(t, someTime, conditions[2].LastTransitionTime)
assert.Equal(t, v1alpha1.ApplicationSetConditionRolloutProgressing, conditions[3].Type)
assert.Equal(t, someTime, conditions[3].LastTransitionTime)
},
},
{
name: "progressing conditions is removed when AppSet is not configured",
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"}`),
}},
}},
},
// Strategy removed
// Strategy: &v1alpha1.ApplicationSetStrategy{
// Type: "RollingSync",
// RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{},
// },
Template: v1alpha1.ApplicationSetTemplate{},
},
Status: v1alpha1.ApplicationSetStatus{
Conditions: []v1alpha1.ApplicationSetCondition{
{
Type: v1alpha1.ApplicationSetConditionErrorOccurred,
Message: "existing",
LastTransitionTime: someTime,
},
existingParameterGeneratedCondition,
{
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
Message: "existing",
Status: v1alpha1.ApplicationSetConditionStatusFalse,
LastTransitionTime: someTime,
},
{
Type: v1alpha1.ApplicationSetConditionRolloutProgressing,
Message: "existing",
LastTransitionTime: someTime,
},
},
},
},
condition: v1alpha1.ApplicationSetCondition{
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
Message: "existing",
Status: v1alpha1.ApplicationSetConditionStatusFalse,
},
parametersGenerated: true,
testfunc: func(t *testing.T, conditions []v1alpha1.ApplicationSetCondition) {
t.Helper()
require.Len(t, conditions, 3)
for _, c := range conditions {
assert.NotEqual(t, v1alpha1.ApplicationSetConditionRolloutProgressing, c.Type)
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")
},
},
{
name: "progressing conditions is ignored when AppSet is not configured",
appset: v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "name",
@ -2546,126 +2295,84 @@ func TestSetApplicationSetStatusCondition(t *testing.T) {
}},
}},
},
// Strategy removed
// Strategy: &v1alpha1.ApplicationSetStrategy{
// Type: "RollingSync",
// RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{},
// },
Template: v1alpha1.ApplicationSetTemplate{},
},
Status: v1alpha1.ApplicationSetStatus{
Conditions: []v1alpha1.ApplicationSetCondition{
{
Type: v1alpha1.ApplicationSetConditionErrorOccurred,
Message: "existing",
LastTransitionTime: someTime,
},
existingParameterGeneratedCondition,
{
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
Message: "existing",
Status: v1alpha1.ApplicationSetConditionStatusFalse,
LastTransitionTime: someTime,
},
},
},
},
condition: v1alpha1.ApplicationSetCondition{
Type: v1alpha1.ApplicationSetConditionRolloutProgressing,
Message: "do not add me",
Status: v1alpha1.ApplicationSetConditionStatusTrue,
},
parametersGenerated: true,
testfunc: func(t *testing.T, conditions []v1alpha1.ApplicationSetCondition) {
t.Helper()
require.Len(t, conditions, 3)
for _, c := range conditions {
assert.NotEqual(t, v1alpha1.ApplicationSetConditionRolloutProgressing, c.Type)
}
},
},
{
name: "progressing conditions is updated correctly when configured",
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"}`),
}},
}},
},
Strategy: &v1alpha1.ApplicationSetStrategy{
Type: "RollingSync",
RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{},
},
Template: v1alpha1.ApplicationSetTemplate{},
},
Status: v1alpha1.ApplicationSetStatus{
Conditions: []v1alpha1.ApplicationSetCondition{
{
Type: v1alpha1.ApplicationSetConditionErrorOccurred,
Message: "existing",
LastTransitionTime: someTime,
},
existingParameterGeneratedCondition,
{
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
Message: "existing",
Status: v1alpha1.ApplicationSetConditionStatusFalse,
LastTransitionTime: someTime,
},
{
Type: v1alpha1.ApplicationSetConditionRolloutProgressing,
Message: "old value",
Status: v1alpha1.ApplicationSetConditionStatusTrue,
Type: "RollingSync",
RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{
Steps: []v1alpha1.ApplicationSetRolloutStep{
{
MatchExpressions: []v1alpha1.ApplicationMatchExpression{
{
Key: "test",
Operator: "In",
Values: []string{"test"},
},
},
},
},
},
},
},
},
condition: v1alpha1.ApplicationSetCondition{
Type: v1alpha1.ApplicationSetConditionRolloutProgressing,
Message: "new value",
Status: v1alpha1.ApplicationSetConditionStatusFalse,
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,
},
},
parametersGenerated: true,
testfunc: func(t *testing.T, conditions []v1alpha1.ApplicationSetCondition) {
testfunc: func(t *testing.T, appset v1alpha1.ApplicationSet) {
t.Helper()
require.Len(t, conditions, 4)
assert.Len(t, appset.Status.Conditions, 4)
assert.Equal(t, v1alpha1.ApplicationSetConditionRolloutProgressing, conditions[3].Type)
assert.Equal(t, v1alpha1.ApplicationSetConditionStatusFalse, conditions[3].Status)
assert.Equal(t, "new value", conditions[3].Message)
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")
},
},
} {
t.Run(c.name, func(t *testing.T) {
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&c.appset).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).WithStatusSubresource(&c.appset).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics()
argodb := db.NewDB("argocd", settings.NewSettingsManager(t.Context(), kubeclientset, "argocd"), kubeclientset)
}
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Renderer: &utils.Render{},
Recorder: record.NewFakeRecorder(1),
Generators: map[string]generators.Generator{
"List": generators.NewListGenerator(),
},
ArgoDB: argodb,
KubeClientset: kubeclientset,
Metrics: metrics,
}
kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...)
err = r.setApplicationSetStatusCondition(t.Context(), &c.appset, c.condition, c.parametersGenerated)
for _, testCase := range testCases {
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&testCase.appset).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).WithStatusSubresource(&testCase.appset).Build()
metrics := appsetmetrics.NewFakeAppsetMetrics()
argodb := db.NewDB("argocd", settings.NewSettingsManager(t.Context(), kubeclientset, "argocd"), kubeclientset)
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Renderer: &utils.Render{},
Recorder: record.NewFakeRecorder(1),
Generators: map[string]generators.Generator{
"List": generators.NewListGenerator(),
},
ArgoDB: argodb,
KubeClientset: kubeclientset,
Metrics: metrics,
}
for _, condition := range testCase.conditions {
err = r.setApplicationSetStatusCondition(t.Context(), &testCase.appset, condition, true)
require.NoError(t, err)
}
c.testfunc(t, c.appset.Status.Conditions)
})
testCase.testfunc(t, testCase.appset)
}
}
@ -7106,28 +6813,6 @@ func TestApplicationSetOwnsHandlerUpdate(t *testing.T) {
enableProgressiveSyncs: false,
want: false,
},
{
name: "deletionTimestamp present when progressive sync enabled",
appSetOld: buildAppSet(map[string]string{}),
appSetNew: &v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
DeletionTimestamp: &metav1.Time{Time: time.Now()},
},
},
enableProgressiveSyncs: true,
want: true,
},
{
name: "deletionTimestamp present when progressive sync disabled",
appSetOld: buildAppSet(map[string]string{}),
appSetNew: &v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
DeletionTimestamp: &metav1.Time{Time: time.Now()},
},
},
enableProgressiveSyncs: false,
want: false,
},
}
for _, tt := range tests {
@ -7449,7 +7134,7 @@ func TestSyncApplication(t *testing.T) {
Info: []*v1alpha1.Info{
{
Name: "Reason",
Value: "ApplicationSet RollingSync triggered a sync of this Application resource",
Value: "ApplicationSet RollingSync triggered a sync of this Application resource.",
},
},
Sync: &v1alpha1.SyncOperation{
@ -7491,7 +7176,7 @@ func TestSyncApplication(t *testing.T) {
Info: []*v1alpha1.Info{
{
Name: "Reason",
Value: "ApplicationSet RollingSync triggered a sync of this Application resource",
Value: "ApplicationSet RollingSync triggered a sync of this Application resource.",
},
},
Sync: &v1alpha1.SyncOperation{
@ -7513,110 +7198,3 @@ func TestSyncApplication(t *testing.T) {
})
}
}
func TestIsRollingSyncDeletionReversed(t *testing.T) {
tests := []struct {
name string
appset *v1alpha1.ApplicationSet
expected bool
}{
{
name: "Deletion Order on strategy is set as Reverse",
appset: &v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
Strategy: &v1alpha1.ApplicationSetStrategy{
Type: "RollingSync",
RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{
Steps: []v1alpha1.ApplicationSetRolloutStep{
{
MatchExpressions: []v1alpha1.ApplicationMatchExpression{
{
Key: "environment",
Operator: "In",
Values: []string{
"dev",
},
},
},
},
{
MatchExpressions: []v1alpha1.ApplicationMatchExpression{
{
Key: "environment",
Operator: "In",
Values: []string{
"staging",
},
},
},
},
},
},
DeletionOrder: ReverseDeletionOrder,
},
},
},
expected: true,
},
{
name: "Deletion Order on strategy is set as AllAtOnce",
appset: &v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
Strategy: &v1alpha1.ApplicationSetStrategy{
Type: "RollingSync",
RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{
Steps: []v1alpha1.ApplicationSetRolloutStep{},
},
DeletionOrder: AllAtOnceDeletionOrder,
},
},
},
expected: false,
},
{
name: "Deletion Order on strategy is set as Reverse but no steps in RollingSync",
appset: &v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
Strategy: &v1alpha1.ApplicationSetStrategy{
Type: "RollingSync",
RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{
Steps: []v1alpha1.ApplicationSetRolloutStep{},
},
DeletionOrder: ReverseDeletionOrder,
},
},
},
expected: false,
},
{
name: "Deletion Order on strategy is set as Reverse, but AllAtOnce is explicitly set",
appset: &v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
Strategy: &v1alpha1.ApplicationSetStrategy{
Type: "AllAtOnce",
RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{
Steps: []v1alpha1.ApplicationSetRolloutStep{},
},
DeletionOrder: ReverseDeletionOrder,
},
},
},
expected: false,
},
{
name: "Strategy is Nil",
appset: &v1alpha1.ApplicationSet{
Spec: v1alpha1.ApplicationSetSpec{
Strategy: &v1alpha1.ApplicationSetStrategy{},
},
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := isProgressiveSyncDeletionOrderReversed(tt.appset)
assert.Equal(t, tt.expected, result)
})
}
}

View file

@ -79,10 +79,14 @@ func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.Ap
return nil, fmt.Errorf("error getting cluster secrets: %w", err)
}
paramHolder := &paramHolder{isFlatMode: appSetGenerator.Clusters.FlatList}
logCtx.Debugf("Using flat mode = %t for cluster generator", paramHolder.isFlatMode)
res := []map[string]any{}
secretsFound := []corev1.Secret{}
isFlatMode := appSetGenerator.Clusters.FlatList
logCtx.Debugf("Using flat mode = %t for cluster generator", isFlatMode)
clustersParams := make([]map[string]any, 0)
for _, cluster := range clustersFromArgoCD {
// If there is a secret for this cluster, then it's a non-local cluster, so it will be
// handled by the next step.
@ -101,80 +105,72 @@ func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.Ap
return nil, fmt.Errorf("error appending templated values for local cluster: %w", err)
}
paramHolder.append(params)
if isFlatMode {
clustersParams = append(clustersParams, params)
} else {
res = append(res, params)
}
logCtx.WithField("cluster", "local cluster").Info("matched local cluster")
}
}
// For each matching cluster secret (non-local clusters only)
for _, cluster := range secretsFound {
params := g.getClusterParameters(cluster, appSet)
params := map[string]any{}
params["name"] = string(cluster.Data["name"])
params["nameNormalized"] = utils.SanitizeName(string(cluster.Data["name"]))
params["server"] = string(cluster.Data["server"])
project, ok := cluster.Data["project"]
if ok {
params["project"] = string(project)
} else {
params["project"] = ""
}
if appSet.Spec.GoTemplate {
meta := map[string]any{}
if len(cluster.Annotations) > 0 {
meta["annotations"] = cluster.Annotations
}
if len(cluster.Labels) > 0 {
meta["labels"] = cluster.Labels
}
params["metadata"] = meta
} else {
for key, value := range cluster.Annotations {
params["metadata.annotations."+key] = value
}
for key, value := range cluster.Labels {
params["metadata.labels."+key] = value
}
}
err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
if err != nil {
return nil, fmt.Errorf("error appending templated values for cluster: %w", err)
}
paramHolder.append(params)
if isFlatMode {
clustersParams = append(clustersParams, params)
} else {
res = append(res, params)
}
logCtx.WithField("cluster", cluster.Name).Debug("matched cluster secret")
}
return paramHolder.consolidate(), nil
}
type paramHolder struct {
isFlatMode bool
params []map[string]any
}
func (p *paramHolder) append(params map[string]any) {
p.params = append(p.params, params)
}
func (p *paramHolder) consolidate() []map[string]any {
if p.isFlatMode {
p.params = []map[string]any{
{"clusters": p.params},
}
if isFlatMode {
res = append(res, map[string]any{
"clusters": clustersParams,
})
}
return p.params
}
func (g *ClusterGenerator) getClusterParameters(cluster corev1.Secret, appSet *argoappsetv1alpha1.ApplicationSet) map[string]any {
params := map[string]any{}
params["name"] = string(cluster.Data["name"])
params["nameNormalized"] = utils.SanitizeName(string(cluster.Data["name"]))
params["server"] = string(cluster.Data["server"])
project, ok := cluster.Data["project"]
if ok {
params["project"] = string(project)
} else {
params["project"] = ""
}
if appSet.Spec.GoTemplate {
meta := map[string]any{}
if len(cluster.Annotations) > 0 {
meta["annotations"] = cluster.Annotations
}
if len(cluster.Labels) > 0 {
meta["labels"] = cluster.Labels
}
params["metadata"] = meta
} else {
for key, value := range cluster.Annotations {
params["metadata.annotations."+key] = value
}
for key, value := range cluster.Labels {
params["metadata.labels."+key] = value
}
}
return params
return res, nil
}
func (g *ClusterGenerator) getSecretsByClusterName(log *log.Entry, appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator) (map[string]corev1.Secret, error) {

View file

@ -222,18 +222,19 @@ func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1al
func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []byte, values map[string]string, useGoTemplate bool, goTemplateOptions []string, pathParamPrefix string) ([]map[string]any, error) {
objectsFound := []map[string]any{}
// First, we attempt to parse as a single object.
// This will also succeed for empty files.
singleObj := map[string]any{}
err := yaml.Unmarshal(fileContent, &singleObj)
if err == nil {
objectsFound = append(objectsFound, singleObj)
} else {
// If unable to parse as an object, try to parse as an array
err = yaml.Unmarshal(fileContent, &objectsFound)
// First, we attempt to parse as an array
err := yaml.Unmarshal(fileContent, &objectsFound)
if err != nil {
// If unable to parse as an array, attempt to parse as a single object
singleObj := make(map[string]any)
err = yaml.Unmarshal(fileContent, &singleObj)
if err != nil {
return nil, fmt.Errorf("unable to parse file: %w", err)
}
objectsFound = append(objectsFound, singleObj)
} else if len(objectsFound) == 0 {
// If file is valid but empty, add a default empty item
objectsFound = append(objectsFound, map[string]any{})
}
res := []map[string]any{}

View file

@ -825,7 +825,7 @@ func TestGitGenerateParamsFromFiles(t *testing.T) {
},
repoPathsError: nil,
expected: []map[string]any{},
expectedError: errors.New("error generating params from git: unable to process file 'cluster-config/production/config.json': unable to parse file: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type []map[string]interface {}"),
expectedError: errors.New("error generating params from git: unable to process file 'cluster-config/production/config.json': unable to parse file: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}"),
},
{
name: "test JSON array",
@ -982,16 +982,6 @@ cluster:
},
expectedError: nil,
},
{
name: "test empty YAML array",
files: []v1alpha1.GitFileGeneratorItem{{Path: "**/config.yaml"}},
repoFileContents: map[string][]byte{
"cluster-config/production/config.yaml": []byte(`[]`),
},
repoPathsError: nil,
expected: []map[string]any{},
expectedError: nil,
},
}
for _, testCase := range cases {
@ -2070,7 +2060,7 @@ func TestGitGenerateParamsFromFilesGoTemplate(t *testing.T) {
},
repoPathsError: nil,
expected: []map[string]any{},
expectedError: errors.New("error generating params from git: unable to process file 'cluster-config/production/config.json': unable to parse file: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type []map[string]interface {}"),
expectedError: errors.New("error generating params from git: unable to process file 'cluster-config/production/config.json': unable to parse file: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}"),
},
{
name: "test JSON array",

View file

@ -11,7 +11,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/gosimple/slug"
log "github.com/sirupsen/logrus"
"github.com/argoproj/argo-cd/v3/applicationset/services"
pullrequest "github.com/argoproj/argo-cd/v3/applicationset/services/pull_request"
@ -19,6 +18,8 @@ import (
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
)
var _ Generator = (*PullRequestGenerator)(nil)
const (
DefaultPullRequestRequeueAfter = 30 * time.Minute
)
@ -48,10 +49,6 @@ func (g *PullRequestGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alph
return DefaultPullRequestRequeueAfter
}
func (g *PullRequestGenerator) GetContinueOnRepoNotFoundError(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) bool {
return appSetGenerator.PullRequest.ContinueOnRepoNotFoundError
}
func (g *PullRequestGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
return &appSetGenerator.PullRequest.Template
}
@ -72,15 +69,10 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
}
pulls, err := pullrequest.ListPullRequests(ctx, svc, appSetGenerator.PullRequest.Filters)
params := make([]map[string]any, 0, len(pulls))
if err != nil {
if pullrequest.IsRepositoryNotFoundError(err) && g.GetContinueOnRepoNotFoundError(appSetGenerator) {
log.WithError(err).WithField("generator", g).
Warn("Skipping params generation for this repository since it was not found.")
return params, nil
}
return nil, fmt.Errorf("error listing repos: %w", err)
}
params := make([]map[string]any, 0, len(pulls))
// In order to follow the DNS label standard as defined in RFC 1123,
// we need to limit the 'branch' to 50 to give room to append/suffix-ing it

View file

@ -16,12 +16,11 @@ import (
func TestPullRequestGithubGenerateParams(t *testing.T) {
ctx := t.Context()
cases := []struct {
selectFunc func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error)
values map[string]string
expected []map[string]any
expectedErr error
applicationSet argoprojiov1alpha1.ApplicationSet
continueOnRepoNotFoundError bool
selectFunc func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error)
values map[string]string
expected []map[string]any
expectedErr error
applicationSet argoprojiov1alpha1.ApplicationSet
}{
{
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
@ -172,30 +171,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
expected: nil,
expectedErr: errors.New("error listing repos: fake error"),
},
{
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
return pullrequest.NewFakeService(
ctx,
nil,
pullrequest.NewRepositoryNotFoundError(errors.New("repository not found")),
)
},
expected: []map[string]any{},
expectedErr: nil,
continueOnRepoNotFoundError: true,
},
{
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
return pullrequest.NewFakeService(
ctx,
nil,
pullrequest.NewRepositoryNotFoundError(errors.New("repository not found")),
)
},
expected: nil,
expectedErr: errors.New("error listing repos: repository not found"),
continueOnRepoNotFoundError: false,
},
{
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
return pullrequest.NewFakeService(
@ -285,8 +260,7 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
}
generatorConfig := argoprojiov1alpha1.ApplicationSetGenerator{
PullRequest: &argoprojiov1alpha1.PullRequestGenerator{
Values: c.values,
ContinueOnRepoNotFoundError: c.continueOnRepoNotFoundError,
Values: c.values,
},
}

View file

@ -10,10 +10,7 @@ import (
"github.com/microsoft/azure-devops-go-api/azuredevops/v7/git"
)
const (
AZURE_DEVOPS_DEFAULT_URL = "https://dev.azure.com"
AZURE_DEVOPS_PROJECT_NOT_FOUND_ERROR = "The following project does not exist"
)
const AZURE_DEVOPS_DEFAULT_URL = "https://dev.azure.com"
type AzureDevOpsClientFactory interface {
// Returns an Azure Devops Client interface.
@ -73,22 +70,13 @@ func (a *AzureDevOpsService) List(ctx context.Context) ([]*PullRequest, error) {
SearchCriteria: &git.GitPullRequestSearchCriteria{},
}
pullRequests := []*PullRequest{}
azurePullRequests, err := client.GetPullRequestsByProject(ctx, args)
if err != nil {
// A standard Http 404 error is not returned for Azure DevOps,
// so checking the error message for a specific pattern.
// NOTE: Since the repos are filtered later, only existence of the project
// is relevant for AzureDevOps
if strings.Contains(err.Error(), AZURE_DEVOPS_PROJECT_NOT_FOUND_ERROR) {
// return a custom error indicating that the repository is not found,
// but also return the empty result since the decision to continue or not in this case is made by the caller
return pullRequests, NewRepositoryNotFoundError(err)
}
return nil, fmt.Errorf("failed to get pull requests by project: %w", err)
}
pullRequests := []*PullRequest{}
for _, pr := range *azurePullRequests {
if pr.Repository == nil ||
pr.Repository.Name == nil ||

View file

@ -2,7 +2,6 @@ package pull_request
import (
"context"
"errors"
"testing"
"github.com/microsoft/azure-devops-go-api/azuredevops/v7/core"
@ -236,36 +235,3 @@ func TestBuildURL(t *testing.T) {
})
}
}
func TestAzureDevOpsListReturnsRepositoryNotFoundError(t *testing.T) {
args := git.GetPullRequestsByProjectArgs{
Project: createStringPtr("nonexistent"),
SearchCriteria: &git.GitPullRequestSearchCriteria{},
}
pullRequestMock := []git.GitPullRequest{}
gitClientMock := azureMock.Client{}
clientFactoryMock := &AzureClientFactoryMock{mock: &mock.Mock{}}
clientFactoryMock.mock.On("GetClient", mock.Anything).Return(&gitClientMock, nil)
// Mock the GetPullRequestsByProject to return an error containing "404"
gitClientMock.On("GetPullRequestsByProject", t.Context(), args).Return(&pullRequestMock,
errors.New("The following project does not exist:"))
provider := AzureDevOpsService{
clientFactory: clientFactoryMock,
project: "nonexistent",
repo: "nonexistent",
labels: nil,
}
prs, err := provider.List(t.Context())
// Should return empty pull requests list
assert.Empty(t, prs)
// Should return RepositoryNotFoundError
require.Error(t, err)
assert.True(t, IsRepositoryNotFoundError(err), "Expected RepositoryNotFoundError but got: %v", err)
}

View file

@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"net/url"
"strings"
"github.com/ktrysmt/go-bitbucket"
)
@ -118,17 +117,8 @@ func (b *BitbucketCloudService) List(_ context.Context) ([]*PullRequest, error)
RepoSlug: b.repositorySlug,
}
pullRequests := []*PullRequest{}
response, err := b.client.Repositories.PullRequests.Gets(opts)
if err != nil {
// A standard Http 404 error is not returned for Bitbucket Cloud,
// so checking the error message for a specific pattern
if strings.Contains(err.Error(), "404 Not Found") {
// return a custom error indicating that the repository is not found,
// but also return the empty result since the decision to continue or not in this case is made by the caller
return pullRequests, NewRepositoryNotFoundError(err)
}
return nil, fmt.Errorf("error listing pull requests for %s/%s: %w", b.owner, b.repositorySlug, err)
}
@ -152,6 +142,7 @@ func (b *BitbucketCloudService) List(_ context.Context) ([]*PullRequest, error)
return nil, fmt.Errorf("error unmarshalling json to type '[]BitbucketCloudPullRequest': %w", err)
}
pullRequests := []*PullRequest{}
for _, pull := range pulls {
pullRequests = append(pullRequests, &PullRequest{
Number: pull.ID,

View file

@ -492,29 +492,3 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) {
TargetBranch: "branch-200",
}, *pullRequests[0])
}
func TestBitbucketCloudListReturnsRepositoryNotFoundError(t *testing.T) {
mux := http.NewServeMux()
server := httptest.NewServer(mux)
defer server.Close()
path := "/repositories/nonexistent/nonexistent/pullrequests/"
mux.HandleFunc(path, func(w http.ResponseWriter, _ *http.Request) {
// Return 404 status to simulate repository not found
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(`{"message": "404 Project Not Found"}`))
})
svc, err := NewBitbucketCloudServiceNoAuth(server.URL, "nonexistent", "nonexistent")
require.NoError(t, err)
prs, err := svc.List(t.Context())
// Should return empty pull requests list
assert.Empty(t, prs)
// Should return RepositoryNotFoundError
require.Error(t, err)
assert.True(t, IsRepositoryNotFoundError(err), "Expected RepositoryNotFoundError but got: %v", err)
}

View file

@ -72,11 +72,6 @@ func (b *BitbucketService) List(_ context.Context) ([]*PullRequest, error) {
for {
response, err := b.client.DefaultApi.GetPullRequestsPage(b.projectKey, b.repositorySlug, paged)
if err != nil {
if response != nil && response.Response != nil && response.StatusCode == http.StatusNotFound {
// return a custom error indicating that the repository is not found,
// but also return the empty result since the decision to continue or not in this case is made by the caller
return pullRequests, NewRepositoryNotFoundError(err)
}
return nil, fmt.Errorf("error listing pull requests for %s/%s: %w", b.projectKey, b.repositorySlug, err)
}
pulls, err := bitbucketv1.GetPullRequestsResponse(response)

View file

@ -510,29 +510,3 @@ func TestListPullRequestBranchMatch(t *testing.T) {
})
require.Error(t, err)
}
func TestBitbucketServerListReturnsRepositoryNotFoundError(t *testing.T) {
mux := http.NewServeMux()
server := httptest.NewServer(mux)
defer server.Close()
path := "/rest/api/1.0/projects/nonexistent/repos/nonexistent/pull-requests?limit=100"
mux.HandleFunc(path, func(w http.ResponseWriter, _ *http.Request) {
// Return 404 status to simulate repository not found
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(`{"message": "404 Project Not Found"}`))
})
svc, err := NewBitbucketServiceNoAuth(t.Context(), server.URL, "nonexistent", "nonexistent", "", false, nil)
require.NoError(t, err)
prs, err := svc.List(t.Context())
// Should return empty pull requests list
assert.Empty(t, prs)
// Should return RepositoryNotFoundError
require.Error(t, err)
assert.True(t, IsRepositoryNotFoundError(err), "Expected RepositoryNotFoundError but got: %v", err)
}

View file

@ -1,23 +0,0 @@
package pull_request
import "errors"
// RepositoryNotFoundError represents an error when a repository is not found by a pull request provider
type RepositoryNotFoundError struct {
causingError error
}
func (e *RepositoryNotFoundError) Error() string {
return e.causingError.Error()
}
// NewRepositoryNotFoundError creates a new repository not found error
func NewRepositoryNotFoundError(err error) error {
return &RepositoryNotFoundError{causingError: err}
}
// IsRepositoryNotFoundError checks if the given error is a repository not found error
func IsRepositoryNotFoundError(err error) bool {
var repoErr *RepositoryNotFoundError
return errors.As(err, &repoErr)
}

View file

@ -1,48 +0,0 @@
package pull_request
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRepositoryNotFoundError(t *testing.T) {
t.Run("NewRepositoryNotFoundError creates correct error type", func(t *testing.T) {
originalErr := errors.New("repository does not exist")
repoNotFoundErr := NewRepositoryNotFoundError(originalErr)
require.Error(t, repoNotFoundErr)
assert.Equal(t, "repository does not exist", repoNotFoundErr.Error())
})
t.Run("IsRepositoryNotFoundError identifies RepositoryNotFoundError", func(t *testing.T) {
originalErr := errors.New("repository does not exist")
repoNotFoundErr := NewRepositoryNotFoundError(originalErr)
assert.True(t, IsRepositoryNotFoundError(repoNotFoundErr))
})
t.Run("IsRepositoryNotFoundError returns false for regular errors", func(t *testing.T) {
regularErr := errors.New("some other error")
assert.False(t, IsRepositoryNotFoundError(regularErr))
})
t.Run("IsRepositoryNotFoundError returns false for nil error", func(t *testing.T) {
assert.False(t, IsRepositoryNotFoundError(nil))
})
t.Run("IsRepositoryNotFoundError works with wrapped errors", func(t *testing.T) {
originalErr := errors.New("repository does not exist")
repoNotFoundErr := NewRepositoryNotFoundError(originalErr)
wrappedErr := errors.New("wrapped: " + repoNotFoundErr.Error())
// Direct RepositoryNotFoundError should be identified
assert.True(t, IsRepositoryNotFoundError(repoNotFoundErr))
// Wrapped string error should not be identified (this is expected behavior)
assert.False(t, IsRepositoryNotFoundError(wrappedErr))
})
}

View file

@ -52,17 +52,11 @@ func (g *GiteaService) List(ctx context.Context) ([]*PullRequest, error) {
State: gitea.StateOpen,
}
g.client.SetContext(ctx)
list := []*PullRequest{}
prs, resp, err := g.client.ListRepoPullRequests(g.owner, g.repo, opts)
prs, _, err := g.client.ListRepoPullRequests(g.owner, g.repo, opts)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
// return a custom error indicating that the repository is not found,
// but also returning the empty result since the decision to continue or not in this case is made by the caller
return list, NewRepositoryNotFoundError(err)
}
return nil, err
}
list := []*PullRequest{}
for _, pr := range prs {
if !giteaContainLabels(g.labels, pr.Labels) {
continue

View file

@ -339,35 +339,3 @@ func TestGetGiteaPRLabelNames(t *testing.T) {
})
}
}
func TestGiteaListReturnsRepositoryNotFoundError(t *testing.T) {
mux := http.NewServeMux()
server := httptest.NewServer(mux)
defer server.Close()
// Handle version endpoint that Gitea client calls first
mux.HandleFunc("/api/v1/version", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"version":"1.17.0+dev-452-g1f0541780"}`))
})
path := "/api/v1/repos/nonexistent/nonexistent/pulls?limit=0&page=1&state=open"
mux.HandleFunc(path, func(w http.ResponseWriter, _ *http.Request) {
// Return 404 status to simulate repository not found
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(`{"message": "404 Project Not Found"}`))
})
svc, err := NewGiteaService("", server.URL, "nonexistent", "nonexistent", []string{}, false)
require.NoError(t, err)
prs, err := svc.List(t.Context())
// Should return empty pull requests list
assert.Empty(t, prs)
// Should return RepositoryNotFoundError
require.Error(t, err)
assert.True(t, IsRepositoryNotFoundError(err), "Expected RepositoryNotFoundError but got: %v", err)
}

View file

@ -64,11 +64,6 @@ func (g *GithubService) List(ctx context.Context) ([]*PullRequest, error) {
for {
pulls, resp, err := g.client.PullRequests.List(ctx, g.owner, g.repo, opts)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
// return a custom error indicating that the repository is not found,
// but also returning the empty result since the decision to continue or not in this case is made by the caller
return pullRequests, NewRepositoryNotFoundError(err)
}
return nil, fmt.Errorf("error listing pull requests for %s/%s: %w", g.owner, g.repo, err)
}
for _, pull := range pulls {

View file

@ -1,12 +1,9 @@
package pull_request
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/google/go-github/v69/github"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -89,29 +86,3 @@ func TestGetGitHubPRLabelNames(t *testing.T) {
})
}
}
func TestGitHubListReturnsRepositoryNotFoundError(t *testing.T) {
mux := http.NewServeMux()
server := httptest.NewServer(mux)
defer server.Close()
path := "/repos/nonexistent/nonexistent/pulls"
mux.HandleFunc(path, func(w http.ResponseWriter, _ *http.Request) {
// Return 404 status to simulate repository not found
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(`{"message": "404 Project Not Found"}`))
})
svc, err := NewGithubService("", server.URL, "nonexistent", "nonexistent", []string{}, nil)
require.NoError(t, err)
prs, err := svc.List(t.Context())
// Should return empty pull requests list
assert.Empty(t, prs)
// Should return RepositoryNotFoundError
require.Error(t, err)
assert.True(t, IsRepositoryNotFoundError(err), "Expected RepositoryNotFoundError but got: %v", err)
}

View file

@ -76,11 +76,6 @@ func (g *GitLabService) List(ctx context.Context) ([]*PullRequest, error) {
for {
mrs, resp, err := g.client.MergeRequests.ListProjectMergeRequests(g.project, opts, gitlab.WithContext(ctx))
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
// return a custom error indicating that the repository is not found,
// but also returning the empty result since the decision to continue or not in this case is made by the caller
return pullRequests, NewRepositoryNotFoundError(err)
}
return nil, fmt.Errorf("error listing merge requests for project '%s': %w", g.project, err)
}
for _, mr := range mrs {

View file

@ -191,29 +191,3 @@ func TestListWithStateTLS(t *testing.T) {
})
}
}
func TestGitLabListReturnsRepositoryNotFoundError(t *testing.T) {
mux := http.NewServeMux()
server := httptest.NewServer(mux)
defer server.Close()
path := "/api/v4/projects/nonexistent/merge_requests"
mux.HandleFunc(path, func(w http.ResponseWriter, _ *http.Request) {
// Return 404 status to simulate repository not found
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(`{"message": "404 Project Not Found"}`))
})
svc, err := NewGitLabService("", server.URL, "nonexistent", []string{}, "", "", false, nil)
require.NoError(t, err)
prs, err := svc.List(t.Context())
// Should return empty pull requests list
assert.Empty(t, prs)
// Should return RepositoryNotFoundError
require.Error(t, err)
assert.True(t, IsRepositoryNotFoundError(err), "Expected RepositoryNotFoundError but got: %v", err)
}

View file

@ -30,5 +30,4 @@ type PullRequestService interface {
type Filter struct {
BranchMatch *regexp.Regexp
TargetBranchMatch *regexp.Regexp
TitleMatch *regexp.Regexp
}

View file

@ -25,12 +25,6 @@ func compileFilters(filters []argoprojiov1alpha1.PullRequestGeneratorFilter) ([]
return nil, fmt.Errorf("error compiling TargetBranchMatch regexp %q: %w", *filter.TargetBranchMatch, err)
}
}
if filter.TitleMatch != nil {
outFilter.TitleMatch, err = regexp.Compile(*filter.TitleMatch)
if err != nil {
return nil, fmt.Errorf("error compiling TitleMatch regexp %q: %w", *filter.TitleMatch, err)
}
}
outFilters = append(outFilters, outFilter)
}
return outFilters, nil
@ -43,9 +37,6 @@ func matchFilter(pullRequest *PullRequest, filter *Filter) bool {
if filter.TargetBranchMatch != nil && !filter.TargetBranchMatch.MatchString(pullRequest.TargetBranch) {
return false
}
if filter.TitleMatch != nil && !filter.TitleMatch.MatchString(pullRequest.Title) {
return false
}
return true
}

View file

@ -137,110 +137,6 @@ func TestFilterTargetBranchMatch(t *testing.T) {
assert.Equal(t, "two", pullRequests[0].Branch)
}
func TestFilterTitleMatch(t *testing.T) {
provider, _ := NewFakeService(
t.Context(),
[]*PullRequest{
{
Number: 1,
Title: "PR one - filter",
Branch: "one",
TargetBranch: "master",
HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958",
Author: "name1",
},
{
Number: 2,
Title: "PR two - ignore",
Branch: "two",
TargetBranch: "branch1",
HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958",
Author: "name2",
},
{
Number: 3,
Title: "[filter] PR three",
Branch: "three",
TargetBranch: "branch2",
HeadSHA: "389d92cbf9ff857a39e6feccd32798ca700fb958",
Author: "name3",
},
{
Number: 4,
Title: "[ignore] PR four",
Branch: "four",
TargetBranch: "branch3",
HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958",
Author: "name4",
},
},
nil,
)
filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{
{
TitleMatch: strp("\\[filter]"),
},
}
pullRequests, err := ListPullRequests(t.Context(), provider, filters)
require.NoError(t, err)
assert.Len(t, pullRequests, 1)
assert.Equal(t, "three", pullRequests[0].Branch)
}
func TestMultiFilterOrWithTitle(t *testing.T) {
provider, _ := NewFakeService(
t.Context(),
[]*PullRequest{
{
Number: 1,
Title: "PR one - filter",
Branch: "one",
TargetBranch: "master",
HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958",
Author: "name1",
},
{
Number: 2,
Title: "PR two - ignore",
Branch: "two",
TargetBranch: "branch1",
HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958",
Author: "name2",
},
{
Number: 3,
Title: "[filter] PR three",
Branch: "three",
TargetBranch: "branch2",
HeadSHA: "389d92cbf9ff857a39e6feccd32798ca700fb958",
Author: "name3",
},
{
Number: 4,
Title: "[ignore] PR four",
Branch: "four",
TargetBranch: "branch3",
HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958",
Author: "name4",
},
},
nil,
)
filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{
{
TitleMatch: strp("\\[filter]"),
},
{
TitleMatch: strp("- filter"),
},
}
pullRequests, err := ListPullRequests(t.Context(), provider, filters)
require.NoError(t, err)
assert.Len(t, pullRequests, 2)
assert.Equal(t, "one", pullRequests[0].Branch)
assert.Equal(t, "three", pullRequests[1].Branch)
}
func TestMultiFilterOr(t *testing.T) {
provider, _ := NewFakeService(
t.Context(),
@ -296,7 +192,7 @@ func TestMultiFilterOr(t *testing.T) {
assert.Equal(t, "four", pullRequests[2].Branch)
}
func TestMultiFilterOrWithTargetBranchFilterOrWithTitleFilter(t *testing.T) {
func TestMultiFilterOrWithTargetBranchFilter(t *testing.T) {
provider, _ := NewFakeService(
t.Context(),
[]*PullRequest{
@ -332,14 +228,6 @@ func TestMultiFilterOrWithTargetBranchFilterOrWithTitleFilter(t *testing.T) {
HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958",
Author: "name4",
},
{
Number: 5,
Title: "PR title is different than branch name",
Branch: "five",
TargetBranch: "branch3",
HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958",
Author: "name5",
},
},
nil,
)
@ -352,21 +240,12 @@ func TestMultiFilterOrWithTargetBranchFilterOrWithTitleFilter(t *testing.T) {
BranchMatch: strp("r"),
TargetBranchMatch: strp("3"),
},
{
TitleMatch: strp("two"),
},
{
BranchMatch: strp("five"),
TitleMatch: strp("PR title is different than branch name"),
},
}
pullRequests, err := ListPullRequests(t.Context(), provider, filters)
require.NoError(t, err)
assert.Len(t, pullRequests, 3)
assert.Len(t, pullRequests, 2)
assert.Equal(t, "two", pullRequests[0].Branch)
assert.Equal(t, "four", pullRequests[1].Branch)
assert.Equal(t, "five", pullRequests[2].Branch)
assert.Equal(t, "PR title is different than branch name", pullRequests[2].Title)
}
func TestNoFilters(t *testing.T) {

View file

@ -14,7 +14,7 @@ import (
var ErrDisallowedSecretAccess = fmt.Errorf("secret must have label %q=%q", common.LabelKeySecretType, common.LabelValueSecretTypeSCMCreds)
// GetSecretRef gets the value of the key for the specified Secret resource.
// getSecretRef gets the value of the key for the specified Secret resource.
func GetSecretRef(ctx context.Context, k8sClient client.Client, ref *argoprojiov1alpha1.SecretRef, namespace string, tokenRefStrictMode bool) (string, error) {
if ref == nil {
return "", nil

37
assets/swagger.json generated
View file

@ -7260,10 +7260,6 @@
"description": "ApplicationSetStrategy configures how generated Applications are updated in sequence.",
"type": "object",
"properties": {
"deletionOrder": {
"type": "string",
"title": "DeletionOrder allows specifying the order for deleting generated apps when progressive sync is enabled.\naccepts values \"AllAtOnce\" and \"Reverse\""
},
"rollingSync": {
"$ref": "#/definitions/v1alpha1ApplicationSetRolloutStrategy"
},
@ -8639,20 +8635,12 @@
"title": "KustomizeOptions are options for kustomize to use when building manifests",
"properties": {
"binaryPath": {
"description": "Deprecated: Use settings.Settings instead. See: settings.Settings.KustomizeVersions.\nIf this field is set, it will be used as the Kustomize binary path.\nOtherwise, Versions is used.",
"type": "string",
"title": "BinaryPath holds optional path to kustomize binary"
},
"buildOptions": {
"type": "string",
"title": "BuildOptions is a string of build parameters to use when calling `kustomize build`"
},
"versions": {
"description": "Versions is a list of Kustomize versions and their corresponding binary paths and build options.",
"type": "array",
"items": {
"$ref": "#/definitions/v1alpha1KustomizeVersion"
}
}
}
},
@ -8716,24 +8704,6 @@
}
}
},
"v1alpha1KustomizeVersion": {
"type": "object",
"title": "KustomizeVersion holds information about additional Kustomize versions",
"properties": {
"buildOptions": {
"type": "string",
"title": "BuildOptions that are specific to a Kustomize version"
},
"name": {
"type": "string",
"title": "Name holds Kustomize version name"
},
"path": {
"type": "string",
"title": "Path holds the corresponding binary path"
}
}
},
"v1alpha1ListGenerator": {
"type": "object",
"title": "ListGenerator include items info",
@ -9055,10 +9025,6 @@
"bitbucketServer": {
"$ref": "#/definitions/v1alpha1PullRequestGeneratorBitbucketServer"
},
"continueOnRepoNotFoundError": {
"description": "ContinueOnRepoNotFoundError is a flag to continue the ApplicationSet Pull Request generator parameters generation even if the repository is not found.",
"type": "boolean"
},
"filters": {
"description": "Filters for which pull requests should be considered.",
"type": "array",
@ -9188,9 +9154,6 @@
},
"targetBranchMatch": {
"type": "string"
},
"titleMatch": {
"type": "string"
}
}
},

View file

@ -415,6 +415,7 @@ func reconcileApplications(
},
settingsMgr,
stateCache,
projInformer,
server,
cache,
time.Second,
@ -463,7 +464,7 @@ func reconcileApplications(
sources = append(sources, app.Spec.GetSource())
revisions = append(revisions, app.Spec.GetSource().TargetRevision)
res, err := appStateManager.CompareAppState(&app, proj, revisions, sources, false, false, nil, false)
res, err := appStateManager.CompareAppState(&app, proj, revisions, sources, false, false, nil, false, false)
if err != nil {
return nil, fmt.Errorf("error comparing app states: %w", err)
}

View file

@ -24,24 +24,24 @@ func TestRun_SignalHandling_GracefulShutdown(t *testing.T) {
},
}
var runErr error
var err error
doneCh := make(chan struct{})
go func() {
runErr = d.Run(t.Context(), &DashboardConfig{ClientOpts: &apiclient.ClientOptions{}})
err = d.Run(t.Context(), &DashboardConfig{ClientOpts: &apiclient.ClientOptions{}})
close(doneCh)
}()
// Allow some time for the dashboard to register the signal handler
time.Sleep(50 * time.Millisecond)
proc, procErr := os.FindProcess(os.Getpid())
require.NoErrorf(t, procErr, "failed to find process: %v", procErr)
sigErr := proc.Signal(syscall.SIGINT)
require.NoErrorf(t, sigErr, "failed to send SIGINT: %v", sigErr)
proc, err := os.FindProcess(os.Getpid())
require.NoErrorf(t, err, "failed to find process: %v", err)
err = proc.Signal(syscall.SIGINT)
require.NoErrorf(t, err, "failed to send SIGINT: %v", err)
select {
case <-doneCh:
require.NoError(t, runErr)
require.NoError(t, err)
case <-time.After(500 * time.Millisecond):
t.Fatal("timeout: dashboard.Run did not exit after SIGINT")
}

View file

@ -185,7 +185,8 @@ argocd login cd.argoproj.io --core`,
command.Flags().StringVar(&password, "password", "", "The password of an account to authenticate")
command.Flags().BoolVar(&sso, "sso", false, "Perform SSO login")
command.Flags().IntVar(&ssoPort, "sso-port", DefaultSSOLocalPort, "Port to run local OAuth2 login application")
command.Flags().BoolVar(&skipTestTLS, "skip-test-tls", false, "Skip testing whether the server is configured with TLS (this can help when the command hangs for no apparent reason)")
command.Flags().
BoolVar(&skipTestTLS, "skip-test-tls", false, "Skip testing whether the server is configured with TLS (this can help when the command hangs for no apparent reason)")
command.Flags().BoolVar(&ssoLaunchBrowser, "sso-launch-browser", true, "Automatically launch the system default browser when performing SSO login")
return command
}

View file

@ -330,25 +330,6 @@ func NewRepoListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
command := &cobra.Command{
Use: "list",
Short: "List configured repositories",
Example: `
# List all repositories
argocd repo list
# List repositories in wide format
argocd repo list -o wide
# List repositories in YAML format
argocd repo list -o yaml
# List repositories in JSON format
argocd repo list -o json
# List urls of repositories
argocd repo list -o url
# Force refresh of cached repository connection status
argocd repo list --refresh hard
`,
Run: func(c *cobra.Command, _ []string) {
ctx := c.Context()

View file

@ -10,7 +10,6 @@ import (
grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/retry"
log "github.com/sirupsen/logrus"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
@ -45,10 +44,11 @@ func NewConnection(address string) (*grpc.ClientConn, error) {
}
unaryInterceptors := []grpc.UnaryClientInterceptor{grpc_retry.UnaryClientInterceptor(retryOpts...)}
dialOpts := []grpc.DialOption{
grpc.WithStreamInterceptor(grpc_util.RetryOnlyForServerStreamInterceptor(retryOpts...)),
grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(retryOpts...)),
grpc.WithChainUnaryInterceptor(unaryInterceptors...),
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(MaxGRPCMessageSize), grpc.MaxCallSendMsgSize(MaxGRPCMessageSize)),
grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
grpc.WithUnaryInterceptor(grpc_util.OTELUnaryClientInterceptor()),
grpc.WithStreamInterceptor(grpc_util.OTELStreamClientInterceptor()),
}
dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials()))

View file

@ -49,11 +49,13 @@ func NewServer(initConstants plugin.CMPServerInitConstants) (*ArgoCDCMPServer, e
serverLog := log.NewEntry(log.StandardLogger())
streamInterceptors := []grpc.StreamServerInterceptor{
otelgrpc.StreamServerInterceptor(), //nolint:staticcheck // TODO: ignore SA1019 for depreciation: see https://github.com/argoproj/argo-cd/issues/18258
logging.StreamServerInterceptor(grpc_util.InterceptorLogger(serverLog)),
serverMetrics.StreamServerInterceptor(),
recovery.StreamServerInterceptor(recovery.WithRecoveryHandler(grpc_util.LoggerRecoveryHandler(serverLog))),
}
unaryInterceptors := []grpc.UnaryServerInterceptor{
otelgrpc.UnaryServerInterceptor(), //nolint:staticcheck // TODO: ignore SA1019 for depreciation: see https://github.com/argoproj/argo-cd/issues/18258
logging.UnaryServerInterceptor(grpc_util.InterceptorLogger(serverLog)),
serverMetrics.UnaryServerInterceptor(),
recovery.UnaryServerInterceptor(recovery.WithRecoveryHandler(grpc_util.LoggerRecoveryHandler(serverLog))),
@ -69,7 +71,6 @@ func NewServer(initConstants plugin.CMPServerInitConstants) (*ArgoCDCMPServer, e
MinTime: common.GetGRPCKeepAliveEnforcementMinimum(),
},
),
grpc.StatsHandler(otelgrpc.NewServerHandler()),
}
return &ArgoCDCMPServer{

View file

@ -17,7 +17,6 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/argoproj/argo-cd/v3/commitserver/apiclient"
"github.com/argoproj/argo-cd/v3/common"
appv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/util/git"
"github.com/argoproj/argo-cd/v3/util/io"
@ -25,9 +24,6 @@ import (
var sprigFuncMap = sprig.GenericFuncMap() // a singleton for better performance
const gitAttributesContents = `*/README.md linguist-generated=true
*/hydrator.metadata linguist-generated=true`
func init() {
// Avoid allowing the user to learn things about the environment.
delete(sprigFuncMap, "env")
@ -61,12 +57,6 @@ func WriteForPaths(root *os.Root, repoUrl, drySha string, dryCommitMetadata *app
return fmt.Errorf("failed to write top-level hydrator metadata: %w", err)
}
// Write .gitattributes
err = writeGitAttributes(root)
if err != nil {
return fmt.Errorf("failed to write git attributes: %w", err)
}
for _, p := range paths {
hydratePath := p.Path
if hydratePath == "." {
@ -147,30 +137,6 @@ func writeReadme(root *os.Root, dirPath string, metadata hydratorMetadataFile) e
return nil
}
func writeGitAttributes(root *os.Root) error {
gitAttributesFile, err := root.Create(".gitattributes")
if err != nil {
return fmt.Errorf("failed to create git attributes file: %w", err)
}
defer func() {
err = gitAttributesFile.Close()
if err != nil {
log.WithFields(log.Fields{
common.SecurityField: common.SecurityMedium,
common.SecurityCWEField: common.SecurityCWEMissingReleaseOfFileDescriptor,
}).Errorf("error closing file %q: %v", gitAttributesFile.Name(), err)
}
}()
_, err = gitAttributesFile.WriteString(gitAttributesContents)
if err != nil {
return fmt.Errorf("failed to write git attributes: %w", err)
}
return nil
}
// writeManifests writes the manifests to the manifest.yaml file, truncating the file if it exists and appending the
// manifests in the order they are provided.
func writeManifests(root *os.Root, dirPath string, manifests []*apiclient.HydratedManifestDetails) error {

View file

@ -223,16 +223,3 @@ func TestWriteManifests(t *testing.T) {
require.NoError(t, err)
assert.Contains(t, string(manifestBytes), "kind")
}
func TestWriteGitAttributes(t *testing.T) {
root := tempRoot(t)
err := writeGitAttributes(root)
require.NoError(t, err)
gitAttributesPath := filepath.Join(root.Name(), ".gitattributes")
gitAttributesBytes, err := os.ReadFile(gitAttributesPath)
require.NoError(t, err)
assert.Contains(t, string(gitAttributesBytes), "*/README.md linguist-generated=true")
assert.Contains(t, string(gitAttributesBytes), "*/hydrator.metadata linguist-generated=true")
}

View file

@ -1,82 +0,0 @@
package common
import (
"runtime"
"testing"
"github.com/stretchr/testify/assert"
)
func TestGetVersion(t *testing.T) {
tests := []struct {
name string
inputGitCommit string
inputGitTag string
inputTreeState string
inputVersion string
expected string
}{
{
name: "Official release with tag and clean state",
inputGitCommit: "abcdef123456",
inputGitTag: "v1.2.3",
inputTreeState: "clean",
inputVersion: "1.2.3",
expected: "v1.2.3",
},
{
name: "Dirty state with commit",
inputGitCommit: "deadbeefcafebabe",
inputGitTag: "",
inputTreeState: "dirty",
inputVersion: "2.0.1",
expected: "v2.0.1+deadbee.dirty",
},
{
name: "Clean state with commit, no tag",
inputGitCommit: "cafebabedeadbeef",
inputGitTag: "",
inputTreeState: "clean",
inputVersion: "2.1.0",
expected: "v2.1.0+cafebab",
},
{
name: "Missing commit and tag",
inputGitCommit: "",
inputGitTag: "",
inputTreeState: "clean",
inputVersion: "3.1.0",
expected: "v3.1.0+unknown",
},
{
name: "Short commit",
inputGitCommit: "abc",
inputGitTag: "",
inputTreeState: "clean",
inputVersion: "4.0.0",
expected: "v4.0.0+unknown",
},
}
for _, tt := range tests {
gitCommit = tt.inputGitCommit
gitTag = tt.inputGitTag
gitTreeState = tt.inputTreeState
version = tt.inputVersion
buildDate = "2025-06-26"
kubectlVersion = "v1.30.0"
extraBuildInfo = "test-build"
got := GetVersion()
assert.Equal(t, tt.expected, got.Version)
assert.Equal(t, buildDate, got.BuildDate)
assert.Equal(t, tt.inputGitCommit, got.GitCommit)
assert.Equal(t, tt.inputGitTag, got.GitTag)
assert.Equal(t, tt.inputTreeState, got.GitTreeState)
assert.Equal(t, runtime.Version(), got.GoVersion)
assert.Equal(t, runtime.Compiler, got.Compiler)
assert.Equal(t, runtime.GOOS+"/"+runtime.GOARCH, got.Platform)
assert.Equal(t, kubectlVersion, got.KubectlVersion)
assert.Equal(t, extraBuildInfo, got.ExtraBuildInfo)
}
}

View file

@ -47,7 +47,6 @@ import (
"github.com/argoproj/argo-cd/v3/common"
statecache "github.com/argoproj/argo-cd/v3/controller/cache"
"github.com/argoproj/argo-cd/v3/controller/hydrator"
hydratortypes "github.com/argoproj/argo-cd/v3/controller/hydrator/types"
"github.com/argoproj/argo-cd/v3/controller/metrics"
"github.com/argoproj/argo-cd/v3/controller/sharding"
"github.com/argoproj/argo-cd/v3/pkg/apis/application"
@ -116,7 +115,7 @@ type ApplicationController struct {
appOperationQueue workqueue.TypedRateLimitingInterface[string]
projectRefreshQueue workqueue.TypedRateLimitingInterface[string]
appHydrateQueue workqueue.TypedRateLimitingInterface[string]
hydrationQueue workqueue.TypedRateLimitingInterface[hydratortypes.HydrationQueueKey]
hydrationQueue workqueue.TypedRateLimitingInterface[hydrator.HydrationQueueKey]
appInformer cache.SharedIndexInformer
appLister applisters.ApplicationLister
projInformer cache.SharedIndexInformer
@ -126,7 +125,7 @@ type ApplicationController struct {
statusHardRefreshTimeout time.Duration
statusRefreshJitter time.Duration
selfHealTimeout time.Duration
selfHealBackoff *wait.Backoff
selfHealBackOff *wait.Backoff
selfHealBackoffCooldown time.Duration
syncTimeout time.Duration
db db.ArgoDB
@ -199,7 +198,7 @@ func NewApplicationController(
projectRefreshQueue: workqueue.NewTypedRateLimitingQueueWithConfig(ratelimiter.NewCustomAppControllerRateLimiter[string](rateLimiterConfig), workqueue.TypedRateLimitingQueueConfig[string]{Name: "project_reconciliation_queue"}),
appComparisonTypeRefreshQueue: workqueue.NewTypedRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter[string](rateLimiterConfig)),
appHydrateQueue: workqueue.NewTypedRateLimitingQueueWithConfig(ratelimiter.NewCustomAppControllerRateLimiter[string](rateLimiterConfig), workqueue.TypedRateLimitingQueueConfig[string]{Name: "app_hydration_queue"}),
hydrationQueue: workqueue.NewTypedRateLimitingQueueWithConfig(ratelimiter.NewCustomAppControllerRateLimiter[hydratortypes.HydrationQueueKey](rateLimiterConfig), workqueue.TypedRateLimitingQueueConfig[hydratortypes.HydrationQueueKey]{Name: "manifest_hydration_queue"}),
hydrationQueue: workqueue.NewTypedRateLimitingQueueWithConfig(ratelimiter.NewCustomAppControllerRateLimiter[hydrator.HydrationQueueKey](rateLimiterConfig), workqueue.TypedRateLimitingQueueConfig[hydrator.HydrationQueueKey]{Name: "manifest_hydration_queue"}),
db: db,
statusRefreshTimeout: appResyncPeriod,
statusHardRefreshTimeout: appHardResyncPeriod,
@ -209,7 +208,7 @@ func NewApplicationController(
auditLogger: argo.NewAuditLogger(kubeClientset, common.ApplicationController, enableK8sEvent),
settingsMgr: settingsMgr,
selfHealTimeout: selfHealTimeout,
selfHealBackoff: selfHealBackoff,
selfHealBackOff: selfHealBackoff,
selfHealBackoffCooldown: selfHealBackoffCooldown,
syncTimeout: syncTimeout,
clusterSharding: clusterSharding,
@ -329,7 +328,7 @@ func NewApplicationController(
}
}
stateCache := statecache.NewLiveStateCache(db, appInformer, ctrl.settingsMgr, ctrl.metricsServer, ctrl.handleObjectUpdated, clusterSharding, argo.NewResourceTracking())
appStateManager := NewAppStateManager(db, applicationClientset, repoClientset, namespace, kubectl, ctrl.onKubectlRun, ctrl.settingsMgr, stateCache, ctrl.metricsServer, argoCache, ctrl.statusRefreshTimeout, argo.NewResourceTracking(), persistResourceHealth, repoErrorGracePeriod, serverSideDiff, ignoreNormalizerOpts)
appStateManager := NewAppStateManager(db, applicationClientset, repoClientset, namespace, kubectl, ctrl.onKubectlRun, ctrl.settingsMgr, stateCache, projInformer, ctrl.metricsServer, argoCache, ctrl.statusRefreshTimeout, argo.NewResourceTracking(), persistResourceHealth, repoErrorGracePeriod, serverSideDiff, ignoreNormalizerOpts)
ctrl.appInformer = appInformer
ctrl.appLister = appLister
ctrl.projInformer = projInformer
@ -1396,12 +1395,12 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli
if isOperationInProgress(app) {
state = app.Status.OperationState.DeepCopy()
terminating = state.Phase == synccommon.OperationTerminating
// Failed operation with retry strategy might have be in-progress and has completion time
switch {
case state.FinishedAt != nil && !terminating:
// Failed operation with retry strategy might be in-progress and has completion time
retryAt, err := app.Status.OperationState.Operation.Retry.NextRetryAt(state.FinishedAt.Time, state.RetryCount)
if err != nil {
state.Phase = synccommon.OperationError
state.Phase = synccommon.OperationFailed
state.Message = err.Error()
ctrl.setOperationState(app, state)
return
@ -1412,11 +1411,12 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli
ctrl.requestAppRefresh(app.QualifiedName(), CompareWithLatest.Pointer(), &retryAfter)
return
}
// Get rid of sync results and null out previous operation completion time
// This will start the retry attempt
// retrying operation. remove previous failure time in app since it is used as a trigger
// that previous failed and operation should be retried
state.FinishedAt = nil
state.SyncResult = nil
ctrl.setOperationState(app, state)
// Get rid of sync results and null out previous operation completion time
state.SyncResult = nil
case ctrl.syncTimeout != time.Duration(0) && time.Now().After(state.StartedAt.Add(ctrl.syncTimeout)) && !terminating:
state.Phase = synccommon.OperationTerminating
state.Message = "operation is terminating due to timeout"
@ -1426,7 +1426,7 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli
logCtx.Infof("Resuming in-progress operation. phase: %s, message: %s", state.Phase, state.Message)
}
} else {
state = NewOperationState(*app.Operation)
state = &appv1.OperationState{Phase: synccommon.OperationRunning, Operation: *app.Operation, StartedAt: metav1.Now()}
ctrl.setOperationState(app, state)
if ctrl.syncTimeout != time.Duration(0) {
// Schedule a check during which the timeout would be checked.
@ -1436,15 +1436,22 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli
}
ts.AddCheckpoint("initial_operation_stage_ms")
project, err := ctrl.getAppProj(app)
if err == nil {
// Start or resume the sync
ctrl.appStateManager.SyncAppState(app, project, state)
// Call GetDestinationCluster to validate the destination cluster.
if _, err := argo.GetDestinationCluster(context.Background(), app.Spec.Destination, ctrl.db); err != nil {
state.Phase = synccommon.OperationFailed
state.Message = err.Error()
} else {
state.Phase = synccommon.OperationError
state.Message = fmt.Sprintf("Failed to load application project: %v", err)
ctrl.appStateManager.SyncAppState(app, state)
}
ts.AddCheckpoint("validate_and_sync_app_state_ms")
// Check whether application is allowed to use project
_, err := ctrl.getAppProj(app)
ts.AddCheckpoint("get_app_proj_ms")
if err != nil {
state.Phase = synccommon.OperationError
state.Message = err.Error()
}
ts.AddCheckpoint("sync_app_state_ms")
switch state.Phase {
case synccommon.OperationRunning:
@ -1452,6 +1459,12 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli
// to clobber the Terminated state with Running. Get the latest app state to check for this.
freshApp, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace).Get(context.Background(), app.Name, metav1.GetOptions{})
if err == nil {
// App may have lost permissions to use the project meanwhile.
_, err = ctrl.getAppProj(freshApp)
if err != nil {
state.Phase = synccommon.OperationFailed
state.Message = fmt.Sprintf("operation not allowed: %v", err)
}
if freshApp.Status.OperationState != nil && freshApp.Status.OperationState.Phase == synccommon.OperationTerminating {
state.Phase = synccommon.OperationTerminating
state.Message = "operation is terminating"
@ -1465,7 +1478,7 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli
now := metav1.Now()
state.FinishedAt = &now
if retryAt, err := state.Operation.Retry.NextRetryAt(now.Time, state.RetryCount); err != nil {
state.Phase = synccommon.OperationError
state.Phase = synccommon.OperationFailed
state.Message = fmt.Sprintf("%s (failed to retry: %v)", state.Message, err)
} else {
state.Phase = synccommon.OperationRunning
@ -1747,7 +1760,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
sources = append(sources, app.Spec.GetSource())
}
compareResult, err := ctrl.appStateManager.CompareAppState(app, project, revisions, sources, refreshType == appv1.RefreshTypeHard, comparisonLevel == CompareWithLatestForceResolve, localManifests, hasMultipleSources)
compareResult, err := ctrl.appStateManager.CompareAppState(app, project, revisions, sources, refreshType == appv1.RefreshTypeHard, comparisonLevel == CompareWithLatestForceResolve, localManifests, hasMultipleSources, false)
ts.AddCheckpoint("compare_app_state_ms")
@ -1773,7 +1786,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
canSync, _ := project.Spec.SyncWindows.Matches(app).CanSync(false)
if canSync {
syncErrCond, opDuration := ctrl.autoSync(app, compareResult.syncStatus, compareResult.resources, compareResult.revisionsMayHaveChanges)
syncErrCond, opDuration := ctrl.autoSync(app, compareResult.syncStatus, compareResult.resources, compareResult.revisionUpdated)
setOpDuration = opDuration
if syncErrCond != nil {
app.Status.SetConditions(
@ -2068,7 +2081,7 @@ func (ctrl *ApplicationController) persistAppStatus(orig *appv1.Application, new
}
// autoSync will initiate a sync operation for an application configured with automated sync
func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *appv1.SyncStatus, resources []appv1.ResourceStatus, shouldCompareRevisions bool) (*appv1.ApplicationCondition, time.Duration) {
func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *appv1.SyncStatus, resources []appv1.ResourceStatus, revisionUpdated bool) (*appv1.ApplicationCondition, time.Duration) {
logCtx := log.WithFields(applog.GetAppLogFields(app))
ts := stats.NewTimingStats()
defer func() {
@ -2112,66 +2125,65 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *
}
}
desiredRevisions := []string{syncStatus.Revision}
if app.Spec.HasMultipleSources() {
desiredRevisions = syncStatus.Revisions
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(), revisionUpdated)
ts.AddCheckpoint("already_attempted_sync_ms")
op := appv1.Operation{
Sync: &appv1.SyncOperation{
Revision: syncStatus.Revision,
Revision: desiredCommitSHA,
Prune: app.Spec.SyncPolicy.Automated.Prune,
SyncOptions: app.Spec.SyncPolicy.SyncOptions,
Revisions: syncStatus.Revisions,
Revisions: desiredCommitSHAsMS,
},
InitiatedBy: appv1.OperationInitiator{Automated: true},
Retry: appv1.RetryStrategy{Limit: 5},
}
if app.Spec.SyncPolicy.Retry != nil {
op.Retry = *app.Spec.SyncPolicy.Retry
}
// It is possible for manifests to remain OutOfSync even after a sync/kubectl apply (e.g.
// auto-sync with pruning disabled). We need to ensure that we do not keep Syncing an
// application in an infinite loop. To detect this, we only attempt the Sync if the revision
// and parameter overrides are different from our most recent sync operation.
alreadyAttempted, lastAttemptedRevisions, lastAttemptedPhase := alreadyAttemptedSync(app, desiredRevisions, shouldCompareRevisions)
ts.AddCheckpoint("already_attempted_sync_ms")
if alreadyAttempted {
if !lastAttemptedPhase.Successful() {
logCtx.Warnf("Skipping auto-sync: failed previous sync attempt to %s and will not retry for %s", lastAttemptedRevisions, desiredRevisions)
message := fmt.Sprintf("Failed last sync attempt to %s: %s", lastAttemptedRevisions, app.Status.OperationState.Message)
if alreadyAttempted && (!selfHeal || !attemptPhase.Successful()) {
if !attemptPhase.Successful() {
logCtx.Warnf("Skipping auto-sync: failed previous sync attempt to %s", desiredCommitSHA)
message := fmt.Sprintf("Failed sync attempt to %s: %s", desiredCommitSHA, app.Status.OperationState.Message)
return &appv1.ApplicationCondition{Type: appv1.ApplicationConditionSyncError, Message: message}, 0
}
if !app.Spec.SyncPolicy.Automated.SelfHeal {
logCtx.Infof("Skipping auto-sync: most recent sync already to %s", desiredRevisions)
return nil, 0
logCtx.Infof("Skipping auto-sync: most recent sync already to %s", desiredCommitSHA)
return nil, 0
} else if selfHeal {
shouldSelfHeal, retryAfter := ctrl.shouldSelfHeal(app, alreadyAttempted)
if app.Status.OperationState != nil && app.Status.OperationState.Operation.Sync != nil {
op.Sync.SelfHealAttemptsCount = app.Status.OperationState.Operation.Sync.SelfHealAttemptsCount
}
// Self heal will trigger a new sync operation when the desired state changes and cause the application to
// be OutOfSync when it was previously synced Successfully. This means SelfHeal should only ever be attempted
// when the revisions have not changed, and where the previous sync to these revision was successful
// Only carry SelfHealAttemptsCount to be increased when the selfHealBackoffCooldown has not elapsed yet
if !ctrl.selfHealBackoffCooldownElapsed(app) {
if app.Status.OperationState != nil && app.Status.OperationState.Operation.Sync != nil {
op.Sync.SelfHealAttemptsCount = app.Status.OperationState.Operation.Sync.SelfHealAttemptsCount
if alreadyAttempted {
if !shouldSelfHeal {
logCtx.Infof("Skipping auto-sync: already attempted sync to %s with timeout %v (retrying in %v)", desiredCommitSHA, ctrl.selfHealTimeout, retryAfter)
ctrl.requestAppRefresh(app.QualifiedName(), CompareWithLatest.Pointer(), &retryAfter)
return nil, 0
}
}
if remainingTime := ctrl.selfHealRemainingBackoff(app, int(op.Sync.SelfHealAttemptsCount)); remainingTime > 0 {
logCtx.Infof("Skipping auto-sync: already attempted sync to %s with timeout %v (retrying in %v)", lastAttemptedRevisions, ctrl.selfHealTimeout, remainingTime)
ctrl.requestAppRefresh(app.QualifiedName(), CompareWithLatest.Pointer(), &remainingTime)
return nil, 0
}
op.Sync.SelfHealAttemptsCount++
for _, resource := range resources {
if resource.Status != appv1.SyncStatusCodeSynced {
op.Sync.Resources = append(op.Sync.Resources, appv1.SyncOperationResource{
Kind: resource.Kind,
Group: resource.Group,
Name: resource.Name,
})
op.Sync.SelfHealAttemptsCount++
for _, resource := range resources {
if resource.Status != appv1.SyncStatusCodeSynced {
op.Sync.Resources = append(op.Sync.Resources, appv1.SyncOperationResource{
Kind: resource.Kind,
Group: resource.Group,
Name: resource.Name,
})
}
}
}
}
@ -2185,7 +2197,7 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *
}
}
if bAllNeedPrune {
message := fmt.Sprintf("Skipping sync attempt to %s: auto-sync will wipe out all resources", desiredRevisions)
message := fmt.Sprintf("Skipping sync attempt to %s: auto-sync will wipe out all resources", desiredCommitSHA)
logCtx.Warn(message)
return &appv1.ApplicationCondition{Type: appv1.ApplicationConditionSyncError, Message: message}, 0
}
@ -2201,65 +2213,62 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *
if stderrors.Is(err, argo.ErrAnotherOperationInProgress) {
// skipping auto-sync because another operation is in progress and was not noticed due to stale data in informer
// it is safe to skip auto-sync because it is already running
logCtx.Warnf("Failed to initiate auto-sync to %s: %v", desiredRevisions, err)
logCtx.Warnf("Failed to initiate auto-sync to %s: %v", desiredCommitSHA, err)
return nil, 0
}
logCtx.Errorf("Failed to initiate auto-sync to %s: %v", desiredRevisions, err)
logCtx.Errorf("Failed to initiate auto-sync to %s: %v", desiredCommitSHA, err)
return &appv1.ApplicationCondition{Type: appv1.ApplicationConditionSyncError, Message: err.Error()}, setOpTime
}
ctrl.writeBackToInformer(updatedApp)
ts.AddCheckpoint("write_back_to_informer_ms")
message := fmt.Sprintf("Initiated automated sync to %s", desiredRevisions)
var target string
if updatedApp.Spec.HasMultipleSources() {
target = strings.Join(desiredCommitSHAsMS, ", ")
} else {
target = desiredCommitSHA
}
message := fmt.Sprintf("Initiated automated sync to '%s'", target)
ctrl.logAppEvent(context.TODO(), app, argo.EventInfo{Reason: argo.EventReasonOperationStarted, Type: corev1.EventTypeNormal}, message)
logCtx.Info(message)
return nil, setOpTime
}
// alreadyAttemptedSync returns whether the most recently synced revision(s) exactly match the given desiredRevisions
// and for the same application source. If the revision(s) have changed or the Application source configuration has been updated,
// it will return false, indicating that a new sync should be attempted.
// When newRevisionHasChanges is false, due to commits not having direct changes on the application, it will not compare the revision(s), but only the sources.
// It also returns the last synced revisions if any, and the result of that last sync operation.
func alreadyAttemptedSync(app *appv1.Application, desiredRevisions []string, newRevisionHasChanges bool) (bool, []string, synccommon.OperationPhase) {
if app.Status.OperationState == nil {
// The operation state may be removed when new operations are triggered
return false, []string{}, ""
// alreadyAttemptedSync returns whether the most recent sync was performed against the
// commitSHA and with the same app source config which are currently set in the app.
func alreadyAttemptedSync(app *appv1.Application, commitSHA string, commitSHAsMS []string, hasMultipleSources bool, revisionUpdated bool) (bool, synccommon.OperationPhase) {
if app.Status.OperationState == nil || app.Status.OperationState.Operation.Sync == nil || app.Status.OperationState.SyncResult == nil {
return false, ""
}
if app.Status.OperationState.SyncResult == nil {
// If the sync has completed without result, it is very likely that an error happened
// We don't want to resync with auto-sync indefinitely. We should have retried the configured amount of time already
// In this case, a manual action to restore the app may be required
log.WithFields(applog.GetAppLogFields(app)).Warn("Already attempted sync: sync does not have any results")
return app.Status.OperationState.Phase.Completed(), []string{}, app.Status.OperationState.Phase
}
if newRevisionHasChanges {
log.WithFields(applog.GetAppLogFields(app)).Infof("Already attempted sync: comparing synced revisions to %s", desiredRevisions)
if app.Spec.HasMultipleSources() {
if !reflect.DeepEqual(app.Status.OperationState.SyncResult.Revisions, desiredRevisions) {
return false, app.Status.OperationState.SyncResult.Revisions, app.Status.OperationState.Phase
if hasMultipleSources {
if revisionUpdated {
if !reflect.DeepEqual(app.Status.OperationState.SyncResult.Revisions, commitSHAsMS) {
return false, ""
}
} else {
if len(desiredRevisions) != 1 || app.Status.OperationState.SyncResult.Revision != desiredRevisions[0] {
return false, []string{app.Status.OperationState.SyncResult.Revision}, app.Status.OperationState.Phase
}
log.WithFields(applog.GetAppLogFields(app)).Debugf("Skipping auto-sync: commitSHA %s has no changes", commitSHA)
}
} else {
log.WithFields(applog.GetAppLogFields(app)).Debugf("Already attempted sync: revisions %s have no changes", desiredRevisions)
if revisionUpdated {
log.WithFields(applog.GetAppLogFields(app)).Infof("Executing compare of syncResult.Revision and commitSha because manifest changed: %v", commitSHA)
if app.Status.OperationState.SyncResult.Revision != commitSHA {
return false, ""
}
} else {
log.WithFields(applog.GetAppLogFields(app)).Debugf("Skipping auto-sync: commitSHA %s has no changes", commitSHA)
}
}
log.WithFields(applog.GetAppLogFields(app)).Debug("Already attempted sync: comparing sources")
if app.Spec.HasMultipleSources() {
return reflect.DeepEqual(app.Spec.Sources, app.Status.OperationState.SyncResult.Sources), app.Status.OperationState.SyncResult.Revisions, app.Status.OperationState.Phase
if hasMultipleSources {
return reflect.DeepEqual(app.Spec.Sources, app.Status.OperationState.SyncResult.Sources), app.Status.OperationState.Phase
}
return reflect.DeepEqual(app.Spec.GetSource(), app.Status.OperationState.SyncResult.Source), []string{app.Status.OperationState.SyncResult.Revision}, app.Status.OperationState.Phase
return reflect.DeepEqual(app.Spec.GetSource(), app.Status.OperationState.SyncResult.Source), app.Status.OperationState.Phase
}
func (ctrl *ApplicationController) selfHealRemainingBackoff(app *appv1.Application, selfHealAttemptsCount int) time.Duration {
func (ctrl *ApplicationController) shouldSelfHeal(app *appv1.Application, alreadyAttempted bool) (bool, time.Duration) {
if app.Status.OperationState == nil {
return time.Duration(0)
return true, time.Duration(0)
}
var timeSinceOperation *time.Duration
@ -2267,41 +2276,34 @@ func (ctrl *ApplicationController) selfHealRemainingBackoff(app *appv1.Applicati
timeSinceOperation = ptr.To(time.Since(app.Status.OperationState.FinishedAt.Time))
}
// Reset counter if the prior sync was successful and the cooldown period is over OR if the revision has changed
if !alreadyAttempted || (timeSinceOperation != nil && *timeSinceOperation >= ctrl.selfHealBackoffCooldown && app.Status.Sync.Status == appv1.SyncStatusCodeSynced) {
app.Status.OperationState.Operation.Sync.SelfHealAttemptsCount = 0
}
var retryAfter time.Duration
if ctrl.selfHealBackoff == nil {
if ctrl.selfHealBackOff == nil {
if timeSinceOperation == nil {
retryAfter = ctrl.selfHealTimeout
} else {
retryAfter = ctrl.selfHealTimeout - *timeSinceOperation
}
} else {
backOff := *ctrl.selfHealBackoff
backOff.Steps = selfHealAttemptsCount
backOff := *ctrl.selfHealBackOff
backOff.Steps = int(app.Status.OperationState.Operation.Sync.SelfHealAttemptsCount)
var delay time.Duration
steps := backOff.Steps
for i := 0; i < steps; i++ {
delay = backOff.Step()
}
if timeSinceOperation == nil {
retryAfter = delay
} else {
retryAfter = delay - *timeSinceOperation
}
}
return retryAfter
}
// selfHealBackoffCooldownElapsed returns true when the last successful sync has occurred since longer
// than then self heal cooldown. This means that the application has been in sync for long enough to
// reset the self healing backoff to its initial state
func (ctrl *ApplicationController) selfHealBackoffCooldownElapsed(app *appv1.Application) bool {
if app.Status.OperationState == nil || app.Status.OperationState.FinishedAt == nil {
// Something is in progress, or about to be. In that case, selfHeal attempt should be zero anyway
return true
}
timeSinceLastOperation := time.Since(app.Status.OperationState.FinishedAt.Time)
return timeSinceLastOperation >= ctrl.selfHealBackoffCooldown && app.Status.OperationState.Phase.Successful()
return retryAfter <= 0, retryAfter
}
// isAppNamespaceAllowed returns whether the application is allowed in the

View file

@ -348,13 +348,10 @@ status:
- cccccccccccccccccccccccccccccccccccccccc
sources:
- path: some/path
helm:
valueFiles:
- $values_test/values.yaml
repoURL: https://github.com/argoproj/argocd-example-apps.git
- path: some/other/path
repoURL: https://github.com/argoproj/argocd-example-apps-fake.git
- ref: values_test
- path: some/other/path
repoURL: https://github.com/argoproj/argocd-example-apps-fake-ref.git
`
@ -628,13 +625,13 @@ func TestAutoSyncEnabledSetToTrue(t *testing.T) {
assert.False(t, app.Operation.Sync.Prune)
}
func TestAutoSyncMultiSourceWithoutSelfHeal(t *testing.T) {
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.OperationState.SyncResult.Revisions = []string{"z", "x", "v"}
app.Status.Sync.Revisions = []string{"z", "x", "v"}
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil)
syncStatus := v1alpha1.SyncStatus{
Status: v1alpha1.SyncStatusCodeOutOfSync,
@ -646,14 +643,15 @@ func TestAutoSyncMultiSourceWithoutSelfHeal(t *testing.T) {
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.OperationState.SyncResult.Revisions = []string{"z", "x", "v"}
app.Status.Sync.Revisions = []string{"a", "b", "c"}
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil)
syncStatus := v1alpha1.SyncStatus{
Status: v1alpha1.SyncStatusCodeOutOfSync,
Revisions: []string{"a", "b", "c"},
Revisions: []string{"z", "x", "v"},
}
cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{{Name: "guestbook-1", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync}}, true)
assert.Nil(t, cond)
@ -796,30 +794,6 @@ func TestSkipAutoSync(t *testing.T) {
assert.Nil(t, app.Operation)
})
t.Run("PreviousSyncAttemptError", func(t *testing.T) {
app := newFakeApp()
app.Status.OperationState = &v1alpha1.OperationState{
Operation: v1alpha1.Operation{
Sync: &v1alpha1.SyncOperation{},
},
Phase: synccommon.OperationError,
SyncResult: &v1alpha1.SyncOperationResult{
Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
Source: *app.Spec.Source.DeepCopy(),
},
}
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil)
syncStatus := v1alpha1.SyncStatus{
Status: v1alpha1.SyncStatusCodeOutOfSync,
Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
}
cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{{Name: "guestbook", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync}}, true)
assert.NotNil(t, cond)
app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get(t.Context(), "my-app", metav1.GetOptions{})
require.NoError(t, err)
assert.Nil(t, app.Operation)
})
t.Run("NeedsToPruneResourcesOnlyButAutomatedPruneDisabled", func(t *testing.T) {
app := newFakeApp()
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil)
@ -874,78 +848,45 @@ func TestAutoSyncIndicateError(t *testing.T) {
// TestAutoSyncParameterOverrides verifies we auto-sync if revision is same but parameter overrides are different
func TestAutoSyncParameterOverrides(t *testing.T) {
t.Run("Single source", func(t *testing.T) {
app := newFakeApp()
app.Spec.Source.Helm = &v1alpha1.ApplicationSourceHelm{
Parameters: []v1alpha1.HelmParameter{
{
Name: "a",
Value: "1",
},
app := newFakeApp()
app.Spec.Source.Helm = &v1alpha1.ApplicationSourceHelm{
Parameters: []v1alpha1.HelmParameter{
{
Name: "a",
Value: "1",
},
}
app.Status.OperationState = &v1alpha1.OperationState{
Operation: v1alpha1.Operation{
Sync: &v1alpha1.SyncOperation{
Source: &v1alpha1.ApplicationSource{
Helm: &v1alpha1.ApplicationSourceHelm{
Parameters: []v1alpha1.HelmParameter{
{
Name: "a",
Value: "2", // this value changed
},
},
}
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil)
syncStatus := v1alpha1.SyncStatus{
Status: v1alpha1.SyncStatusCodeOutOfSync,
Revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
}
app.Status.OperationState = &v1alpha1.OperationState{
Operation: v1alpha1.Operation{
Sync: &v1alpha1.SyncOperation{
Source: &v1alpha1.ApplicationSource{
Helm: &v1alpha1.ApplicationSourceHelm{
Parameters: []v1alpha1.HelmParameter{
{
Name: "a",
Value: "2", // this value changed
},
},
},
},
},
Phase: synccommon.OperationFailed,
SyncResult: &v1alpha1.SyncOperationResult{
Revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
}
syncStatus := v1alpha1.SyncStatus{
Status: v1alpha1.SyncStatusCodeOutOfSync,
},
Phase: synccommon.OperationFailed,
SyncResult: &v1alpha1.SyncOperationResult{
Revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
}
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil)
cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{{Name: "guestbook", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync}}, true)
assert.Nil(t, cond)
app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get(t.Context(), "my-app", metav1.GetOptions{})
require.NoError(t, err)
assert.NotNil(t, app.Operation)
})
t.Run("Multi sources", func(t *testing.T) {
app := newFakeMultiSourceApp()
app.Spec.Sources[0].Helm = &v1alpha1.ApplicationSourceHelm{
Parameters: []v1alpha1.HelmParameter{
{
Name: "a",
Value: "1",
},
},
}
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil)
app.Status.OperationState.SyncResult.Revisions = []string{"z", "x", "v"}
app.Status.OperationState.SyncResult.Sources[0].Helm = &v1alpha1.ApplicationSourceHelm{
Parameters: []v1alpha1.HelmParameter{
{
Name: "a",
Value: "2", // this value changed
},
},
}
syncStatus := v1alpha1.SyncStatus{
Status: v1alpha1.SyncStatusCodeOutOfSync,
Revisions: []string{"z", "x", "v"},
}
cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{{Name: "guestbook", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync}}, true)
assert.Nil(t, cond)
app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get(t.Context(), "my-app", metav1.GetOptions{})
require.NoError(t, err)
assert.NotNil(t, app.Operation)
})
},
}
cond, _ := ctrl.autoSync(app, &syncStatus, []v1alpha1.ResourceStatus{{Name: "guestbook", Kind: kube.DeploymentKind, Status: v1alpha1.SyncStatusCodeOutOfSync}}, true)
assert.Nil(t, cond)
app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get(t.Context(), "my-app", metav1.GetOptions{})
require.NoError(t, err)
assert.NotNil(t, app.Operation)
}
// TestFinalizeAppDeletion verifies application deletion
@ -1925,7 +1866,7 @@ apps/Deployment:
hs = {}
hs.status = ""
hs.message = ""
if obj.metadata ~= nil then
if obj.metadata.labels ~= nil then
current_status = obj.metadata.labels["status"]
@ -2122,7 +2063,7 @@ func TestProcessRequestedAppOperation_InvalidDestination(t *testing.T) {
ctrl.processRequestedAppOperation(app)
phase, _, _ := unstructured.NestedString(receivedPatch, "status", "operationState", "phase")
assert.Equal(t, string(synccommon.OperationError), phase)
assert.Equal(t, string(synccommon.OperationFailed), phase)
message, _, _ := unstructured.NestedString(receivedPatch, "status", "operationState", "message")
assert.Contains(t, message, "application destination can't have both name and server defined: another-cluster https://localhost:6443")
}
@ -2522,71 +2463,35 @@ func TestAppStatusIsReplaced(t *testing.T) {
func TestAlreadyAttemptSync(t *testing.T) {
app := newFakeApp()
defaultRevision := app.Status.OperationState.SyncResult.Revision
t.Run("no operation state", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState = nil
attempted, _, _ := alreadyAttemptedSync(app, []string{defaultRevision}, true)
attempted, _ := alreadyAttemptedSync(app, "", []string{}, false, false)
assert.False(t, attempted)
})
t.Run("no sync result for running sync", func(t *testing.T) {
t.Run("no sync operation", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.SyncResult = nil
app.Status.OperationState.Phase = synccommon.OperationRunning
attempted, _, _ := alreadyAttemptedSync(app, []string{defaultRevision}, true)
app.Status.OperationState.Operation.Sync = nil
attempted, _ := alreadyAttemptedSync(app, "", []string{}, false, false)
assert.False(t, attempted)
})
t.Run("no sync result for completed sync", func(t *testing.T) {
t.Run("no sync result", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.SyncResult = nil
app.Status.OperationState.Phase = synccommon.OperationError
attempted, _, _ := alreadyAttemptedSync(app, []string{defaultRevision}, true)
assert.True(t, attempted)
attempted, _ := alreadyAttemptedSync(app, "", []string{}, false, false)
assert.False(t, attempted)
})
t.Run("single source", func(t *testing.T) {
t.Run("no revision", func(t *testing.T) {
attempted, _, _ := alreadyAttemptedSync(app, []string{}, true)
assert.False(t, attempted)
})
t.Run("empty revision", func(t *testing.T) {
attempted, _, _ := alreadyAttemptedSync(app, []string{""}, true)
assert.False(t, attempted)
})
t.Run("too many revision", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.SyncResult.Revision = "sha"
attempted, _, _ := alreadyAttemptedSync(app, []string{"sha", "sha2"}, true)
assert.False(t, attempted)
})
t.Run("same manifest, same SHA with changes", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.SyncResult.Revision = "sha"
attempted, _, _ := alreadyAttemptedSync(app, []string{"sha"}, true)
t.Run("same manifest with sync result", func(t *testing.T) {
attempted, _ := alreadyAttemptedSync(app, "sha", []string{}, false, false)
assert.True(t, attempted)
})
t.Run("same manifest, different SHA with changes", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.SyncResult.Revision = "sha1"
attempted, _, _ := alreadyAttemptedSync(app, []string{"sha2"}, true)
assert.False(t, attempted)
})
t.Run("same manifest, different SHA without changes", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.SyncResult.Revision = "sha1"
attempted, _, _ := alreadyAttemptedSync(app, []string{"sha2"}, false)
assert.True(t, attempted)
})
t.Run("different manifest, same SHA with changes", func(t *testing.T) {
t.Run("same manifest with sync result different targetRevision, same SHA", func(t *testing.T) {
// This test represents the case where the user changed a source's target revision to a new branch, but it
// points to the same revision as the old branch. We currently do not consider this as having been "already
// attempted." In the future we may want to short-circuit the auto-sync in these cases.
@ -2594,101 +2499,55 @@ func TestAlreadyAttemptSync(t *testing.T) {
app.Status.OperationState.SyncResult.Source = v1alpha1.ApplicationSource{TargetRevision: "branch1"}
app.Spec.Source = &v1alpha1.ApplicationSource{TargetRevision: "branch2"}
app.Status.OperationState.SyncResult.Revision = "sha"
attempted, _, _ := alreadyAttemptedSync(app, []string{"sha"}, true)
attempted, _ := alreadyAttemptedSync(app, "sha", []string{}, false, false)
assert.False(t, attempted)
})
t.Run("different manifest, different SHA with changes", func(t *testing.T) {
t.Run("different manifest with sync result, different SHA", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.SyncResult.Source = v1alpha1.ApplicationSource{Path: "folder1"}
app.Spec.Source = &v1alpha1.ApplicationSource{Path: "folder2"}
app.Status.OperationState.SyncResult.Revision = "sha1"
attempted, _, _ := alreadyAttemptedSync(app, []string{"sha2"}, true)
attempted, _ := alreadyAttemptedSync(app, "sha2", []string{}, false, true)
assert.False(t, attempted)
})
t.Run("different manifest, different SHA without changes", func(t *testing.T) {
t.Run("different manifest with sync result, same SHA", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.SyncResult.Source = v1alpha1.ApplicationSource{Path: "folder1"}
app.Spec.Source = &v1alpha1.ApplicationSource{Path: "folder2"}
app.Status.OperationState.SyncResult.Revision = "sha1"
attempted, _, _ := alreadyAttemptedSync(app, []string{"sha2"}, false)
assert.False(t, attempted)
})
t.Run("different manifest, same SHA without changes", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.SyncResult.Source = v1alpha1.ApplicationSource{Path: "folder1"}
app.Spec.Source = &v1alpha1.ApplicationSource{Path: "folder2"}
app.Status.OperationState.SyncResult.Revision = "sha"
attempted, _, _ := alreadyAttemptedSync(app, []string{"sha"}, false)
assert.False(t, attempted)
attempted, _ := alreadyAttemptedSync(app, "sha", []string{}, false, true)
assert.True(t, attempted)
})
})
t.Run("multi-source", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.SyncResult.Sources = []v1alpha1.ApplicationSource{{Path: "folder1"}, {Path: "folder2"}}
app.Spec.Sources = []v1alpha1.ApplicationSource{{Path: "folder1"}, {Path: "folder2"}}
t.Run("same manifest, same SHAs with changes", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.SyncResult.Revisions = []string{"sha_a", "sha_b"}
attempted, _, _ := alreadyAttemptedSync(app, []string{"sha_a", "sha_b"}, true)
t.Run("same manifest with sync result", func(t *testing.T) {
attempted, _ := alreadyAttemptedSync(app, "", []string{"sha"}, true, false)
assert.True(t, attempted)
})
t.Run("same manifest, different SHAs with changes", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.SyncResult.Revisions = []string{"sha_a_=", "sha_b_1"}
attempted, _, _ := alreadyAttemptedSync(app, []string{"sha_a_2", "sha_b_2"}, true)
assert.False(t, attempted)
})
t.Run("same manifest, different SHA without changes", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.SyncResult.Revisions = []string{"sha_a_=", "sha_b_1"}
attempted, _, _ := alreadyAttemptedSync(app, []string{"sha_a_2", "sha_b_2"}, false)
assert.True(t, attempted)
})
t.Run("different manifest, same SHA with changes", func(t *testing.T) {
t.Run("same manifest with sync result, different targetRevision, same SHA", func(t *testing.T) {
// This test represents the case where the user changed a source's target revision to a new branch, but it
// points to the same revision as the old branch. We currently do not consider this as having been "already
// attempted." In the future we may want to short-circuit the auto-sync in these cases.
app := app.DeepCopy()
app.Status.OperationState.SyncResult.Sources = []v1alpha1.ApplicationSource{{TargetRevision: "branch1"}, {TargetRevision: "branch2"}}
app.Spec.Sources = []v1alpha1.ApplicationSource{{TargetRevision: "branch1"}, {TargetRevision: "branch3"}}
app.Status.OperationState.SyncResult.Revisions = []string{"sha_a_2", "sha_b_2"}
attempted, _, _ := alreadyAttemptedSync(app, []string{"sha_a_2", "sha_b_2"}, false)
app.Status.OperationState.SyncResult.Sources = []v1alpha1.ApplicationSource{{TargetRevision: "branch1"}}
app.Spec.Sources = []v1alpha1.ApplicationSource{{TargetRevision: "branch2"}}
app.Status.OperationState.SyncResult.Revisions = []string{"sha"}
attempted, _ := alreadyAttemptedSync(app, "", []string{"sha"}, true, false)
assert.False(t, attempted)
})
t.Run("different manifest, different SHA with changes", func(t *testing.T) {
t.Run("different manifest with sync result, different SHAs", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.SyncResult.Sources = []v1alpha1.ApplicationSource{{Path: "folder1"}, {Path: "folder2"}}
app.Spec.Sources = []v1alpha1.ApplicationSource{{Path: "folder1"}, {Path: "folder3"}}
app.Status.OperationState.SyncResult.Revisions = []string{"sha_a", "sha_b"}
attempted, _, _ := alreadyAttemptedSync(app, []string{"sha_a", "sha_b_2"}, true)
app.Status.OperationState.SyncResult.Revisions = []string{"sha_a_=", "sha_b_1"}
attempted, _ := alreadyAttemptedSync(app, "", []string{"sha_a_2", "sha_b_2"}, true, true)
assert.False(t, attempted)
})
t.Run("different manifest, different SHA without changes", func(t *testing.T) {
t.Run("different manifest with sync result, same SHAs", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.SyncResult.Sources = []v1alpha1.ApplicationSource{{Path: "folder1"}, {Path: "folder2"}}
app.Spec.Sources = []v1alpha1.ApplicationSource{{Path: "folder1"}, {Path: "folder3"}}
app.Status.OperationState.SyncResult.Revisions = []string{"sha_a", "sha_b"}
attempted, _, _ := alreadyAttemptedSync(app, []string{"sha_a", "sha_b_2"}, false)
assert.False(t, attempted)
})
t.Run("different manifest, same SHA without changes", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.SyncResult.Sources = []v1alpha1.ApplicationSource{{Path: "folder1"}, {Path: "folder2"}}
app.Spec.Sources = []v1alpha1.ApplicationSource{{Path: "folder1"}, {Path: "folder3"}}
app.Status.OperationState.SyncResult.Revisions = []string{"sha_a", "sha_b"}
attempted, _, _ := alreadyAttemptedSync(app, []string{"sha_a", "sha_b"}, false)
assert.False(t, attempted)
attempted, _ := alreadyAttemptedSync(app, "", []string{"sha_a", "sha_b"}, true, true)
assert.True(t, attempted)
})
})
}
@ -2700,13 +2559,14 @@ func assertDurationAround(t *testing.T, expected time.Duration, actual time.Dura
assert.LessOrEqual(t, expected, actual+delta)
}
func TestSelfHealRemainingBackoff(t *testing.T) {
func TestSelfHealExponentialBackoff(t *testing.T) {
ctrl := newFakeController(&fakeData{}, nil)
ctrl.selfHealBackoff = &wait.Backoff{
ctrl.selfHealBackOff = &wait.Backoff{
Factor: 3,
Duration: 2 * time.Second,
Cap: 2 * time.Minute,
}
app := &v1alpha1.Application{
Status: v1alpha1.ApplicationStatus{
OperationState: &v1alpha1.OperationState{
@ -2718,112 +2578,109 @@ func TestSelfHealRemainingBackoff(t *testing.T) {
}
testCases := []struct {
attempts int
attempts int64
expectedAttempts int64
finishedAt *metav1.Time
expectedDuration time.Duration
shouldSelfHeal bool
alreadyAttempted bool
syncStatus v1alpha1.SyncStatusCode
}{{
attempts: 0,
finishedAt: ptr.To(metav1.Now()),
expectedDuration: 0,
shouldSelfHeal: true,
alreadyAttempted: true,
expectedAttempts: 0,
syncStatus: v1alpha1.SyncStatusCodeOutOfSync,
}, {
attempts: 1,
finishedAt: ptr.To(metav1.Now()),
expectedDuration: 2 * time.Second,
shouldSelfHeal: false,
alreadyAttempted: true,
expectedAttempts: 1,
syncStatus: v1alpha1.SyncStatusCodeOutOfSync,
}, {
attempts: 2,
finishedAt: ptr.To(metav1.Now()),
expectedDuration: 6 * time.Second,
shouldSelfHeal: false,
alreadyAttempted: true,
expectedAttempts: 2,
syncStatus: v1alpha1.SyncStatusCodeOutOfSync,
}, {
attempts: 3,
finishedAt: nil,
expectedDuration: 18 * time.Second,
shouldSelfHeal: false,
alreadyAttempted: true,
expectedAttempts: 3,
syncStatus: v1alpha1.SyncStatusCodeOutOfSync,
}, {
attempts: 4,
finishedAt: nil,
expectedDuration: 54 * time.Second,
shouldSelfHeal: false,
alreadyAttempted: true,
expectedAttempts: 4,
syncStatus: v1alpha1.SyncStatusCodeOutOfSync,
}, {
attempts: 5,
finishedAt: nil,
expectedDuration: 120 * time.Second,
shouldSelfHeal: false,
alreadyAttempted: true,
expectedAttempts: 5,
syncStatus: v1alpha1.SyncStatusCodeOutOfSync,
}, {
attempts: 6,
finishedAt: nil,
expectedDuration: 120 * time.Second,
shouldSelfHeal: false,
alreadyAttempted: true,
expectedAttempts: 6,
syncStatus: v1alpha1.SyncStatusCodeOutOfSync,
}, {
attempts: 6,
finishedAt: nil,
expectedDuration: 0,
shouldSelfHeal: true,
alreadyAttempted: false,
expectedAttempts: 0,
syncStatus: v1alpha1.SyncStatusCodeOutOfSync,
}, { // backoff will not reset as finished tme isn't >= cooldown
attempts: 6,
finishedAt: ptr.To(metav1.Now()),
expectedDuration: 120 * time.Second,
shouldSelfHeal: false,
}, {
alreadyAttempted: true,
expectedAttempts: 6,
syncStatus: v1alpha1.SyncStatusCodeSynced,
}, { // backoff will reset as finished time is >= cooldown
attempts: 40,
finishedAt: &metav1.Time{Time: time.Now().Add(-1 * time.Minute)},
expectedDuration: 60 * time.Second,
shouldSelfHeal: false,
finishedAt: &metav1.Time{Time: time.Now().Add(-(1 * time.Minute))},
expectedDuration: -60 * time.Second,
shouldSelfHeal: true,
alreadyAttempted: true,
expectedAttempts: 0,
syncStatus: v1alpha1.SyncStatusCodeSynced,
}}
for i := range testCases {
tc := testCases[i]
t.Run(fmt.Sprintf("test case %d", i), func(t *testing.T) {
app.Status.OperationState.Operation.Sync.SelfHealAttemptsCount = tc.attempts
app.Status.OperationState.FinishedAt = tc.finishedAt
duration := ctrl.selfHealRemainingBackoff(app, tc.attempts)
shouldSelfHeal := duration <= 0
require.Equal(t, tc.shouldSelfHeal, shouldSelfHeal)
app.Status.Sync.Status = tc.syncStatus
ok, duration := ctrl.shouldSelfHeal(app, tc.alreadyAttempted)
require.Equal(t, ok, tc.shouldSelfHeal)
require.Equal(t, tc.expectedAttempts, app.Status.OperationState.Operation.Sync.SelfHealAttemptsCount)
assertDurationAround(t, tc.expectedDuration, duration)
})
}
}
func TestSelfHealBackoffCooldownElapsed(t *testing.T) {
cooldown := time.Second * 30
ctrl := newFakeController(&fakeData{}, nil)
ctrl.selfHealBackoffCooldown = cooldown
app := &v1alpha1.Application{
Status: v1alpha1.ApplicationStatus{
OperationState: &v1alpha1.OperationState{
Phase: synccommon.OperationSucceeded,
},
},
}
t.Run("operation not completed", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.FinishedAt = nil
elapsed := ctrl.selfHealBackoffCooldownElapsed(app)
assert.True(t, elapsed)
})
t.Run("successful operation finised after cooldown", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.FinishedAt = &metav1.Time{Time: time.Now().Add(-cooldown)}
elapsed := ctrl.selfHealBackoffCooldownElapsed(app)
assert.True(t, elapsed)
})
t.Run("unsuccessful operation finised after cooldown", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.Phase = synccommon.OperationFailed
app.Status.OperationState.FinishedAt = &metav1.Time{Time: time.Now().Add(-cooldown)}
elapsed := ctrl.selfHealBackoffCooldownElapsed(app)
assert.False(t, elapsed)
})
t.Run("successful operation finised before cooldown", func(t *testing.T) {
app := app.DeepCopy()
app.Status.OperationState.FinishedAt = &metav1.Time{Time: time.Now()}
elapsed := ctrl.selfHealBackoffCooldownElapsed(app)
assert.False(t, elapsed)
})
}
func TestSyncTimeout(t *testing.T) {
testCases := []struct {
delta time.Duration

View file

@ -1,7 +1,6 @@
package cache
import (
"context"
"errors"
"net"
"net/url"
@ -40,36 +39,6 @@ func (n netError) Error() string { return string(n) }
func (n netError) Timeout() bool { return false }
func (n netError) Temporary() bool { return false }
func fixtures(data map[string]string, opts ...func(secret *corev1.Secret)) (*fake.Clientset, *argosettings.SettingsManager) {
cm := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: common.ArgoCDConfigMapName,
Namespace: "default",
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
Data: data,
}
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: common.ArgoCDSecretName,
Namespace: "default",
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
Data: map[string][]byte{},
}
for i := range opts {
opts[i](secret)
}
kubeClient := fake.NewClientset(cm, secret)
settingsManager := argosettings.NewSettingsManager(context.Background(), kubeClient, "default")
return kubeClient, settingsManager
}
func TestHandleModEvent_HasChanges(_ *testing.T) {
clusterCache := &mocks.ClusterCache{}
clusterCache.On("Invalidate", mock.Anything, mock.Anything).Return(nil).Once()
@ -776,72 +745,3 @@ func Test_GetVersionsInfo_error_redacted(t *testing.T) {
require.Error(t, err)
assert.NotContains(t, err.Error(), "password")
}
func TestLoadCacheSettings(t *testing.T) {
_, settingsManager := fixtures(map[string]string{
"application.instanceLabelKey": "testLabel",
"application.resourceTrackingMethod": string(appv1.TrackingMethodLabel),
"installationID": "123456789",
})
ch := liveStateCache{
settingsMgr: settingsManager,
}
label, err := settingsManager.GetAppInstanceLabelKey()
require.NoError(t, err)
trackingMethod, err := settingsManager.GetTrackingMethod()
require.NoError(t, err)
res, err := ch.loadCacheSettings()
require.NoError(t, err)
assert.Equal(t, label, res.appInstanceLabelKey)
assert.Equal(t, string(appv1.TrackingMethodLabel), trackingMethod)
assert.Equal(t, "123456789", res.installationID)
// By default the values won't be nil
assert.NotNil(t, res.resourceOverrides)
assert.NotNil(t, res.clusterSettings)
assert.True(t, res.ignoreResourceUpdatesEnabled)
}
func Test_ownerRefGV(t *testing.T) {
tests := []struct {
name string
input metav1.OwnerReference
expected schema.GroupVersion
}{
{
name: "valid API Version",
input: metav1.OwnerReference{
APIVersion: "apps/v1",
},
expected: schema.GroupVersion{
Group: "apps",
Version: "v1",
},
},
{
name: "custom defined version",
input: metav1.OwnerReference{
APIVersion: "custom-version",
},
expected: schema.GroupVersion{
Version: "custom-version",
Group: "",
},
},
{
name: "empty APIVersion",
input: metav1.OwnerReference{
APIVersion: "",
},
expected: schema.GroupVersion{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
res := ownerRefGV(tt.input)
assert.Equal(t, tt.expected, res)
})
}
}

View file

@ -51,7 +51,7 @@ func (ctrl *ApplicationController) executePostDeleteHooks(app *v1alpha1.Applicat
revisions = append(revisions, src.TargetRevision)
}
targets, _, _, err := ctrl.appStateManager.GetRepoObjs(app, app.Spec.GetSources(), appLabelKey, revisions, false, false, false, proj, true)
targets, _, _, err := ctrl.appStateManager.GetRepoObjs(app, app.Spec.GetSources(), appLabelKey, revisions, false, false, false, proj, false, true)
if err != nil {
return false, err
}

View file

@ -11,16 +11,12 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
commitclient "github.com/argoproj/argo-cd/v3/commitserver/apiclient"
"github.com/argoproj/argo-cd/v3/controller/hydrator/types"
appv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/reposerver/apiclient"
applog "github.com/argoproj/argo-cd/v3/util/app/log"
"github.com/argoproj/argo-cd/v3/util/git"
utilio "github.com/argoproj/argo-cd/v3/util/io"
)
// RepoGetter is an interface that defines methods for getting repository objects. It's a subset of the DB interface to
// avoid granting access to things we don't need.
type RepoGetter interface {
// GetRepository returns a repository by its URL and project name.
GetRepository(ctx context.Context, repoURL, project string) (*appv1.Repository, error)
@ -32,37 +28,16 @@ type RepoGetter interface {
type Dependencies interface {
// TODO: determine if we actually need to get the app, or if all the stuff we need the app for is done already on
// the app controller side.
// GetProcessableAppProj returns the AppProject for the given application. It should only return projects that are
// processable by the controller, meaning that the project is not deleted and the application is in a namespace
// permitted by the project.
GetProcessableAppProj(app *appv1.Application) (*appv1.AppProject, error)
// GetProcessableApps returns a list of applications that are processable by the controller.
GetProcessableApps() (*appv1.ApplicationList, error)
// GetRepoObjs returns the repository objects for the given application, source, and revision. It calls the repo-
// server and gets the manifests (objects).
GetRepoObjs(app *appv1.Application, source appv1.ApplicationSource, revision string, project *appv1.AppProject) ([]*unstructured.Unstructured, *apiclient.ManifestResponse, error)
// GetWriteCredentials returns the repository credentials for the given repository URL and project. These are to be
// sent to the commit server to write the hydrated manifests.
GetWriteCredentials(ctx context.Context, repoURL string, project string) (*appv1.Repository, error)
// RequestAppRefresh requests a refresh of the application with the given name and namespace. This is used to
// trigger a refresh after the application has been hydrated and a new commit has been pushed.
RequestAppRefresh(appName string, appNamespace string) error
// PersistAppHydratorStatus persists the application status for the source hydrator.
// TODO: only allow access to the hydrator status
PersistAppHydratorStatus(orig *appv1.Application, newStatus *appv1.SourceHydratorStatus)
// AddHydrationQueueItem adds a hydration queue item to the queue. This is used to trigger the hydration process for
// a group of applications which are hydrating to the same repo and target branch.
AddHydrationQueueItem(key types.HydrationQueueKey)
AddHydrationQueueItem(key HydrationQueueKey)
}
// Hydrator is the main struct that implements the hydration logic. It uses the Dependencies interface to access the
// app controller's functionality without directly depending on it.
type Hydrator struct {
dependencies Dependencies
statusRefreshTimeout time.Duration
@ -71,9 +46,6 @@ type Hydrator struct {
repoGetter RepoGetter
}
// NewHydrator creates a new Hydrator instance with the given dependencies, status refresh timeout, commit clientset,
// repo clientset, and repo getter. The refresh timeout determines how often the hydrator checks if an application
// needs to be hydrated.
func NewHydrator(dependencies Dependencies, statusRefreshTimeout time.Duration, commitClientset commitclient.Clientset, repoClientset apiclient.Clientset, repoGetter RepoGetter) *Hydrator {
return &Hydrator{
dependencies: dependencies,
@ -84,12 +56,6 @@ func NewHydrator(dependencies Dependencies, statusRefreshTimeout time.Duration,
}
}
// ProcessAppHydrateQueueItem processes an application hydrate queue item. It checks if the application needs hydration
// and if so, it updates the application's status to indicate that hydration is in progress. It then adds the
// hydration queue item to the queue for further processing.
//
// It's likely that multiple applications will trigger hydration at the same time. The hydration queue key is meant to
// dedupe these requests.
func (h *Hydrator) ProcessAppHydrateQueueItem(origApp *appv1.Application) {
origApp = origApp.DeepCopy()
app := origApp.DeepCopy()
@ -123,24 +89,27 @@ func (h *Hydrator) ProcessAppHydrateQueueItem(origApp *appv1.Application) {
logCtx.Debug("Successfully processed app hydrate queue item")
}
func getHydrationQueueKey(app *appv1.Application) types.HydrationQueueKey {
func getHydrationQueueKey(app *appv1.Application) HydrationQueueKey {
destinationBranch := app.Spec.SourceHydrator.SyncSource.TargetBranch
if app.Spec.SourceHydrator.HydrateTo != nil {
destinationBranch = app.Spec.SourceHydrator.HydrateTo.TargetBranch
}
key := types.HydrationQueueKey{
SourceRepoURL: git.NormalizeGitURLAllowInvalid(app.Spec.SourceHydrator.DrySource.RepoURL),
key := HydrationQueueKey{
SourceRepoURL: app.Spec.SourceHydrator.DrySource.RepoURL,
SourceTargetRevision: app.Spec.SourceHydrator.DrySource.TargetRevision,
DestinationBranch: destinationBranch,
}
return key
}
type HydrationQueueKey struct {
SourceRepoURL string
SourceTargetRevision string
DestinationBranch string
}
// uniqueHydrationDestination is used to detect duplicate hydrate destinations.
type uniqueHydrationDestination struct {
// sourceRepoURL must be normalized with git.NormalizeGitURL to ensure that two apps with different URL formats
// don't end up in two different hydration queue items. Failing to normalize would result in one hydrated commit for
// each unique URL.
//nolint:unused // used as part of a map key
sourceRepoURL string
//nolint:unused // used as part of a map key
@ -151,11 +120,7 @@ type uniqueHydrationDestination struct {
destinationPath string
}
// ProcessHydrationQueueItem processes a hydration queue item. It retrieves the relevant applications for the given
// hydration key, hydrates their latest commit, and updates their status accordingly. If the hydration fails, it marks
// the operation as failed and logs the error. If successful, it updates the operation to indicate that hydration was
// successful and requests a refresh of the applications to pick up the new hydrated commit.
func (h *Hydrator) ProcessHydrationQueueItem(hydrationKey types.HydrationQueueKey) (processNext bool) {
func (h *Hydrator) ProcessHydrationQueueItem(hydrationKey HydrationQueueKey) (processNext bool) {
logCtx := log.WithFields(log.Fields{
"sourceRepoURL": hydrationKey.SourceRepoURL,
"sourceTargetRevision": hydrationKey.SourceTargetRevision,
@ -212,7 +177,7 @@ func (h *Hydrator) ProcessHydrationQueueItem(hydrationKey types.HydrationQueueKe
return
}
func (h *Hydrator) hydrateAppsLatestCommit(logCtx *log.Entry, hydrationKey types.HydrationQueueKey) ([]*appv1.Application, string, string, error) {
func (h *Hydrator) hydrateAppsLatestCommit(logCtx *log.Entry, hydrationKey HydrationQueueKey) ([]*appv1.Application, string, string, error) {
relevantApps, err := h.getRelevantAppsForHydration(logCtx, hydrationKey)
if err != nil {
return nil, "", "", fmt.Errorf("failed to get relevant apps for hydration: %w", err)
@ -226,7 +191,7 @@ func (h *Hydrator) hydrateAppsLatestCommit(logCtx *log.Entry, hydrationKey types
return relevantApps, dryRevision, hydratedRevision, nil
}
func (h *Hydrator) getRelevantAppsForHydration(logCtx *log.Entry, hydrationKey types.HydrationQueueKey) ([]*appv1.Application, error) {
func (h *Hydrator) getRelevantAppsForHydration(logCtx *log.Entry, hydrationKey HydrationQueueKey) ([]*appv1.Application, error) {
// Get all apps
apps, err := h.dependencies.GetProcessableApps()
if err != nil {
@ -240,7 +205,7 @@ func (h *Hydrator) getRelevantAppsForHydration(logCtx *log.Entry, hydrationKey t
continue
}
if !git.SameURL(app.Spec.SourceHydrator.DrySource.RepoURL, hydrationKey.SourceRepoURL) ||
if app.Spec.SourceHydrator.DrySource.RepoURL != hydrationKey.SourceRepoURL ||
app.Spec.SourceHydrator.DrySource.TargetRevision != hydrationKey.SourceTargetRevision {
continue
}
@ -265,7 +230,7 @@ func (h *Hydrator) getRelevantAppsForHydration(logCtx *log.Entry, hydrationKey t
}
uniqueDestinationKey := uniqueHydrationDestination{
sourceRepoURL: git.NormalizeGitURLAllowInvalid(app.Spec.SourceHydrator.DrySource.RepoURL),
sourceRepoURL: app.Spec.SourceHydrator.DrySource.RepoURL,
sourceTargetRevision: app.Spec.SourceHydrator.DrySource.TargetRevision,
destinationBranch: destinationBranch,
destinationPath: app.Spec.SourceHydrator.SyncSource.Path,

View file

@ -4,14 +4,9 @@ import (
"testing"
"time"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/argoproj/argo-cd/v3/controller/hydrator/mocks"
"github.com/argoproj/argo-cd/v3/controller/hydrator/types"
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
)
@ -106,64 +101,3 @@ func Test_appNeedsHydration(t *testing.T) {
})
}
}
func Test_getRelevantAppsForHydration_RepoURLNormalization(t *testing.T) {
t.Parallel()
d := mocks.NewDependencies(t)
d.On("GetProcessableApps").Return(&v1alpha1.ApplicationList{
Items: []v1alpha1.Application{
{
Spec: v1alpha1.ApplicationSpec{
Project: "project",
SourceHydrator: &v1alpha1.SourceHydrator{
DrySource: v1alpha1.DrySource{
RepoURL: "https://example.com/repo.git",
TargetRevision: "main",
Path: "app1",
},
SyncSource: v1alpha1.SyncSource{
TargetBranch: "main",
Path: "app1",
},
},
},
},
{
Spec: v1alpha1.ApplicationSpec{
Project: "project",
SourceHydrator: &v1alpha1.SourceHydrator{
DrySource: v1alpha1.DrySource{
RepoURL: "https://example.com/repo",
TargetRevision: "main",
Path: "app2",
},
SyncSource: v1alpha1.SyncSource{
TargetBranch: "main",
Path: "app2",
},
},
},
},
},
}, nil)
d.On("GetProcessableAppProj", mock.Anything).Return(&v1alpha1.AppProject{
Spec: v1alpha1.AppProjectSpec{
SourceRepos: []string{"https://example.com/*"},
},
}, nil)
hydrator := &Hydrator{dependencies: d}
hydrationKey := types.HydrationQueueKey{
SourceRepoURL: "https://example.com/repo",
SourceTargetRevision: "main",
DestinationBranch: "main",
}
logCtx := log.WithField("test", "RepoURLNormalization")
relevantApps, err := hydrator.getRelevantAppsForHydration(logCtx, hydrationKey)
require.NoError(t, err)
assert.Len(t, relevantApps, 2, "Expected both apps to be considered relevant despite URL differences")
}

View file

@ -1,464 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package mocks
import (
"context"
"github.com/argoproj/argo-cd/v3/controller/hydrator/types"
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/reposerver/apiclient"
mock "github.com/stretchr/testify/mock"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
// NewDependencies creates a new instance of Dependencies. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewDependencies(t interface {
mock.TestingT
Cleanup(func())
}) *Dependencies {
mock := &Dependencies{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// Dependencies is an autogenerated mock type for the Dependencies type
type Dependencies struct {
mock.Mock
}
type Dependencies_Expecter struct {
mock *mock.Mock
}
func (_m *Dependencies) EXPECT() *Dependencies_Expecter {
return &Dependencies_Expecter{mock: &_m.Mock}
}
// AddHydrationQueueItem provides a mock function for the type Dependencies
func (_mock *Dependencies) AddHydrationQueueItem(key types.HydrationQueueKey) {
_mock.Called(key)
return
}
// Dependencies_AddHydrationQueueItem_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddHydrationQueueItem'
type Dependencies_AddHydrationQueueItem_Call struct {
*mock.Call
}
// AddHydrationQueueItem is a helper method to define mock.On call
// - key types.HydrationQueueKey
func (_e *Dependencies_Expecter) AddHydrationQueueItem(key interface{}) *Dependencies_AddHydrationQueueItem_Call {
return &Dependencies_AddHydrationQueueItem_Call{Call: _e.mock.On("AddHydrationQueueItem", key)}
}
func (_c *Dependencies_AddHydrationQueueItem_Call) Run(run func(key types.HydrationQueueKey)) *Dependencies_AddHydrationQueueItem_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 types.HydrationQueueKey
if args[0] != nil {
arg0 = args[0].(types.HydrationQueueKey)
}
run(
arg0,
)
})
return _c
}
func (_c *Dependencies_AddHydrationQueueItem_Call) Return() *Dependencies_AddHydrationQueueItem_Call {
_c.Call.Return()
return _c
}
func (_c *Dependencies_AddHydrationQueueItem_Call) RunAndReturn(run func(key types.HydrationQueueKey)) *Dependencies_AddHydrationQueueItem_Call {
_c.Run(run)
return _c
}
// GetProcessableAppProj provides a mock function for the type Dependencies
func (_mock *Dependencies) GetProcessableAppProj(app *v1alpha1.Application) (*v1alpha1.AppProject, error) {
ret := _mock.Called(app)
if len(ret) == 0 {
panic("no return value specified for GetProcessableAppProj")
}
var r0 *v1alpha1.AppProject
var r1 error
if returnFunc, ok := ret.Get(0).(func(*v1alpha1.Application) (*v1alpha1.AppProject, error)); ok {
return returnFunc(app)
}
if returnFunc, ok := ret.Get(0).(func(*v1alpha1.Application) *v1alpha1.AppProject); ok {
r0 = returnFunc(app)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*v1alpha1.AppProject)
}
}
if returnFunc, ok := ret.Get(1).(func(*v1alpha1.Application) error); ok {
r1 = returnFunc(app)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Dependencies_GetProcessableAppProj_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetProcessableAppProj'
type Dependencies_GetProcessableAppProj_Call struct {
*mock.Call
}
// GetProcessableAppProj is a helper method to define mock.On call
// - app *v1alpha1.Application
func (_e *Dependencies_Expecter) GetProcessableAppProj(app interface{}) *Dependencies_GetProcessableAppProj_Call {
return &Dependencies_GetProcessableAppProj_Call{Call: _e.mock.On("GetProcessableAppProj", app)}
}
func (_c *Dependencies_GetProcessableAppProj_Call) Run(run func(app *v1alpha1.Application)) *Dependencies_GetProcessableAppProj_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 *v1alpha1.Application
if args[0] != nil {
arg0 = args[0].(*v1alpha1.Application)
}
run(
arg0,
)
})
return _c
}
func (_c *Dependencies_GetProcessableAppProj_Call) Return(appProject *v1alpha1.AppProject, err error) *Dependencies_GetProcessableAppProj_Call {
_c.Call.Return(appProject, err)
return _c
}
func (_c *Dependencies_GetProcessableAppProj_Call) RunAndReturn(run func(app *v1alpha1.Application) (*v1alpha1.AppProject, error)) *Dependencies_GetProcessableAppProj_Call {
_c.Call.Return(run)
return _c
}
// GetProcessableApps provides a mock function for the type Dependencies
func (_mock *Dependencies) GetProcessableApps() (*v1alpha1.ApplicationList, error) {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for GetProcessableApps")
}
var r0 *v1alpha1.ApplicationList
var r1 error
if returnFunc, ok := ret.Get(0).(func() (*v1alpha1.ApplicationList, error)); ok {
return returnFunc()
}
if returnFunc, ok := ret.Get(0).(func() *v1alpha1.ApplicationList); ok {
r0 = returnFunc()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*v1alpha1.ApplicationList)
}
}
if returnFunc, ok := ret.Get(1).(func() error); ok {
r1 = returnFunc()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Dependencies_GetProcessableApps_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetProcessableApps'
type Dependencies_GetProcessableApps_Call struct {
*mock.Call
}
// GetProcessableApps is a helper method to define mock.On call
func (_e *Dependencies_Expecter) GetProcessableApps() *Dependencies_GetProcessableApps_Call {
return &Dependencies_GetProcessableApps_Call{Call: _e.mock.On("GetProcessableApps")}
}
func (_c *Dependencies_GetProcessableApps_Call) Run(run func()) *Dependencies_GetProcessableApps_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *Dependencies_GetProcessableApps_Call) Return(applicationList *v1alpha1.ApplicationList, err error) *Dependencies_GetProcessableApps_Call {
_c.Call.Return(applicationList, err)
return _c
}
func (_c *Dependencies_GetProcessableApps_Call) RunAndReturn(run func() (*v1alpha1.ApplicationList, error)) *Dependencies_GetProcessableApps_Call {
_c.Call.Return(run)
return _c
}
// GetRepoObjs provides a mock function for the type Dependencies
func (_mock *Dependencies) GetRepoObjs(app *v1alpha1.Application, source v1alpha1.ApplicationSource, revision string, project *v1alpha1.AppProject) ([]*unstructured.Unstructured, *apiclient.ManifestResponse, error) {
ret := _mock.Called(app, source, revision, project)
if len(ret) == 0 {
panic("no return value specified for GetRepoObjs")
}
var r0 []*unstructured.Unstructured
var r1 *apiclient.ManifestResponse
var r2 error
if returnFunc, ok := ret.Get(0).(func(*v1alpha1.Application, v1alpha1.ApplicationSource, string, *v1alpha1.AppProject) ([]*unstructured.Unstructured, *apiclient.ManifestResponse, error)); ok {
return returnFunc(app, source, revision, project)
}
if returnFunc, ok := ret.Get(0).(func(*v1alpha1.Application, v1alpha1.ApplicationSource, string, *v1alpha1.AppProject) []*unstructured.Unstructured); ok {
r0 = returnFunc(app, source, revision, project)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*unstructured.Unstructured)
}
}
if returnFunc, ok := ret.Get(1).(func(*v1alpha1.Application, v1alpha1.ApplicationSource, string, *v1alpha1.AppProject) *apiclient.ManifestResponse); ok {
r1 = returnFunc(app, source, revision, project)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*apiclient.ManifestResponse)
}
}
if returnFunc, ok := ret.Get(2).(func(*v1alpha1.Application, v1alpha1.ApplicationSource, string, *v1alpha1.AppProject) error); ok {
r2 = returnFunc(app, source, revision, project)
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}
// Dependencies_GetRepoObjs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRepoObjs'
type Dependencies_GetRepoObjs_Call struct {
*mock.Call
}
// GetRepoObjs is a helper method to define mock.On call
// - app *v1alpha1.Application
// - source v1alpha1.ApplicationSource
// - revision string
// - project *v1alpha1.AppProject
func (_e *Dependencies_Expecter) GetRepoObjs(app interface{}, source interface{}, revision interface{}, project interface{}) *Dependencies_GetRepoObjs_Call {
return &Dependencies_GetRepoObjs_Call{Call: _e.mock.On("GetRepoObjs", app, source, revision, project)}
}
func (_c *Dependencies_GetRepoObjs_Call) Run(run func(app *v1alpha1.Application, source v1alpha1.ApplicationSource, revision string, project *v1alpha1.AppProject)) *Dependencies_GetRepoObjs_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 *v1alpha1.Application
if args[0] != nil {
arg0 = args[0].(*v1alpha1.Application)
}
var arg1 v1alpha1.ApplicationSource
if args[1] != nil {
arg1 = args[1].(v1alpha1.ApplicationSource)
}
var arg2 string
if args[2] != nil {
arg2 = args[2].(string)
}
var arg3 *v1alpha1.AppProject
if args[3] != nil {
arg3 = args[3].(*v1alpha1.AppProject)
}
run(
arg0,
arg1,
arg2,
arg3,
)
})
return _c
}
func (_c *Dependencies_GetRepoObjs_Call) Return(unstructureds []*unstructured.Unstructured, manifestResponse *apiclient.ManifestResponse, err error) *Dependencies_GetRepoObjs_Call {
_c.Call.Return(unstructureds, manifestResponse, err)
return _c
}
func (_c *Dependencies_GetRepoObjs_Call) RunAndReturn(run func(app *v1alpha1.Application, source v1alpha1.ApplicationSource, revision string, project *v1alpha1.AppProject) ([]*unstructured.Unstructured, *apiclient.ManifestResponse, error)) *Dependencies_GetRepoObjs_Call {
_c.Call.Return(run)
return _c
}
// GetWriteCredentials provides a mock function for the type Dependencies
func (_mock *Dependencies) GetWriteCredentials(ctx context.Context, repoURL string, project string) (*v1alpha1.Repository, error) {
ret := _mock.Called(ctx, repoURL, project)
if len(ret) == 0 {
panic("no return value specified for GetWriteCredentials")
}
var r0 *v1alpha1.Repository
var r1 error
if returnFunc, ok := ret.Get(0).(func(context.Context, string, string) (*v1alpha1.Repository, error)); ok {
return returnFunc(ctx, repoURL, project)
}
if returnFunc, ok := ret.Get(0).(func(context.Context, string, string) *v1alpha1.Repository); ok {
r0 = returnFunc(ctx, repoURL, project)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*v1alpha1.Repository)
}
}
if returnFunc, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
r1 = returnFunc(ctx, repoURL, project)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Dependencies_GetWriteCredentials_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetWriteCredentials'
type Dependencies_GetWriteCredentials_Call struct {
*mock.Call
}
// GetWriteCredentials is a helper method to define mock.On call
// - ctx context.Context
// - repoURL string
// - project string
func (_e *Dependencies_Expecter) GetWriteCredentials(ctx interface{}, repoURL interface{}, project interface{}) *Dependencies_GetWriteCredentials_Call {
return &Dependencies_GetWriteCredentials_Call{Call: _e.mock.On("GetWriteCredentials", ctx, repoURL, project)}
}
func (_c *Dependencies_GetWriteCredentials_Call) Run(run func(ctx context.Context, repoURL string, project string)) *Dependencies_GetWriteCredentials_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context
if args[0] != nil {
arg0 = args[0].(context.Context)
}
var arg1 string
if args[1] != nil {
arg1 = args[1].(string)
}
var arg2 string
if args[2] != nil {
arg2 = args[2].(string)
}
run(
arg0,
arg1,
arg2,
)
})
return _c
}
func (_c *Dependencies_GetWriteCredentials_Call) Return(repository *v1alpha1.Repository, err error) *Dependencies_GetWriteCredentials_Call {
_c.Call.Return(repository, err)
return _c
}
func (_c *Dependencies_GetWriteCredentials_Call) RunAndReturn(run func(ctx context.Context, repoURL string, project string) (*v1alpha1.Repository, error)) *Dependencies_GetWriteCredentials_Call {
_c.Call.Return(run)
return _c
}
// PersistAppHydratorStatus provides a mock function for the type Dependencies
func (_mock *Dependencies) PersistAppHydratorStatus(orig *v1alpha1.Application, newStatus *v1alpha1.SourceHydratorStatus) {
_mock.Called(orig, newStatus)
return
}
// Dependencies_PersistAppHydratorStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PersistAppHydratorStatus'
type Dependencies_PersistAppHydratorStatus_Call struct {
*mock.Call
}
// PersistAppHydratorStatus is a helper method to define mock.On call
// - orig *v1alpha1.Application
// - newStatus *v1alpha1.SourceHydratorStatus
func (_e *Dependencies_Expecter) PersistAppHydratorStatus(orig interface{}, newStatus interface{}) *Dependencies_PersistAppHydratorStatus_Call {
return &Dependencies_PersistAppHydratorStatus_Call{Call: _e.mock.On("PersistAppHydratorStatus", orig, newStatus)}
}
func (_c *Dependencies_PersistAppHydratorStatus_Call) Run(run func(orig *v1alpha1.Application, newStatus *v1alpha1.SourceHydratorStatus)) *Dependencies_PersistAppHydratorStatus_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 *v1alpha1.Application
if args[0] != nil {
arg0 = args[0].(*v1alpha1.Application)
}
var arg1 *v1alpha1.SourceHydratorStatus
if args[1] != nil {
arg1 = args[1].(*v1alpha1.SourceHydratorStatus)
}
run(
arg0,
arg1,
)
})
return _c
}
func (_c *Dependencies_PersistAppHydratorStatus_Call) Return() *Dependencies_PersistAppHydratorStatus_Call {
_c.Call.Return()
return _c
}
func (_c *Dependencies_PersistAppHydratorStatus_Call) RunAndReturn(run func(orig *v1alpha1.Application, newStatus *v1alpha1.SourceHydratorStatus)) *Dependencies_PersistAppHydratorStatus_Call {
_c.Run(run)
return _c
}
// RequestAppRefresh provides a mock function for the type Dependencies
func (_mock *Dependencies) RequestAppRefresh(appName string, appNamespace string) error {
ret := _mock.Called(appName, appNamespace)
if len(ret) == 0 {
panic("no return value specified for RequestAppRefresh")
}
var r0 error
if returnFunc, ok := ret.Get(0).(func(string, string) error); ok {
r0 = returnFunc(appName, appNamespace)
} else {
r0 = ret.Error(0)
}
return r0
}
// Dependencies_RequestAppRefresh_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RequestAppRefresh'
type Dependencies_RequestAppRefresh_Call struct {
*mock.Call
}
// RequestAppRefresh is a helper method to define mock.On call
// - appName string
// - appNamespace string
func (_e *Dependencies_Expecter) RequestAppRefresh(appName interface{}, appNamespace interface{}) *Dependencies_RequestAppRefresh_Call {
return &Dependencies_RequestAppRefresh_Call{Call: _e.mock.On("RequestAppRefresh", appName, appNamespace)}
}
func (_c *Dependencies_RequestAppRefresh_Call) Run(run func(appName string, appNamespace string)) *Dependencies_RequestAppRefresh_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 string
if args[0] != nil {
arg0 = args[0].(string)
}
var arg1 string
if args[1] != nil {
arg1 = args[1].(string)
}
run(
arg0,
arg1,
)
})
return _c
}
func (_c *Dependencies_RequestAppRefresh_Call) Return(err error) *Dependencies_RequestAppRefresh_Call {
_c.Call.Return(err)
return _c
}
func (_c *Dependencies_RequestAppRefresh_Call) RunAndReturn(run func(appName string, appNamespace string) error) *Dependencies_RequestAppRefresh_Call {
_c.Call.Return(run)
return _c
}

View file

@ -1,11 +0,0 @@
package types
// HydrationQueueKey is used to uniquely identify a hydration operation in the queue. If several applications request
// hydration, but they have the same queue key, only one hydration operation will be performed.
type HydrationQueueKey struct {
// SourceRepoURL must be normalized with git.NormalizeGitURL to ensure that we don't double-queue a single hydration
// operation because two apps have different URL formats.
SourceRepoURL string
SourceTargetRevision string
DestinationBranch string
}

View file

@ -4,7 +4,7 @@ import (
"context"
"fmt"
"github.com/argoproj/argo-cd/v3/controller/hydrator/types"
"github.com/argoproj/argo-cd/v3/controller/hydrator"
appv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/reposerver/apiclient"
argoutil "github.com/argoproj/argo-cd/v3/util/argo"
@ -50,7 +50,7 @@ func (ctrl *ApplicationController) GetRepoObjs(origApp *appv1.Application, drySo
delete(app.Annotations, appv1.AnnotationKeyManifestGeneratePaths)
// FIXME: use cache and revision cache
objs, resp, _, err := ctrl.appStateManager.GetRepoObjs(app, drySources, appLabelKey, dryRevisions, true, true, false, project, false)
objs, resp, _, err := ctrl.appStateManager.GetRepoObjs(app, drySources, appLabelKey, dryRevisions, true, true, false, project, false, false)
if err != nil {
return nil, nil, fmt.Errorf("failed to get repo objects: %w", err)
}
@ -85,6 +85,6 @@ func (ctrl *ApplicationController) PersistAppHydratorStatus(orig *appv1.Applicat
ctrl.persistAppStatus(orig, status)
}
func (ctrl *ApplicationController) AddHydrationQueueItem(key types.HydrationQueueKey) {
func (ctrl *ApplicationController) AddHydrationQueueItem(key hydrator.HydrationQueueKey) {
ctrl.hydrationQueue.AddRateLimited(key)
}

View file

@ -27,6 +27,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/cache"
"github.com/argoproj/argo-cd/v3/common"
statecache "github.com/argoproj/argo-cd/v3/controller/cache"
@ -70,9 +71,9 @@ type managedResource struct {
// AppStateManager defines methods which allow to compare application spec and actual application state.
type AppStateManager interface {
CompareAppState(app *v1alpha1.Application, project *v1alpha1.AppProject, revisions []string, sources []v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localObjects []string, hasMultipleSources bool) (*comparisonResult, error)
SyncAppState(app *v1alpha1.Application, project *v1alpha1.AppProject, state *v1alpha1.OperationState)
GetRepoObjs(app *v1alpha1.Application, sources []v1alpha1.ApplicationSource, appLabelKey string, revisions []string, noCache, noRevisionCache, verifySignature bool, proj *v1alpha1.AppProject, sendRuntimeState bool) ([]*unstructured.Unstructured, []*apiclient.ManifestResponse, bool, error)
CompareAppState(app *v1alpha1.Application, project *v1alpha1.AppProject, revisions []string, sources []v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localObjects []string, hasMultipleSources bool, rollback bool) (*comparisonResult, error)
SyncAppState(app *v1alpha1.Application, state *v1alpha1.OperationState)
GetRepoObjs(app *v1alpha1.Application, sources []v1alpha1.ApplicationSource, appLabelKey string, revisions []string, noCache, noRevisionCache, verifySignature bool, proj *v1alpha1.AppProject, rollback, sendRuntimeState bool) ([]*unstructured.Unstructured, []*apiclient.ManifestResponse, bool, error)
}
// comparisonResult holds the state of an application after the reconciliation
@ -90,8 +91,7 @@ type comparisonResult struct {
timings map[string]time.Duration
diffResultList *diff.DiffResultList
hasPostDeleteHooks bool
// revisionsMayHaveChanges indicates if there are any possibilities that the revisions contain changes
revisionsMayHaveChanges bool
revisionUpdated bool
}
func (res *comparisonResult) GetSyncStatus() *v1alpha1.SyncStatus {
@ -108,6 +108,7 @@ type appStateManager struct {
db db.ArgoDB
settingsMgr *settings.SettingsManager
appclientset appclientset.Interface
projInformer cache.SharedIndexInformer
kubectl kubeutil.Kubectl
onKubectlRun kubeutil.OnKubectlRunFunc
repoClientset apiclient.Clientset
@ -127,7 +128,7 @@ type appStateManager struct {
// task to the repo-server. It returns the list of generated manifests as unstructured
// objects. It also returns the full response from all calls to the repo server as the
// second argument.
func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alpha1.ApplicationSource, appLabelKey string, revisions []string, noCache, noRevisionCache, verifySignature bool, proj *v1alpha1.AppProject, sendRuntimeState bool) ([]*unstructured.Unstructured, []*apiclient.ManifestResponse, bool, error) {
func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alpha1.ApplicationSource, appLabelKey string, revisions []string, noCache, noRevisionCache, verifySignature bool, proj *v1alpha1.AppProject, rollback, sendRuntimeState bool) ([]*unstructured.Unstructured, []*apiclient.ManifestResponse, bool, error) {
ts := stats.NewTimingStats()
helmRepos, err := m.db.ListHelmRepositories(context.Background())
if err != nil {
@ -218,12 +219,14 @@ func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alp
// Store the map of all sources having ref field into a map for applications with sources field
// If it's for a rollback process, the refSources[*].targetRevision fields are the desired
// revisions for the rollback
refSources, err := argo.GetRefSources(context.Background(), sources, app.Spec.Project, m.db.GetRepository, revisions)
refSources, err := argo.GetRefSources(context.Background(), sources, app.Spec.Project, m.db.GetRepository, revisions, rollback)
if err != nil {
return nil, nil, false, fmt.Errorf("failed to get ref sources: %w", err)
}
revisionsMayHaveChanges := false
revisionUpdated := false
atLeastOneRevisionIsNotPossibleToBeUpdated := false
keyManifestGenerateAnnotationVal, keyManifestGenerateAnnotationExists := app.Annotations[v1alpha1.AnnotationKeyManifestGeneratePaths]
@ -235,6 +238,10 @@ func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alp
if err != nil {
return nil, nil, false, fmt.Errorf("failed to get repo %q: %w", source.RepoURL, err)
}
kustomizeOptions, err := kustomizeSettings.GetOptions(source)
if err != nil {
return nil, nil, false, fmt.Errorf("failed to get Kustomize options for source %d of %d: %w", i+1, len(sources), err)
}
syncedRevision := app.Status.Sync.Revision
if app.Spec.HasMultipleSources() {
@ -276,7 +283,7 @@ func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alp
return nil, nil, false, fmt.Errorf("failed to compare revisions for source %d of %d: %w", i+1, len(sources), err)
}
if updateRevisionResult.Changes {
revisionsMayHaveChanges = true
revisionUpdated = true
}
// Generate manifests should use same revision as updateRevisionForPaths, because HEAD revision may be different between these two calls
@ -284,8 +291,8 @@ func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alp
revision = updateRevisionResult.Revision
}
} else {
// revisionsMayHaveChanges is set to true if at least one revision is not possible to be updated
revisionsMayHaveChanges = true
// revisionUpdated is set to true if at least one revision is not possible to be updated,
atLeastOneRevisionIsNotPossibleToBeUpdated = true
}
repos := permittedHelmRepos
@ -311,7 +318,7 @@ func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alp
AppName: app.InstanceName(m.namespace),
Namespace: appNamespace,
ApplicationSource: &source,
KustomizeOptions: kustomizeSettings,
KustomizeOptions: kustomizeOptions,
KubeVersion: serverVersion,
ApiVersions: apiVersions,
VerifySignature: verifySignature,
@ -346,7 +353,13 @@ func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alp
logCtx = logCtx.WithField("time_ms", time.Since(ts.StartTime).Milliseconds())
logCtx.Info("GetRepoObjs stats")
return targetObjs, manifestInfos, revisionsMayHaveChanges, nil
// If a revision in any of the sources cannot be updated,
// we should trigger self-healing whenever there are changes to the manifests.
if atLeastOneRevisionIsNotPossibleToBeUpdated {
revisionUpdated = true
}
return targetObjs, manifestInfos, revisionUpdated, nil
}
// ResolveGitRevision will resolve the given revision to a full commit SHA. Only works for git.
@ -529,37 +542,32 @@ func isManagedNamespace(ns *unstructured.Unstructured, app *v1alpha1.Application
// CompareAppState compares application git state to the live app state, using the specified
// revision and supplied source. If revision or overrides are empty, then compares against
// revision and overrides in the app spec.
func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1alpha1.AppProject, revisions []string, sources []v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localManifests []string, hasMultipleSources bool) (*comparisonResult, error) {
func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1alpha1.AppProject, revisions []string, sources []v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localManifests []string, hasMultipleSources bool, rollback bool) (*comparisonResult, error) {
ts := stats.NewTimingStats()
logCtx := log.WithFields(applog.GetAppLogFields(app))
// Build initial sync status
syncStatus := &v1alpha1.SyncStatus{
ComparedTo: v1alpha1.ComparedTo{
Destination: app.Spec.Destination,
IgnoreDifferences: app.Spec.IgnoreDifferences,
},
Status: v1alpha1.SyncStatusCodeUnknown,
}
if hasMultipleSources {
syncStatus.ComparedTo.Sources = sources
syncStatus.Revisions = revisions
} else {
if len(sources) > 0 {
syncStatus.ComparedTo.Source = sources[0]
} else {
logCtx.Warn("CompareAppState: sources should not be empty")
}
if len(revisions) > 0 {
syncStatus.Revision = revisions[0]
}
}
appLabelKey, resourceOverrides, resFilter, installationID, trackingMethod, err := m.getComparisonSettings()
ts.AddCheckpoint("settings_ms")
// return unknown comparison result if basic comparison settings cannot be loaded
if err != nil {
// return unknown comparison result if basic comparison settings cannot be loaded
return &comparisonResult{syncStatus: syncStatus, healthStatus: health.HealthStatusUnknown}, nil
if hasMultipleSources {
return &comparisonResult{
syncStatus: &v1alpha1.SyncStatus{
ComparedTo: app.Spec.BuildComparedToStatus(),
Status: v1alpha1.SyncStatusCodeUnknown,
Revisions: revisions,
},
healthStatus: health.HealthStatusUnknown,
}, nil
}
return &comparisonResult{
syncStatus: &v1alpha1.SyncStatus{
ComparedTo: app.Spec.BuildComparedToStatus(),
Status: v1alpha1.SyncStatusCodeUnknown,
Revision: revisions[0],
},
healthStatus: health.HealthStatusUnknown,
}, nil
}
// When signature keys are defined in the project spec, we need to verify the signature on the Git revision
@ -574,6 +582,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1
return nil, err
}
logCtx := log.WithFields(applog.GetAppLogFields(app))
logCtx.Infof("Comparing app state (cluster: %s, namespace: %s)", app.Spec.Destination.Server, app.Spec.Destination.Namespace)
var targetObjs []*unstructured.Unstructured
@ -582,7 +591,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1
var manifestInfos []*apiclient.ManifestResponse
targetNsExists := false
var revisionsMayHaveChanges bool
var revisionUpdated bool
if len(localManifests) == 0 {
// If the length of revisions is not same as the length of sources,
@ -594,7 +603,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1
}
}
targetObjs, manifestInfos, revisionsMayHaveChanges, err = m.GetRepoObjs(app, sources, appLabelKey, revisions, noCache, noRevisionCache, verifySignature, project, true)
targetObjs, manifestInfos, revisionUpdated, err = m.GetRepoObjs(app, sources, appLabelKey, revisions, noCache, noRevisionCache, verifySignature, project, rollback, true)
if err != nil {
targetObjs = make([]*unstructured.Unstructured, 0)
msg := "Failed to load target state: " + err.Error()
@ -942,14 +951,32 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1
} else if app.HasChangedManagedNamespaceMetadata() {
syncCode = v1alpha1.SyncStatusCodeOutOfSync
}
var revision string
syncStatus.Status = syncCode
// Update the initial revision to the resolved manifest SHA
if !hasMultipleSources && len(manifestRevisions) > 0 {
revision = manifestRevisions[0]
}
var syncStatus v1alpha1.SyncStatus
if hasMultipleSources {
syncStatus.Revisions = manifestRevisions
} else if len(manifestRevisions) > 0 {
syncStatus.Revision = manifestRevisions[0]
syncStatus = v1alpha1.SyncStatus{
ComparedTo: v1alpha1.ComparedTo{
Destination: app.Spec.Destination,
Sources: sources,
IgnoreDifferences: app.Spec.IgnoreDifferences,
},
Status: syncCode,
Revisions: manifestRevisions,
}
} else {
syncStatus = v1alpha1.SyncStatus{
ComparedTo: v1alpha1.ComparedTo{
Destination: app.Spec.Destination,
Source: app.Spec.GetSource(),
IgnoreDifferences: app.Spec.IgnoreDifferences,
},
Status: syncCode,
Revision: revision,
}
}
ts.AddCheckpoint("sync_ms")
@ -969,15 +996,15 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1
}
compRes := comparisonResult{
syncStatus: syncStatus,
healthStatus: healthStatus,
resources: resourceSummaries,
managedResources: managedResources,
reconciliationResult: reconciliation,
diffConfig: diffConfig,
diffResultList: diffResults,
hasPostDeleteHooks: hasPostDeleteHooks,
revisionsMayHaveChanges: revisionsMayHaveChanges,
syncStatus: &syncStatus,
healthStatus: healthStatus,
resources: resourceSummaries,
managedResources: managedResources,
reconciliationResult: reconciliation,
diffConfig: diffConfig,
diffResultList: diffResults,
hasPostDeleteHooks: hasPostDeleteHooks,
revisionUpdated: revisionUpdated,
}
if hasMultipleSources {
@ -1035,7 +1062,7 @@ func useDiffCache(noCache bool, manifestInfos []*apiclient.ManifestResponse, sou
return false
}
if !specEqualsCompareTo(app.Spec, sources, app.Status.Sync.ComparedTo) {
if !specEqualsCompareTo(app.Spec, app.Status.Sync.ComparedTo) {
log.WithField("useDiffCache", "false").Debug("specChanged")
return false
}
@ -1046,11 +1073,11 @@ func useDiffCache(noCache bool, manifestInfos []*apiclient.ManifestResponse, sou
// specEqualsCompareTo compares the application spec to the comparedTo status. It normalizes the destination to match
// the comparedTo destination before comparing. It does not mutate the original spec or comparedTo.
func specEqualsCompareTo(spec v1alpha1.ApplicationSpec, sources []v1alpha1.ApplicationSource, comparedTo v1alpha1.ComparedTo) bool {
func specEqualsCompareTo(spec v1alpha1.ApplicationSpec, comparedTo v1alpha1.ComparedTo) bool {
// Make a copy to be sure we don't mutate the original.
specCopy := spec.DeepCopy()
compareToSpec := specCopy.BuildComparedToStatus(sources)
return reflect.DeepEqual(comparedTo, compareToSpec)
currentSpec := specCopy.BuildComparedToStatus()
return reflect.DeepEqual(comparedTo, currentSpec)
}
func (m *appStateManager) persistRevisionHistory(
@ -1112,6 +1139,7 @@ func NewAppStateManager(
onKubectlRun kubeutil.OnKubectlRunFunc,
settingsMgr *settings.SettingsManager,
liveStateCache statecache.LiveStateCache,
projInformer cache.SharedIndexInformer,
metricsServer *metrics.MetricsServer,
cache *appstatecache.Cache,
statusRefreshTimeout time.Duration,
@ -1131,6 +1159,7 @@ func NewAppStateManager(
repoClientset: repoClientset,
namespace: namespace,
settingsMgr: settingsMgr,
projInformer: projInformer,
metricsServer: metricsServer,
statusRefreshTimeout: statusRefreshTimeout,
resourceTracking: resourceTracking,

View file

@ -25,7 +25,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/utils/ptr"
"github.com/argoproj/argo-cd/v3/common"
"github.com/argoproj/argo-cd/v3/controller/testdata"
@ -53,7 +52,7 @@ func TestCompareAppStateEmpty(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
@ -71,18 +70,18 @@ func TestCompareAppStateRepoError(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
assert.Nil(t, compRes)
require.EqualError(t, err, ErrCompareStateRepo.Error())
// expect to still get compare state error to as inside grace period
compRes, err = ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err = ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
assert.Nil(t, compRes)
require.EqualError(t, err, ErrCompareStateRepo.Error())
time.Sleep(10 * time.Second)
// expect to not get error as outside of grace period, but status should be unknown
compRes, err = ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err = ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
assert.NotNil(t, compRes)
require.NoError(t, err)
assert.Equal(t, v1alpha1.SyncStatusCodeUnknown, compRes.syncStatus.Status)
@ -117,7 +116,7 @@ func TestCompareAppStateNamespaceMetadataDiffers(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
@ -166,7 +165,7 @@ func TestCompareAppStateNamespaceMetadataDiffersToManifest(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
@ -224,7 +223,7 @@ func TestCompareAppStateNamespaceMetadata(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
@ -283,7 +282,7 @@ func TestCompareAppStateNamespaceMetadataIsTheSame(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
@ -311,7 +310,7 @@ func TestCompareAppStateMissing(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
@ -343,7 +342,7 @@ func TestCompareAppStateExtra(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.Equal(t, v1alpha1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status)
@ -374,7 +373,7 @@ func TestCompareAppStateHook(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.Equal(t, v1alpha1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@ -406,7 +405,7 @@ func TestCompareAppStateSkipHook(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.Equal(t, v1alpha1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@ -437,7 +436,7 @@ func TestCompareAppStateCompareOptionIgnoreExtraneous(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
@ -470,7 +469,7 @@ func TestCompareAppStateExtraHook(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
@ -499,7 +498,7 @@ func TestAppRevisionsSingleSource(t *testing.T) {
app := newFakeApp()
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, app.Spec.GetSources(), false, false, nil, app.Spec.HasMultipleSources())
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, app.Spec.GetSources(), false, false, nil, app.Spec.HasMultipleSources(), false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
@ -539,7 +538,7 @@ func TestAppRevisionsMultiSource(t *testing.T) {
app := newFakeMultiSourceApp()
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, app.Spec.GetSources(), false, false, nil, app.Spec.HasMultipleSources())
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, app.Spec.GetSources(), false, false, nil, app.Spec.HasMultipleSources(), false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
@ -588,7 +587,7 @@ func TestCompareAppStateDuplicatedNamespacedResources(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
@ -625,7 +624,7 @@ func TestCompareAppStateManagedNamespaceMetadataWithLiveNsDoesNotGetPruned(t *te
},
}
ctrl := newFakeController(&data, nil)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, []string{}, app.Spec.Sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, []string{}, app.Spec.Sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
@ -679,7 +678,7 @@ func TestCompareAppStateWithManifestGeneratePath(t *testing.T) {
ctrl := newFakeController(&data, nil)
revisions := make([]string, 0)
revisions = append(revisions, "abc123")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, app.Spec.GetSources(), false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, app.Spec.GetSources(), false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.Equal(t, v1alpha1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@ -715,7 +714,7 @@ func TestSetHealth(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.Equal(t, health.HealthStatusHealthy, compRes.healthStatus)
@ -751,7 +750,7 @@ func TestPreserveStatusTimestamp(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.Equal(t, health.HealthStatusHealthy, compRes.healthStatus)
@ -788,7 +787,7 @@ func TestSetHealthSelfReferencedApp(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.Equal(t, health.HealthStatusHealthy, compRes.healthStatus)
@ -863,7 +862,7 @@ func TestReturnUnknownComparisonStateOnSettingLoadError(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.Equal(t, health.HealthStatusUnknown, compRes.healthStatus)
@ -1012,7 +1011,7 @@ func TestSignedResponseNoSignatureRequired(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
@ -1039,7 +1038,7 @@ func TestSignedResponseNoSignatureRequired(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
@ -1071,7 +1070,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
@ -1098,7 +1097,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "abc123")
compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
@ -1125,7 +1124,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "abc123")
compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
@ -1152,7 +1151,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "abc123")
compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
@ -1182,7 +1181,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "abc123")
compRes, err := ctrl.appStateManager.CompareAppState(app, &testProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &testProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
@ -1212,7 +1211,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "abc123")
compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, localManifests, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, localManifests, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
@ -1242,7 +1241,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "abc123")
compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
@ -1272,7 +1271,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "abc123")
compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, localManifests, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, localManifests, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
@ -1482,6 +1481,7 @@ func TestIsLiveResourceManaged(t *testing.T) {
func TestUseDiffCache(t *testing.T) {
t.Parallel()
type fixture struct {
testName string
noCache bool
@ -1493,6 +1493,7 @@ func TestUseDiffCache(t *testing.T) {
expectedUseCache bool
serverSideDiff bool
}
manifestInfos := func(revision string) []*apiclient.ManifestResponse {
return []*apiclient.ManifestResponse{
{
@ -1508,15 +1509,14 @@ func TestUseDiffCache(t *testing.T) {
},
}
}
source := func() v1alpha1.ApplicationSource {
return v1alpha1.ApplicationSource{
RepoURL: "https://some-repo.com",
Path: "argocd/httpbin",
TargetRevision: "HEAD",
}
}
sources := func() []v1alpha1.ApplicationSource {
return []v1alpha1.ApplicationSource{source()}
return []v1alpha1.ApplicationSource{
{
RepoURL: "https://some-repo.com",
Path: "argocd/httpbin",
TargetRevision: "HEAD",
},
}
}
app := func(namespace string, revision string, refresh bool, a *v1alpha1.Application) *v1alpha1.Application {
@ -1526,7 +1526,11 @@ func TestUseDiffCache(t *testing.T) {
Namespace: namespace,
},
Spec: v1alpha1.ApplicationSpec{
Source: ptr.To(source()),
Source: &v1alpha1.ApplicationSource{
RepoURL: "https://some-repo.com",
Path: "argocd/httpbin",
TargetRevision: "HEAD",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "httpbin",
@ -1544,7 +1548,11 @@ func TestUseDiffCache(t *testing.T) {
Sync: v1alpha1.SyncStatus{
Status: v1alpha1.SyncStatusCodeSynced,
ComparedTo: v1alpha1.ComparedTo{
Source: source(),
Source: v1alpha1.ApplicationSource{
RepoURL: "https://some-repo.com",
Path: "argocd/httpbin",
TargetRevision: "HEAD",
},
Destination: v1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "httpbin",
@ -1569,6 +1577,7 @@ func TestUseDiffCache(t *testing.T) {
}
return app
}
cases := []fixture{
{
testName: "will use diff cache",
@ -1585,7 +1594,7 @@ func TestUseDiffCache(t *testing.T) {
testName: "will use diff cache with sync policy",
noCache: false,
manifestInfos: manifestInfos("rev1"),
sources: []v1alpha1.ApplicationSource{test.YamlToApplication(testdata.DiffCacheYaml).Status.Sync.ComparedTo.Source},
sources: sources(),
app: test.YamlToApplication(testdata.DiffCacheYaml),
manifestRevisions: []string{"rev1"},
statusRefreshTimeout: time.Hour * 24,
@ -1595,15 +1604,8 @@ func TestUseDiffCache(t *testing.T) {
{
testName: "will use diff cache for multisource",
noCache: false,
manifestInfos: append(manifestInfos("rev1"), manifestInfos("rev2")...),
sources: v1alpha1.ApplicationSources{
{
RepoURL: "multisource repo1",
},
{
RepoURL: "multisource repo2",
},
},
manifestInfos: manifestInfos("rev1"),
sources: sources(),
app: app("httpbin", "", false, &v1alpha1.Application{
Spec: v1alpha1.ApplicationSpec{
Source: nil,
@ -1741,13 +1743,16 @@ func TestUseDiffCache(t *testing.T) {
}
for _, tc := range cases {
tc := tc
t.Run(tc.testName, func(t *testing.T) {
// Given
t.Parallel()
logger, _ := logrustest.NewNullLogger()
log := logrus.NewEntry(logger)
// When
useDiffCache := useDiffCache(tc.noCache, tc.manifestInfos, tc.sources, tc.app, tc.manifestRevisions, tc.statusRefreshTimeout, tc.serverSideDiff, log)
// Then
assert.Equal(t, tc.expectedUseCache, useDiffCache)
})
@ -1770,11 +1775,11 @@ func TestCompareAppStateDefaultRevisionUpdated(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.True(t, compRes.revisionsMayHaveChanges)
assert.True(t, compRes.revisionUpdated)
}
func TestCompareAppStateRevisionUpdatedWithHelmSource(t *testing.T) {
@ -1793,11 +1798,11 @@ func TestCompareAppStateRevisionUpdatedWithHelmSource(t *testing.T) {
sources = append(sources, app.Spec.GetSource())
revisions := make([]string, 0)
revisions = append(revisions, "")
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false, false)
require.NoError(t, err)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.True(t, compRes.revisionsMayHaveChanges)
assert.True(t, compRes.revisionUpdated)
}
func Test_normalizeClusterScopeTracking(t *testing.T) {
@ -1845,6 +1850,6 @@ func TestCompareAppState_DoesNotCallUpdateRevisionForPaths_ForOCI(t *testing.T)
sources := make([]v1alpha1.ApplicationSource, 0)
sources = append(sources, source)
_, _, _, err := ctrl.appStateManager.GetRepoObjs(app, sources, "abc123", []string{"123456"}, false, false, false, &defaultProj, false)
_, _, _, err := ctrl.appStateManager.GetRepoObjs(app, sources, "abc123", []string{"123456"}, false, false, false, &defaultProj, false, false)
require.NoError(t, err)
}

View file

@ -30,6 +30,7 @@ import (
"github.com/argoproj/argo-cd/v3/controller/metrics"
"github.com/argoproj/argo-cd/v3/controller/syncid"
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
listersv1alpha1 "github.com/argoproj/argo-cd/v3/pkg/client/listers/application/v1alpha1"
applog "github.com/argoproj/argo-cd/v3/util/app/log"
"github.com/argoproj/argo-cd/v3/util/argo"
"github.com/argoproj/argo-cd/v3/util/argo/diff"
@ -86,95 +87,123 @@ func (m *appStateManager) getServerSideDiffDryRunApplier(cluster *v1alpha1.Clust
return ops, cleanup, nil
}
func NewOperationState(operation v1alpha1.Operation) *v1alpha1.OperationState {
return &v1alpha1.OperationState{
Phase: common.OperationRunning,
Operation: operation,
StartedAt: metav1.Now(),
}
}
func newSyncOperationResult(app *v1alpha1.Application, op v1alpha1.SyncOperation) *v1alpha1.SyncOperationResult {
syncRes := &v1alpha1.SyncOperationResult{}
if len(op.Sources) > 0 || op.Source != nil {
// specific source specified in the SyncOperation
if op.Source != nil {
syncRes.Source = *op.Source
}
syncRes.Sources = op.Sources
} else {
// normal sync case, get sources from the spec
syncRes.Sources = app.Spec.Sources
syncRes.Source = app.Spec.GetSource()
}
func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha1.OperationState) {
// Sync requests might be requested with ambiguous revisions (e.g. master, HEAD, v1.2.3).
// This can change meaning when resuming operations (e.g a hook sync). After calculating a
// concrete git commit SHA, the revision of the SyncOperationResult will be updated with the SHA
syncRes.Revision = op.Revision
syncRes.Revisions = op.Revisions
return syncRes
}
// concrete git commit SHA, the SHA is remembered in the status.operationState.syncResult field.
// This ensures that when resuming an operation, we sync to the same revision that we initially
// started with.
func (m *appStateManager) SyncAppState(app *v1alpha1.Application, project *v1alpha1.AppProject, state *v1alpha1.OperationState) {
syncId, err := syncid.Generate()
if err != nil {
state.Phase = common.OperationError
state.Message = fmt.Sprintf("Failed to generate sync ID: %v", err)
return
}
logEntry := log.WithFields(applog.GetAppLogFields(app)).WithField("syncId", syncId)
var revision string
var syncOp v1alpha1.SyncOperation
var syncRes *v1alpha1.SyncOperationResult
var source v1alpha1.ApplicationSource
var sources []v1alpha1.ApplicationSource
revisions := make([]string, 0)
if state.Operation.Sync == nil {
state.Phase = common.OperationError
state.Phase = common.OperationFailed
state.Message = "Invalid operation request: no operation specified"
return
}
syncOp = *state.Operation.Sync
syncOp := *state.Operation.Sync
if state.SyncResult == nil {
state.SyncResult = newSyncOperationResult(app, syncOp)
}
if isBlocked, err := syncWindowPreventsSync(app, project); isBlocked {
// If the operation is currently running, simply let the user know the sync is blocked by a current sync window
if state.Phase == common.OperationRunning {
state.Message = "Sync operation blocked by sync window"
if err != nil {
state.Message = fmt.Sprintf("%s: %v", state.Message, err)
}
isMultiSourceRevision := app.Spec.HasMultipleSources()
rollback := len(syncOp.Sources) > 0 || syncOp.Source != nil
if rollback {
// rollback case
if len(state.Operation.Sync.Sources) > 0 {
sources = state.Operation.Sync.Sources
isMultiSourceRevision = true
} else {
source = *state.Operation.Sync.Source
sources = make([]v1alpha1.ApplicationSource, 0)
isMultiSourceRevision = false
}
} else {
// normal sync case (where source is taken from app.spec.sources)
if app.Spec.HasMultipleSources() {
sources = app.Spec.Sources
} else {
// normal sync case (where source is taken from app.spec.source)
source = app.Spec.GetSource()
sources = make([]v1alpha1.ApplicationSource, 0)
}
return
}
revisions := state.SyncResult.Revisions
sources := state.SyncResult.Sources
isMultiSourceSync := len(sources) > 0
if !isMultiSourceSync {
sources = []v1alpha1.ApplicationSource{state.SyncResult.Source}
revisions = []string{state.SyncResult.Revision}
if state.SyncResult != nil {
syncRes = state.SyncResult
revision = state.SyncResult.Revision
revisions = append(revisions, state.SyncResult.Revisions...)
} else {
syncRes = &v1alpha1.SyncOperationResult{}
// status.operationState.syncResult.source. must be set properly since auto-sync relies
// on this information to decide if it should sync (if source is different than the last
// sync attempt)
if isMultiSourceRevision {
syncRes.Sources = sources
} else {
syncRes.Source = source
}
state.SyncResult = syncRes
}
// if we get here, it means we did not remember a commit SHA which we should be syncing to.
// This typically indicates we are just about to begin a brand new sync/rollback operation.
// Take the value in the requested operation. We will resolve this to a SHA later.
if isMultiSourceRevision {
if len(revisions) != len(sources) {
revisions = syncOp.Revisions
}
} else {
if revision == "" {
revision = syncOp.Revision
}
}
proj, err := argo.GetAppProject(context.TODO(), app, listersv1alpha1.NewAppProjectLister(m.projInformer.GetIndexer()), m.namespace, m.settingsMgr, m.db)
if err != nil {
state.Phase = common.OperationError
state.Message = fmt.Sprintf("Failed to load application project: %v", err)
return
} else {
isBlocked, err := syncWindowPreventsSync(app, proj)
if isBlocked {
// If the operation is currently running, simply let the user know the sync is blocked by a current sync window
if state.Phase == common.OperationRunning {
state.Message = "Sync operation blocked by sync window"
if err != nil {
state.Message = fmt.Sprintf("%s: %v", state.Message, err)
}
}
return
}
}
if !isMultiSourceRevision {
sources = []v1alpha1.ApplicationSource{source}
revisions = []string{revision}
}
// ignore error if CompareStateRepoError, this shouldn't happen as noRevisionCache is true
compareResult, err := m.CompareAppState(app, project, revisions, sources, false, true, syncOp.Manifests, isMultiSourceSync)
compareResult, err := m.CompareAppState(app, proj, revisions, sources, false, true, syncOp.Manifests, isMultiSourceRevision, rollback)
if err != nil && !stderrors.Is(err, ErrCompareStateRepo) {
state.Phase = common.OperationError
state.Message = err.Error()
return
}
// We are now guaranteed to have a concrete commit SHA. Save this in the sync result revision so that we remember
// We now have a concrete commit SHA. Save this in the sync result revision so that we remember
// what we should be syncing to when resuming operations.
state.SyncResult.Revision = compareResult.syncStatus.Revision
state.SyncResult.Revisions = compareResult.syncStatus.Revisions
// validates if it should fail the sync on that revision if it finds shared resources
syncRes.Revision = compareResult.syncStatus.Revision
syncRes.Revisions = compareResult.syncStatus.Revisions
// validates if it should fail the sync if it finds shared resources
hasSharedResource, sharedResourceMessage := hasSharedResourceCondition(app)
if syncOp.SyncOptions.HasOption("FailOnSharedResource=true") && hasSharedResource {
if syncOp.SyncOptions.HasOption("FailOnSharedResource=true") &&
hasSharedResource {
state.Phase = common.OperationFailed
state.Message = "Shared resource found: " + sharedResourceMessage
state.Message = "Shared resource found: %s" + sharedResourceMessage
return
}
@ -217,8 +246,15 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, project *v1alp
return
}
initialResourcesRes := make([]common.ResourceSyncResult, len(state.SyncResult.Resources))
for i, res := range state.SyncResult.Resources {
syncId, err := syncid.Generate()
if err != nil {
state.Phase = common.OperationError
state.Message = fmt.Sprintf("Failed to generate sync ID: %v", err)
return
}
logEntry := log.WithFields(applog.GetAppLogFields(app)).WithField("syncId", syncId)
initialResourcesRes := make([]common.ResourceSyncResult, len(syncRes.Resources))
for i, res := range syncRes.Resources {
key := kube.ResourceKey{Group: res.Group, Kind: res.Kind, Namespace: res.Namespace, Name: res.Name}
initialResourcesRes[i] = common.ResourceSyncResult{
ResourceKey: key,
@ -288,7 +324,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, project *v1alp
return
}
if impersonationEnabled {
serviceAccountToImpersonate, err := deriveServiceAccountToImpersonate(project, app, destCluster)
serviceAccountToImpersonate, err := deriveServiceAccountToImpersonate(proj, app, destCluster)
if err != nil {
state.Phase = common.OperationError
state.Message = fmt.Sprintf("failed to find a matching service account to impersonate: %v", err)
@ -308,11 +344,11 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, project *v1alp
sync.WithLogr(logutils.NewLogrusLogger(logEntry)),
sync.WithHealthOverride(lua.ResourceHealthOverrides(resourceOverrides)),
sync.WithPermissionValidator(func(un *unstructured.Unstructured, res *metav1.APIResource) error {
if !project.IsGroupKindPermitted(un.GroupVersionKind().GroupKind(), res.Namespaced) {
return fmt.Errorf("resource %s:%s is not permitted in project %s", un.GroupVersionKind().Group, un.GroupVersionKind().Kind, project.Name)
if !proj.IsGroupKindPermitted(un.GroupVersionKind().GroupKind(), res.Namespaced) {
return fmt.Errorf("resource %s:%s is not permitted in project %s", un.GroupVersionKind().Group, un.GroupVersionKind().Kind, proj.Name)
}
if res.Namespaced {
permitted, err := project.IsDestinationPermitted(destCluster, un.GetNamespace(), func(project string) ([]*v1alpha1.Cluster, error) {
permitted, err := proj.IsDestinationPermitted(destCluster, un.GetNamespace(), func(project string) ([]*v1alpha1.Cluster, error) {
return m.db.GetProjectClusters(context.TODO(), project)
})
if err != nil {
@ -320,7 +356,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, project *v1alp
}
if !permitted {
return fmt.Errorf("namespace %v is not permitted in project '%s'", un.GetNamespace(), project.Name)
return fmt.Errorf("namespace %v is not permitted in project '%s'", un.GetNamespace(), proj.Name)
}
}
return nil
@ -422,7 +458,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, project *v1alp
logEntry.WithField("duration", time.Since(start)).Info("sync/terminate complete")
if !syncOp.DryRun && len(syncOp.Resources) == 0 && state.Phase.Successful() {
err := m.persistRevisionHistory(app, compareResult.syncStatus.Revision, compareResult.syncStatus.ComparedTo.Source, compareResult.syncStatus.Revisions, compareResult.syncStatus.ComparedTo.Sources, isMultiSourceSync, state.StartedAt, state.Operation.InitiatedBy)
err := m.persistRevisionHistory(app, compareResult.syncStatus.Revision, source, compareResult.syncStatus.Revisions, compareResult.syncStatus.ComparedTo.Sources, isMultiSourceRevision, state.StartedAt, state.Operation.InitiatedBy)
if err != nil {
state.Phase = common.OperationError
state.Message = fmt.Sprintf("failed to record sync to history: %v", err)

View file

@ -50,7 +50,7 @@ func TestPersistRevisionHistory(t *testing.T) {
opState := &v1alpha1.OperationState{Operation: v1alpha1.Operation{
Sync: &v1alpha1.SyncOperation{},
}}
ctrl.appStateManager.SyncAppState(app, defaultProject, opState)
ctrl.appStateManager.SyncAppState(app, opState)
// Ensure we record spec.source into sync result
assert.Equal(t, app.Spec.GetSource(), opState.SyncResult.Source)
@ -96,7 +96,7 @@ func TestPersistManagedNamespaceMetadataState(t *testing.T) {
opState := &v1alpha1.OperationState{Operation: v1alpha1.Operation{
Sync: &v1alpha1.SyncOperation{},
}}
ctrl.appStateManager.SyncAppState(app, defaultProject, opState)
ctrl.appStateManager.SyncAppState(app, opState)
// Ensure we record spec.syncPolicy.managedNamespaceMetadata into sync result
assert.Equal(t, app.Spec.SyncPolicy.ManagedNamespaceMetadata, opState.SyncResult.ManagedNamespaceMetadata)
}
@ -139,7 +139,7 @@ func TestPersistRevisionHistoryRollback(t *testing.T) {
Source: &source,
},
}}
ctrl.appStateManager.SyncAppState(app, defaultProject, opState)
ctrl.appStateManager.SyncAppState(app, opState)
// Ensure we record opState's source into sync result
assert.Equal(t, source, opState.SyncResult.Source)
@ -182,7 +182,7 @@ func TestSyncComparisonError(t *testing.T) {
Sync: &v1alpha1.SyncOperation{},
}}
t.Setenv("ARGOCD_GPG_ENABLED", "true")
ctrl.appStateManager.SyncAppState(app, defaultProject, opState)
ctrl.appStateManager.SyncAppState(app, opState)
conditions := app.Status.GetConditions(map[v1alpha1.ApplicationConditionType]bool{v1alpha1.ApplicationConditionComparisonError: true})
assert.NotEmpty(t, conditions)
@ -194,7 +194,6 @@ func TestAppStateManager_SyncAppState(t *testing.T) {
type fixture struct {
application *v1alpha1.Application
project *v1alpha1.AppProject
controller *ApplicationController
}
@ -236,7 +235,6 @@ func TestAppStateManager_SyncAppState(t *testing.T) {
return &fixture{
application: app,
project: project,
controller: ctrl,
}
}
@ -271,7 +269,7 @@ func TestAppStateManager_SyncAppState(t *testing.T) {
}}
// when
f.controller.appStateManager.SyncAppState(f.application, f.project, opState)
f.controller.appStateManager.SyncAppState(f.application, opState)
// then
assert.Equal(t, synccommon.OperationFailed, opState.Phase)
@ -284,7 +282,6 @@ func TestSyncWindowDeniesSync(t *testing.T) {
type fixture struct {
application *v1alpha1.Application
project *v1alpha1.AppProject
controller *ApplicationController
}
@ -323,7 +320,6 @@ func TestSyncWindowDeniesSync(t *testing.T) {
return &fixture{
application: app,
project: project,
controller: ctrl,
}
}
@ -343,7 +339,7 @@ func TestSyncWindowDeniesSync(t *testing.T) {
Phase: synccommon.OperationRunning,
}
// when
f.controller.appStateManager.SyncAppState(f.application, f.project, opState)
f.controller.appStateManager.SyncAppState(f.application, opState)
// then
assert.Equal(t, synccommon.OperationRunning, opState.Phase)
@ -1366,7 +1362,6 @@ func TestDeriveServiceAccountMatchingServers(t *testing.T) {
func TestSyncWithImpersonate(t *testing.T) {
type fixture struct {
application *v1alpha1.Application
project *v1alpha1.AppProject
controller *ApplicationController
}
@ -1416,7 +1411,6 @@ func TestSyncWithImpersonate(t *testing.T) {
ctrl := newFakeController(&data, nil)
return &fixture{
application: app,
project: project,
controller: ctrl,
}
}
@ -1435,7 +1429,7 @@ func TestSyncWithImpersonate(t *testing.T) {
Phase: synccommon.OperationRunning,
}
// when
f.controller.appStateManager.SyncAppState(f.application, f.project, opState)
f.controller.appStateManager.SyncAppState(f.application, opState)
// then, app sync should fail with expected error message in operation state
assert.Equal(t, synccommon.OperationError, opState.Phase)
@ -1456,7 +1450,7 @@ func TestSyncWithImpersonate(t *testing.T) {
Phase: synccommon.OperationRunning,
}
// when
f.controller.appStateManager.SyncAppState(f.application, f.project, opState)
f.controller.appStateManager.SyncAppState(f.application, opState)
// then app sync should fail with expected error message in operation state
assert.Equal(t, synccommon.OperationError, opState.Phase)
@ -1477,7 +1471,7 @@ func TestSyncWithImpersonate(t *testing.T) {
Phase: synccommon.OperationRunning,
}
// when
f.controller.appStateManager.SyncAppState(f.application, f.project, opState)
f.controller.appStateManager.SyncAppState(f.application, opState)
// then app sync should not fail
assert.Equal(t, synccommon.OperationSucceeded, opState.Phase)
@ -1498,7 +1492,7 @@ func TestSyncWithImpersonate(t *testing.T) {
Phase: synccommon.OperationRunning,
}
// when
f.controller.appStateManager.SyncAppState(f.application, f.project, opState)
f.controller.appStateManager.SyncAppState(f.application, opState)
// then application sync should pass using the control plane service account
assert.Equal(t, synccommon.OperationSucceeded, opState.Phase)
@ -1523,7 +1517,7 @@ func TestSyncWithImpersonate(t *testing.T) {
f.application.Spec.Destination.Name = "minikube"
// when
f.controller.appStateManager.SyncAppState(f.application, f.project, opState)
f.controller.appStateManager.SyncAppState(f.application, opState)
// then app sync should not fail
assert.Equal(t, synccommon.OperationSucceeded, opState.Phase)
@ -1536,7 +1530,6 @@ func TestClientSideApplyMigration(t *testing.T) {
type fixture struct {
application *v1alpha1.Application
project *v1alpha1.AppProject
controller *ApplicationController
}
@ -1577,7 +1570,6 @@ func TestClientSideApplyMigration(t *testing.T) {
return &fixture{
application: app,
project: project,
controller: ctrl,
}
}
@ -1593,7 +1585,7 @@ func TestClientSideApplyMigration(t *testing.T) {
Source: &v1alpha1.ApplicationSource{},
},
}}
f.controller.appStateManager.SyncAppState(f.application, f.project, opState)
f.controller.appStateManager.SyncAppState(f.application, opState)
// then
assert.Equal(t, synccommon.OperationSucceeded, opState.Phase)
@ -1611,7 +1603,7 @@ func TestClientSideApplyMigration(t *testing.T) {
Source: &v1alpha1.ApplicationSource{},
},
}}
f.controller.appStateManager.SyncAppState(f.application, f.project, opState)
f.controller.appStateManager.SyncAppState(f.application, opState)
// then
assert.Equal(t, synccommon.OperationSucceeded, opState.Phase)
@ -1629,7 +1621,7 @@ func TestClientSideApplyMigration(t *testing.T) {
Source: &v1alpha1.ApplicationSource{},
},
}}
f.controller.appStateManager.SyncAppState(f.application, f.project, opState)
f.controller.appStateManager.SyncAppState(f.application, opState)
// then
assert.Equal(t, synccommon.OperationSucceeded, opState.Phase)

View file

@ -133,15 +133,6 @@ spec:
- pullRequest:
# When using a Pull Request generator, the ApplicationSet controller polls every `requeueAfterSeconds` interval (defaulting to every 30 minutes) to detect changes.
requeueAfterSeconds: 1800
# When set to true, the ApplicationSet controller will continue to generate Applications even if the repository is not found, and will not enter a failed state.
# One example use case is when a pull request generator is combined with a Git generator in a matrix generator.
# NOTE, that if a repository exists but is inaccessible due to
# access rights, SCM providers usually return a "404 Not Found" error
# instead of a "403 Permission Denied" error. Consequently, using this
# option may lead to the deletion of Argo CD applications if the SCM
# user associated with the token loses access to the repository.
continueOnRepoNotFoundError: false
# See below for provider specific options.
# Specify the repository from which to fetch the GitHub Pull requests.
github:
@ -334,4 +325,4 @@ spec:
jqPathExpressions:
- .spec.source.helm.values

View file

@ -283,7 +283,7 @@ data:
# Comma delimited list of labels to preserve in generated applications
applicationsetcontroller.global.preserved.labels: "acme.com/label1,acme.com/label2"
# Enable GitHub API metrics for generators that use GitHub API
applicationsetcontroller.enable.github.api.metrics: "false"
applicationsetcontroller.enable.github.api.metrics: "true"
## Argo CD Notifications Controller Properties
# Set the logging level. One of: debug|info|warn|error (default "info")

View file

@ -1007,15 +1007,15 @@ Azure cluster secret example using argocd-k8s-auth and [kubelogin](https://githu
|Variable Name|Description|
|-------------|-----------|
|AAD_LOGIN_METHOD|One of devicecode, spn, ropc, msi, azurecli, or workloadidentity|
|AZURE_CLIENT_CERTIFICATE_PATH|Path to AAD client cert in pfx. Used in spn login and WorkloadIdentityLogin flow|
|AZURE_CLIENT_CERTIFICATE_PASSWORD|Password for the client cert in pfx. Used in spn login|
|AZURE_CLIENT_ID|AAD client application ID|
|AZURE_CLIENT_SECRET|AAD client application secret|
|AAD_SERVICE_PRINCIPAL_CLIENT_CERTIFICATE|AAD client cert in pfx. Used in spn login|
|AAD_SERVICE_PRINCIPAL_CLIENT_ID|AAD client application ID|
|AAD_SERVICE_PRINCIPAL_CLIENT_SECRET|AAD client application secret|
|AAD_USER_PRINCIPAL_NAME|Used in the ropc flow|
|AAD_USER_PRINCIPAL_PASSWORD|Used in the ropc flow|
|AZURE_TENANT_ID|The AAD tenant ID.|
|AZURE_AUTHORITY_HOST|Used in the WorkloadIdentityLogin flow|
|AZURE_FEDERATED_TOKEN_FILE|Used in the WorkloadIdentityLogin flow|
|AZURE_CLIENT_ID|Used in the WorkloadIdentityLogin flow|
In addition to the environment variables above, argocd-k8s-auth accepts two extra environment variables to set the AAD environment, and to set the AAD server application ID. The AAD server application ID will default to 6dae42f8-4368-4678-94ff-3960e28e3630 if not specified. See [here](https://github.com/azure/kubelogin#exec-plugin-format) for details.
@ -1089,9 +1089,9 @@ stringData:
"command": "argocd-k8s-auth",
"env": {
"AAD_ENVIRONMENT_NAME": "AzurePublicCloud",
"AZURE_CLIENT_SECRET": "fill in your service principal client secret",
"AAD_SERVICE_PRINCIPAL_CLIENT_SECRET": "fill in your service principal client secret",
"AZURE_TENANT_ID": "fill in tenant id",
"AZURE_CLIENT_ID": "fill in your service principal client id",
"AAD_SERVICE_PRINCIPAL_CLIENT_ID": "fill in your service principal client id",
"AAD_LOGIN_METHOD": "spn"
},
"args": ["azure"],

View file

@ -210,9 +210,8 @@ argocd_cluster_labels{label_environment="production",label_team_name="team3",nam
Metrics about API Server API request and response activity (request totals, response codes, etc...).
Scraped at the `argocd-server-metrics:8083/metrics` endpoint.
| Metric | Type | Description
|---------------------------------------------------|:---------:|---------------------------------------------------------------------------------------------|
| `argocd_login_request_total` | counter | Number of login requests. |
| Metric | Type | Description |
| ------------------------------------------------- | :-------: | ------------------------------------------------------------------------------------------- |
| `argocd_redis_request_duration` | histogram | Redis requests duration. |
| `argocd_redis_request_total` | counter | Number of Kubernetes requests executed during application reconciliation. |
| `grpc_server_handled_total` | counter | Total number of RPCs completed on the server, regardless of success or failure. |

View file

@ -1,2 +1,5 @@
This page is populated for released Argo CD versions. Use the version selector to view this table for a specific
version.
| Argo CD version | Kubernetes versions |
|-----------------|---------------------|
| 3.1 | v1.33, v1.32, v1.31, v1.30 |
| 3.0 | v1.32, v1.31, v1.30, v1.29 |
| 2.14 | v1.31, v1.30, v1.29, v1.28 |

View file

@ -10,7 +10,7 @@ most recent minor versions (so 2.14 until 3.2 is released and 2.13 until 3.1 is
## Images missing release notes on GitHub
!!! important
Images 3.0.7 - 3.0.10 are missing release notes on GitHub. There was an issue with GoReleaser and building the darwin
Images 3.0.7 - 3.0.9 are missing release notes on GitHub. There was an issue with GoReleaser and building the darwin
CLI that prevented the release notes from being published. More information can be found
on [PR #23507](https://github.com/argoproj/argo-cd/pull/23507)

View file

@ -2,7 +2,7 @@
## No more KubeVersions variable modification
Until v3.0, Argo CD removed `+` identifier from `kubeVersions` in Helm, Kustomize and Plugins. For example, if Argo CD receive `kubeVersions` as vX.Y.Z+, we convert to vX.Y.Z internally. Starting with v3.1, the internal conversion is entirely removed.
Until v3.0, Argo CD removed `+` identifier from `kubeVersions` in Helm, Kustomize and Plugins. For example, if Argo CD receive `kubeVersions` as vX.Y.Z+, we convert to vX.Y.Z internally. Starting with v3.1, the internal conversation is entirely removed.
### Detection

View file

@ -1,39 +0,0 @@
# v3.1 to 3.2
## Argo CD Now Respects Kustomize Version in `.argocd-source.yaml`
Argo CD provides a way to [override Application `spec.source` values](../../user-guide/parameters.md#store-overrides-in-git)
using the `.argocd-source.yaml` file.
Before Argo CD v3.2, you could set the Kustomize version in the Application's `.spec.source.kustomize.version` field,
but you could not set it in the `.argocd-source.yaml` file.
Starting with Argo CD v3.2, you can now set the Kustomize version in the `.argocd-source.yaml` file like this:
```yaml
kustomize:
version: v4.5.7
```
## Deprecated fields in the repo-server GRPC service
The repo-server's GRPC service is generally considered an internal API and is not recommended for use by external
clients. No user-facing services or functionality have changed. However, if you are using the repo-server's GRPC service
directly, please note field deprecations in the following messages.
The `kustomizeOptions.binaryPath` field in the `ManifestRequest` and `RepoServerAppDetailsQuery` messages has been
deprecated. Instead of calculating the correct binary path client-side, the client is expected to populate the
`kustomizeOptions.versions` field with the [configured Kustomize binary paths](../../user-guide/kustomize.md#custom-kustomize-versions).
This allows the repo-server to select the correct binary path based on the Kustomize version configured in the
Application's source field as well as any [overrides configured via git](../../user-guide/parameters.md#store-overrides-in-git).
The `kustomizeOptions.binaryPath` will continue to be respected when `kustomizeOptions.versions` is not set, but this is
not recommended. It will prevent overrides configured via git from being respected. The `kustomizeOptions.binaryPath`
field will be removed in a future release.
If the repo-server encounters a request with the `kustomizeOptions.binaryPath` field set, it will log a warning message:
> kustomizeOptions.binaryPath is deprecated, use KustomizeOptions.versions instead
The `ManifestRequest` and `RepoServerAppDetailsQuery` messages are used by the following GRPC services:
`GenerateManifest`, `GenerateManifestWithFiles`, and `GetAppDetails`.

View file

@ -38,7 +38,6 @@ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/<v
<hr/>
- [v3.1 to v3.2](./3.1-3.2.md)
- [v3.0 to v3.1](./3.0-3.1.md)
- [v2.14 to v3.0](./2.14-3.0.md)
- [v2.13 to v2.14](./2.13-2.14.md)

View file

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

View file

@ -23,7 +23,7 @@ To create the application, do the following:
2. Click "Add App".
3. Search for "OpenID Connect" in the search field.
4. Select the "OpenId Connect (OIDC)" app to create.
5. Update the "Display Name" field (could be something like "ArgoCD (Production)").
5. Update the "Display Name" field (could be something like "ArgoCD (Production)".
6. Click "Save".
### Configuring OIDC Application Settings

View file

@ -3,7 +3,7 @@ mkdocs==1.6.1
# Thus pointing to the older version of mkdocs-material.
mkdocs-material==7.1.8
markdown_include==0.8.1
pygments==2.19.2
pygments==2.19.1
jinja2==3.1.6
markdown==3.8.2
pymdown-extensions==10.16
markdown==3.8.1
pymdown-extensions==10.15

View file

@ -18,50 +18,37 @@ recent minor releases.
| [dex:v2.43.0](master/ghcr.io_dexidp_dex_v2.43.0.html) | 0 | 0 | 0 | 0 |
| [haproxy:3.0.8-alpine](master/public.ecr.aws_docker_library_haproxy_3.0.8-alpine.html) | 0 | 0 | 0 | 0 |
| [redis:7.2.7-alpine](master/public.ecr.aws_docker_library_redis_7.2.7-alpine.html) | 0 | 0 | 0 | 0 |
| [argocd:latest](master/quay.io_argoproj_argocd_latest.html) | 0 | 0 | 9 | 7 |
| [argocd:latest](master/quay.io_argoproj_argocd_latest.html) | 0 | 0 | 4 | 9 |
| [install.yaml](master/argocd-iac-install.html) | - | - | - | - |
| [namespace-install.yaml](master/argocd-iac-namespace-install.html) | - | - | - | - |
### v3.1.0-rc3
### v3.0.6
| | Critical | High | Medium | Low |
|---:|:--------:|:----:|:------:|:---:|
| [go.mod](v3.1.0-rc3/argocd-test.html) | 0 | 0 | 5 | 0 |
| [ui/yarn.lock](v3.1.0-rc3/argocd-test.html) | 0 | 0 | 1 | 2 |
| [dex:v2.43.0](v3.1.0-rc3/ghcr.io_dexidp_dex_v2.43.0.html) | 0 | 0 | 0 | 0 |
| [haproxy:3.0.8-alpine](v3.1.0-rc3/public.ecr.aws_docker_library_haproxy_3.0.8-alpine.html) | 0 | 0 | 0 | 0 |
| [redis:7.2.7-alpine](v3.1.0-rc3/public.ecr.aws_docker_library_redis_7.2.7-alpine.html) | 0 | 0 | 0 | 0 |
| [argocd:v3.1.0-rc3](v3.1.0-rc3/quay.io_argoproj_argocd_v3.1.0-rc3.html) | 0 | 0 | 8 | 9 |
| [install.yaml](v3.1.0-rc3/argocd-iac-install.html) | - | - | - | - |
| [namespace-install.yaml](v3.1.0-rc3/argocd-iac-namespace-install.html) | - | - | - | - |
| [go.mod](v3.0.6/argocd-test.html) | 0 | 3 | 7 | 0 |
| [ui/yarn.lock](v3.0.6/argocd-test.html) | 0 | 1 | 2 | 4 |
| [dex:v2.41.1](v3.0.6/ghcr.io_dexidp_dex_v2.41.1.html) | 0 | 1 | 0 | 4 |
| [haproxy:3.0.8-alpine](v3.0.6/public.ecr.aws_docker_library_haproxy_3.0.8-alpine.html) | 0 | 0 | 0 | 0 |
| [redis:7.2.7-alpine](v3.0.6/public.ecr.aws_docker_library_redis_7.2.7-alpine.html) | 0 | 0 | 0 | 0 |
| [argocd:v3.0.6](v3.0.6/quay.io_argoproj_argocd_v3.0.6.html) | 0 | 0 | 4 | 9 |
| [redis:7.2.7-alpine](v3.0.6/redis_7.2.7-alpine.html) | 0 | 0 | 0 | 0 |
| [install.yaml](v3.0.6/argocd-iac-install.html) | - | - | - | - |
| [namespace-install.yaml](v3.0.6/argocd-iac-namespace-install.html) | - | - | - | - |
### v3.0.11
### v2.14.14
| | Critical | High | Medium | Low |
|---:|:--------:|:----:|:------:|:---:|
| [go.mod](v3.0.11/argocd-test.html) | 0 | 3 | 5 | 0 |
| [ui/yarn.lock](v3.0.11/argocd-test.html) | 0 | 1 | 2 | 4 |
| [dex:v2.41.1](v3.0.11/ghcr.io_dexidp_dex_v2.41.1.html) | 0 | 1 | 0 | 4 |
| [haproxy:3.0.8-alpine](v3.0.11/public.ecr.aws_docker_library_haproxy_3.0.8-alpine.html) | 0 | 0 | 0 | 0 |
| [redis:7.2.7-alpine](v3.0.11/public.ecr.aws_docker_library_redis_7.2.7-alpine.html) | 0 | 0 | 0 | 0 |
| [argocd:v3.0.11](v3.0.11/quay.io_argoproj_argocd_v3.0.11.html) | 0 | 0 | 8 | 9 |
| [redis:7.2.7-alpine](v3.0.11/redis_7.2.7-alpine.html) | 0 | 0 | 0 | 0 |
| [install.yaml](v3.0.11/argocd-iac-install.html) | - | - | - | - |
| [namespace-install.yaml](v3.0.11/argocd-iac-namespace-install.html) | - | - | - | - |
### v2.14.15
| | Critical | High | Medium | Low |
|---:|:--------:|:----:|:------:|:---:|
| [go.mod](v2.14.15/argocd-test.html) | 0 | 1 | 8 | 0 |
| [ui/yarn.lock](v2.14.15/argocd-test.html) | 0 | 0 | 2 | 3 |
| [dex:v2.41.1](v2.14.15/ghcr.io_dexidp_dex_v2.41.1.html) | 0 | 1 | 0 | 4 |
| [haproxy:2.6.17-alpine](v2.14.15/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html) | 0 | 1 | 2 | 6 |
| [redis:7.0.15-alpine](v2.14.15/public.ecr.aws_docker_library_redis_7.0.15-alpine.html) | 0 | 0 | 0 | 4 |
| [argocd:v2.14.15](v2.14.15/quay.io_argoproj_argocd_v2.14.15.html) | 0 | 0 | 21 | 9 |
| [redis:7.0.15-alpine](v2.14.15/redis_7.0.15-alpine.html) | 0 | 0 | 0 | 4 |
| [install.yaml](v2.14.15/argocd-iac-install.html) | - | - | - | - |
| [namespace-install.yaml](v2.14.15/argocd-iac-namespace-install.html) | - | - | - | - |
| [go.mod](v2.14.14/argocd-test.html) | 0 | 1 | 8 | 0 |
| [ui/yarn.lock](v2.14.14/argocd-test.html) | 0 | 0 | 2 | 3 |
| [dex:v2.41.1](v2.14.14/ghcr.io_dexidp_dex_v2.41.1.html) | 0 | 1 | 0 | 4 |
| [haproxy:2.6.17-alpine](v2.14.14/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html) | 0 | 1 | 2 | 6 |
| [redis:7.0.15-alpine](v2.14.14/public.ecr.aws_docker_library_redis_7.0.15-alpine.html) | 0 | 0 | 0 | 4 |
| [argocd:v2.14.14](v2.14.14/quay.io_argoproj_argocd_v2.14.14.html) | 0 | 0 | 4 | 9 |
| [redis:7.0.15-alpine](v2.14.14/redis_7.0.15-alpine.html) | 0 | 0 | 0 | 4 |
| [install.yaml](v2.14.14/argocd-iac-install.html) | - | - | - | - |
| [namespace-install.yaml](v2.14.14/argocd-iac-namespace-install.html) | - | - | - | - |
### v2.13.8
@ -72,7 +59,7 @@ recent minor releases.
| [dex:v2.41.1](v2.13.8/ghcr.io_dexidp_dex_v2.41.1.html) | 0 | 1 | 0 | 4 |
| [haproxy:2.6.17-alpine](v2.13.8/public.ecr.aws_docker_library_haproxy_2.6.17-alpine.html) | 0 | 1 | 2 | 6 |
| [redis:7.0.15-alpine](v2.13.8/public.ecr.aws_docker_library_redis_7.0.15-alpine.html) | 0 | 0 | 0 | 4 |
| [argocd:v2.13.8](v2.13.8/quay.io_argoproj_argocd_v2.13.8.html) | 0 | 0 | 23 | 9 |
| [argocd:v2.13.8](v2.13.8/quay.io_argoproj_argocd_v2.13.8.html) | 0 | 0 | 5 | 9 |
| [redis:7.0.15-alpine](v2.13.8/redis_7.0.15-alpine.html) | 0 | 0 | 0 | 4 |
| [install.yaml](v2.13.8/argocd-iac-install.html) | - | - | - | - |
| [namespace-install.yaml](v2.13.8/argocd-iac-namespace-install.html) | - | - | - | - |

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:26:39 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:25:46 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: 24394
Line number: 24380
</li>
</ul>
@ -553,7 +553,7 @@
</li>
<li class="card__meta__item">
Line number: 24074
Line number: 24060
</li>
</ul>
@ -599,7 +599,7 @@
</li>
<li class="card__meta__item">
Line number: 24162
Line number: 24148
</li>
</ul>
@ -645,7 +645,7 @@
</li>
<li class="card__meta__item">
Line number: 24190
Line number: 24176
</li>
</ul>
@ -691,7 +691,7 @@
</li>
<li class="card__meta__item">
Line number: 24220
Line number: 24206
</li>
</ul>
@ -737,7 +737,7 @@
</li>
<li class="card__meta__item">
Line number: 24238
Line number: 24224
</li>
</ul>
@ -783,7 +783,7 @@
</li>
<li class="card__meta__item">
Line number: 24256
Line number: 24242
</li>
</ul>
@ -829,7 +829,7 @@
</li>
<li class="card__meta__item">
Line number: 24278
Line number: 24264
</li>
</ul>
@ -881,7 +881,7 @@
</li>
<li class="card__meta__item">
Line number: 25486
Line number: 25466
</li>
</ul>
@ -933,7 +933,7 @@
</li>
<li class="card__meta__item">
Line number: 25805
Line number: 25785
</li>
</ul>
@ -991,7 +991,7 @@
</li>
<li class="card__meta__item">
Line number: 24981
Line number: 24967
</li>
</ul>
@ -1049,7 +1049,7 @@
</li>
<li class="card__meta__item">
Line number: 25282
Line number: 25262
</li>
</ul>
@ -1107,7 +1107,7 @@
</li>
<li class="card__meta__item">
Line number: 25230
Line number: 25210
</li>
</ul>
@ -1165,7 +1165,7 @@
</li>
<li class="card__meta__item">
Line number: 25344
Line number: 25324
</li>
</ul>
@ -1223,7 +1223,7 @@
</li>
<li class="card__meta__item">
Line number: 25457
Line number: 25437
</li>
</ul>
@ -1281,7 +1281,7 @@
</li>
<li class="card__meta__item">
Line number: 25481
Line number: 25461
</li>
</ul>
@ -1339,7 +1339,7 @@
</li>
<li class="card__meta__item">
Line number: 25805
Line number: 25785
</li>
</ul>
@ -1397,7 +1397,7 @@
</li>
<li class="card__meta__item">
Line number: 25540
Line number: 25520
</li>
</ul>
@ -1455,7 +1455,7 @@
</li>
<li class="card__meta__item">
Line number: 25892
Line number: 25872
</li>
</ul>
@ -1513,7 +1513,7 @@
</li>
<li class="card__meta__item">
Line number: 26302
Line number: 26276
</li>
</ul>
@ -1565,7 +1565,7 @@
</li>
<li class="card__meta__item">
Line number: 25262
Line number: 25242
</li>
</ul>
@ -1617,7 +1617,7 @@
</li>
<li class="card__meta__item">
Line number: 24981
Line number: 24967
</li>
</ul>
@ -1669,7 +1669,7 @@
</li>
<li class="card__meta__item">
Line number: 25230
Line number: 25210
</li>
</ul>
@ -1721,7 +1721,7 @@
</li>
<li class="card__meta__item">
Line number: 25457
Line number: 25437
</li>
</ul>
@ -1779,7 +1779,7 @@
</li>
<li class="card__meta__item">
Line number: 24981
Line number: 24967
</li>
</ul>
@ -1837,7 +1837,7 @@
</li>
<li class="card__meta__item">
Line number: 25230
Line number: 25210
</li>
</ul>
@ -1895,7 +1895,7 @@
</li>
<li class="card__meta__item">
Line number: 25282
Line number: 25262
</li>
</ul>
@ -1953,7 +1953,7 @@
</li>
<li class="card__meta__item">
Line number: 25344
Line number: 25324
</li>
</ul>
@ -2011,7 +2011,7 @@
</li>
<li class="card__meta__item">
Line number: 25457
Line number: 25437
</li>
</ul>
@ -2069,7 +2069,7 @@
</li>
<li class="card__meta__item">
Line number: 25481
Line number: 25461
</li>
</ul>
@ -2127,7 +2127,7 @@
</li>
<li class="card__meta__item">
Line number: 25805
Line number: 25785
</li>
</ul>
@ -2185,7 +2185,7 @@
</li>
<li class="card__meta__item">
Line number: 25540
Line number: 25520
</li>
</ul>
@ -2243,7 +2243,7 @@
</li>
<li class="card__meta__item">
Line number: 25892
Line number: 25872
</li>
</ul>
@ -2301,7 +2301,7 @@
</li>
<li class="card__meta__item">
Line number: 26302
Line number: 26276
</li>
</ul>
@ -2357,7 +2357,7 @@
</li>
<li class="card__meta__item">
Line number: 25152
Line number: 25132
</li>
</ul>
@ -2413,7 +2413,7 @@
</li>
<li class="card__meta__item">
Line number: 25290
Line number: 25270
</li>
</ul>
@ -2469,7 +2469,7 @@
</li>
<li class="card__meta__item">
Line number: 25265
Line number: 25245
</li>
</ul>
@ -2525,7 +2525,7 @@
</li>
<li class="card__meta__item">
Line number: 25389
Line number: 25369
</li>
</ul>
@ -2581,7 +2581,7 @@
</li>
<li class="card__meta__item">
Line number: 25474
Line number: 25454
</li>
</ul>
@ -2637,7 +2637,7 @@
</li>
<li class="card__meta__item">
Line number: 25488
Line number: 25468
</li>
</ul>
@ -2693,7 +2693,7 @@
</li>
<li class="card__meta__item">
Line number: 25812
Line number: 25792
</li>
</ul>
@ -2749,7 +2749,7 @@
</li>
<li class="card__meta__item">
Line number: 25778
Line number: 25758
</li>
</ul>
@ -2805,7 +2805,7 @@
</li>
<li class="card__meta__item">
Line number: 26201
Line number: 26175
</li>
</ul>
@ -2861,7 +2861,7 @@
</li>
<li class="card__meta__item">
Line number: 26571
Line number: 26539
</li>
</ul>

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:26:49 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:25:58 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: 1275
Line number: 1269
</li>
</ul>
@ -887,7 +887,7 @@
</li>
<li class="card__meta__item">
Line number: 1594
Line number: 1588
</li>
</ul>
@ -1003,7 +1003,7 @@
</li>
<li class="card__meta__item">
Line number: 1071
Line number: 1065
</li>
</ul>
@ -1061,7 +1061,7 @@
</li>
<li class="card__meta__item">
Line number: 1019
Line number: 1013
</li>
</ul>
@ -1119,7 +1119,7 @@
</li>
<li class="card__meta__item">
Line number: 1133
Line number: 1127
</li>
</ul>
@ -1177,7 +1177,7 @@
</li>
<li class="card__meta__item">
Line number: 1246
Line number: 1240
</li>
</ul>
@ -1235,7 +1235,7 @@
</li>
<li class="card__meta__item">
Line number: 1270
Line number: 1264
</li>
</ul>
@ -1293,7 +1293,7 @@
</li>
<li class="card__meta__item">
Line number: 1594
Line number: 1588
</li>
</ul>
@ -1351,7 +1351,7 @@
</li>
<li class="card__meta__item">
Line number: 1329
Line number: 1323
</li>
</ul>
@ -1409,7 +1409,7 @@
</li>
<li class="card__meta__item">
Line number: 1681
Line number: 1675
</li>
</ul>
@ -1467,7 +1467,7 @@
</li>
<li class="card__meta__item">
Line number: 2091
Line number: 2079
</li>
</ul>
@ -1519,7 +1519,7 @@
</li>
<li class="card__meta__item">
Line number: 1051
Line number: 1045
</li>
</ul>
@ -1623,7 +1623,7 @@
</li>
<li class="card__meta__item">
Line number: 1019
Line number: 1013
</li>
</ul>
@ -1675,7 +1675,7 @@
</li>
<li class="card__meta__item">
Line number: 1246
Line number: 1240
</li>
</ul>
@ -1791,7 +1791,7 @@
</li>
<li class="card__meta__item">
Line number: 1019
Line number: 1013
</li>
</ul>
@ -1849,7 +1849,7 @@
</li>
<li class="card__meta__item">
Line number: 1071
Line number: 1065
</li>
</ul>
@ -1907,7 +1907,7 @@
</li>
<li class="card__meta__item">
Line number: 1133
Line number: 1127
</li>
</ul>
@ -1965,7 +1965,7 @@
</li>
<li class="card__meta__item">
Line number: 1246
Line number: 1240
</li>
</ul>
@ -2023,7 +2023,7 @@
</li>
<li class="card__meta__item">
Line number: 1270
Line number: 1264
</li>
</ul>
@ -2081,7 +2081,7 @@
</li>
<li class="card__meta__item">
Line number: 1594
Line number: 1588
</li>
</ul>
@ -2139,7 +2139,7 @@
</li>
<li class="card__meta__item">
Line number: 1329
Line number: 1323
</li>
</ul>
@ -2197,7 +2197,7 @@
</li>
<li class="card__meta__item">
Line number: 1681
Line number: 1675
</li>
</ul>
@ -2255,7 +2255,7 @@
</li>
<li class="card__meta__item">
Line number: 2091
Line number: 2079
</li>
</ul>
@ -2311,7 +2311,7 @@
</li>
<li class="card__meta__item">
Line number: 941
Line number: 935
</li>
</ul>
@ -2367,7 +2367,7 @@
</li>
<li class="card__meta__item">
Line number: 1079
Line number: 1073
</li>
</ul>
@ -2423,7 +2423,7 @@
</li>
<li class="card__meta__item">
Line number: 1054
Line number: 1048
</li>
</ul>
@ -2479,7 +2479,7 @@
</li>
<li class="card__meta__item">
Line number: 1178
Line number: 1172
</li>
</ul>
@ -2535,7 +2535,7 @@
</li>
<li class="card__meta__item">
Line number: 1263
Line number: 1257
</li>
</ul>
@ -2591,7 +2591,7 @@
</li>
<li class="card__meta__item">
Line number: 1277
Line number: 1271
</li>
</ul>
@ -2647,7 +2647,7 @@
</li>
<li class="card__meta__item">
Line number: 1601
Line number: 1595
</li>
</ul>
@ -2703,7 +2703,7 @@
</li>
<li class="card__meta__item">
Line number: 1567
Line number: 1561
</li>
</ul>
@ -2759,7 +2759,7 @@
</li>
<li class="card__meta__item">
Line number: 1990
Line number: 1978
</li>
</ul>
@ -2815,7 +2815,7 @@
</li>
<li class="card__meta__item">
Line number: 2360
Line number: 2342
</li>
</ul>

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -201,15 +201,6 @@
padding: 1.5em;
}
.card__labels {
position: absolute;
top: 1.1em;
left: 0;
display: flex;
align-items: center;
gap: 8px;
}
.card .label {
background-color: #767676;
border: 2px solid #767676;
@ -267,7 +258,10 @@
padding-top: 4em;
}
.card--vuln .card__labels > .label:first-child {
.card--vuln .label {
left: 0;
position: absolute;
top: 1.1em;
padding-left: 1.9em;
padding-right: 1.9em;
border-radius: 0 0.25rem 0.25rem 0;
@ -295,7 +289,6 @@
.card--vuln .card__title {
font-size: 28px;
margin-top: 0;
margin-right: 100px; /* Ensure space for the risk score */
}
.card--vuln .card__cta p {
@ -303,30 +296,6 @@
text-align: right;
}
.risk-score-display {
position: absolute;
top: 1.5em;
right: 1.5em;
text-align: right;
z-index: 10;
}
.risk-score-display__label {
font-size: 0.7em;
font-weight: bold;
color: #586069;
text-transform: uppercase;
line-height: 1;
margin-bottom: 3px;
}
.risk-score-display__value {
font-size: 1.9em;
font-weight: 600;
color: #24292e;
line-height: 1;
}
.source-panel {
clear: both;
display: flex;
@ -487,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:24:27 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:23:32 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>
@ -501,7 +470,7 @@
<div class="meta-counts">
<div class="meta-count"><span>8</span> <span>known vulnerabilities</span></div>
<div class="meta-count"><span>28 vulnerable dependency paths</span></div>
<div class="meta-count"><span>2106</span> <span>dependencies</span></div>
<div class="meta-count"><span>2104</span> <span>dependencies</span></div>
</div><!-- .meta-counts -->
</div><!-- .layout-container--short -->
</header><!-- .project__header -->
@ -513,10 +482,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -575,10 +542,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -639,10 +604,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -662,7 +625,7 @@
<li class="card__meta__item">Introduced through:
github.com/argoproj/argo-cd/v3@0.0.0 and github.com/hashicorp/go-retryablehttp@0.7.8
github.com/argoproj/argo-cd/v3@0.0.0 and github.com/hashicorp/go-retryablehttp@0.7.7
</li>
</ul>
@ -677,7 +640,7 @@
<span class="list-paths__item__introduced"><em>Introduced through</em>:
github.com/argoproj/argo-cd/v3@0.0.0
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
</span>
@ -688,7 +651,7 @@
<span class="list-paths__item__arrow"></span>
github.com/argoproj/notifications-engine/pkg/services@#87bf0576a872
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
</span>
@ -697,9 +660,9 @@
<span class="list-paths__item__introduced"><em>Introduced through</em>:
github.com/argoproj/argo-cd/v3@0.0.0
<span class="list-paths__item__arrow"></span>
gitlab.com/gitlab-org/api/client-go@0.134.0
gitlab.com/gitlab-org/api/client-go@0.130.1
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
</span>
@ -712,7 +675,7 @@
<span class="list-paths__item__arrow"></span>
github.com/argoproj/notifications-engine/pkg/services@#87bf0576a872
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
</span>
@ -725,7 +688,7 @@
<span class="list-paths__item__arrow"></span>
github.com/argoproj/notifications-engine/pkg/services@#87bf0576a872
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
</span>
@ -738,7 +701,7 @@
<span class="list-paths__item__arrow"></span>
github.com/opsgenie/opsgenie-go-sdk-v2/client@1.2.23
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
</span>
@ -753,7 +716,7 @@
<span class="list-paths__item__arrow"></span>
github.com/argoproj/notifications-engine/pkg/services@#87bf0576a872
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
</span>
@ -768,7 +731,7 @@
<span class="list-paths__item__arrow"></span>
github.com/argoproj/notifications-engine/pkg/services@#87bf0576a872
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
</span>
@ -783,7 +746,7 @@
<span class="list-paths__item__arrow"></span>
github.com/opsgenie/opsgenie-go-sdk-v2/client@1.2.23
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
</span>
@ -798,7 +761,7 @@
<span class="list-paths__item__arrow"></span>
github.com/opsgenie/opsgenie-go-sdk-v2/client@1.2.23
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
</span>
@ -815,7 +778,7 @@
<span class="list-paths__item__arrow"></span>
github.com/opsgenie/opsgenie-go-sdk-v2/client@1.2.23
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
</span>
@ -832,7 +795,7 @@
<span class="list-paths__item__arrow"></span>
github.com/opsgenie/opsgenie-go-sdk-v2/client@1.2.23
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
</span>
@ -856,10 +819,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -880,7 +841,7 @@
<li class="card__meta__item">Introduced through:
github.com/argoproj/argo-cd/v3@0.0.0, github.com/hashicorp/go-retryablehttp@0.7.8 and others
github.com/argoproj/argo-cd/v3@0.0.0, github.com/hashicorp/go-retryablehttp@0.7.7 and others
</li>
</ul>
@ -894,7 +855,7 @@
<span class="list-paths__item__introduced"><em>Introduced through</em>:
github.com/argoproj/argo-cd/v3@0.0.0
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-cleanhttp@0.5.2
@ -905,7 +866,7 @@
<span class="list-paths__item__introduced"><em>Introduced through</em>:
github.com/argoproj/argo-cd/v3@0.0.0
<span class="list-paths__item__arrow"></span>
gitlab.com/gitlab-org/api/client-go@0.134.0
gitlab.com/gitlab-org/api/client-go@0.130.1
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-cleanhttp@0.5.2
@ -916,9 +877,9 @@
<span class="list-paths__item__introduced"><em>Introduced through</em>:
github.com/argoproj/argo-cd/v3@0.0.0
<span class="list-paths__item__arrow"></span>
gitlab.com/gitlab-org/api/client-go@0.134.0
gitlab.com/gitlab-org/api/client-go@0.130.1
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-cleanhttp@0.5.2
@ -933,7 +894,7 @@
<span class="list-paths__item__arrow"></span>
github.com/opsgenie/opsgenie-go-sdk-v2/client@1.2.23
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-cleanhttp@0.5.2
@ -950,7 +911,7 @@
<span class="list-paths__item__arrow"></span>
github.com/opsgenie/opsgenie-go-sdk-v2/client@1.2.23
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-cleanhttp@0.5.2
@ -967,7 +928,7 @@
<span class="list-paths__item__arrow"></span>
github.com/opsgenie/opsgenie-go-sdk-v2/client@1.2.23
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-cleanhttp@0.5.2
@ -986,7 +947,7 @@
<span class="list-paths__item__arrow"></span>
github.com/opsgenie/opsgenie-go-sdk-v2/client@1.2.23
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-cleanhttp@0.5.2
@ -1005,7 +966,7 @@
<span class="list-paths__item__arrow"></span>
github.com/opsgenie/opsgenie-go-sdk-v2/client@1.2.23
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-retryablehttp@0.7.8
github.com/hashicorp/go-retryablehttp@0.7.7
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/go-cleanhttp@0.5.2
@ -1031,10 +992,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1093,10 +1052,8 @@
<h2 class="card__title">Regular Expression Denial of Service (ReDoS)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1240,10 +1197,8 @@
<h2 class="card__title">Insecure Randomness</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -1312,10 +1267,8 @@
<h2 class="card__title">Regular Expression Denial of Service (ReDoS)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>

View file

@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Snyk test report</title>
<meta name="description" content="25 known vulnerabilities found in 34 vulnerable dependency paths.">
<meta name="description" content="24 known vulnerabilities found in 33 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">
@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -201,15 +201,6 @@
padding: 1.5em;
}
.card__labels {
position: absolute;
top: 1.1em;
left: 0;
display: flex;
align-items: center;
gap: 8px;
}
.card .label {
background-color: #767676;
border: 2px solid #767676;
@ -267,7 +258,10 @@
padding-top: 4em;
}
.card--vuln .card__labels > .label:first-child {
.card--vuln .label {
left: 0;
position: absolute;
top: 1.1em;
padding-left: 1.9em;
padding-right: 1.9em;
border-radius: 0 0.25rem 0.25rem 0;
@ -295,7 +289,6 @@
.card--vuln .card__title {
font-size: 28px;
margin-top: 0;
margin-right: 100px; /* Ensure space for the risk score */
}
.card--vuln .card__cta p {
@ -303,30 +296,6 @@
text-align: right;
}
.risk-score-display {
position: absolute;
top: 1.5em;
right: 1.5em;
text-align: right;
z-index: 10;
}
.risk-score-display__label {
font-size: 0.7em;
font-weight: bold;
color: #586069;
text-transform: uppercase;
line-height: 1;
margin-bottom: 3px;
}
.risk-score-display__value {
font-size: 1.9em;
font-weight: 600;
color: #24292e;
line-height: 1;
}
.source-panel {
clear: both;
display: flex;
@ -487,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:24:38 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:23:41 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>
@ -500,8 +469,8 @@
</div>
<div class="meta-counts">
<div class="meta-count"><span>25</span> <span>known vulnerabilities</span></div>
<div class="meta-count"><span>34 vulnerable dependency paths</span></div>
<div class="meta-count"><span>24</span> <span>known vulnerabilities</span></div>
<div class="meta-count"><span>33 vulnerable dependency paths</span></div>
<div class="meta-count"><span>1131</span> <span>dependencies</span></div>
</div><!-- .meta-counts -->
</div><!-- .layout-container--short -->
@ -514,10 +483,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -587,10 +554,8 @@
<h2 class="card__title">Server-side Request Forgery (SSRF)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -660,10 +625,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -732,10 +695,8 @@
<h2 class="card__title">Asymmetric Resource Consumption (Amplification)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -803,10 +764,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -865,10 +824,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -927,10 +884,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1052,10 +1007,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1123,10 +1076,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1185,10 +1136,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1247,10 +1196,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1318,10 +1265,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1380,10 +1325,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1442,10 +1385,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1504,10 +1445,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1566,10 +1505,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1628,10 +1565,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1690,10 +1625,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1752,10 +1685,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1814,10 +1745,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1876,10 +1805,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1938,10 +1865,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2000,10 +1925,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2062,10 +1985,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2130,77 +2051,6 @@
</div>
</div><!-- .card -->
<div class="card card--vuln disclosure--not-new severity--low" data-snyk-test="low">
<h2 class="card__title">Synchronous Access of Remote Resource without Timeout</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
</div>
<hr/>
<ul class="card__meta">
<li class="card__meta__item">
Manifest file: ghcr.io/dexidp/dex:v2.43.0/hairyhenderson/gomplate/v4 <span class="list-paths__item__arrow"></span> /usr/local/bin/gomplate
</li>
<li class="card__meta__item">
Package Manager: golang
</li>
<li class="card__meta__item">
Vulnerable module:
github.com/hashicorp/vault/api
</li>
<li class="card__meta__item">Introduced through:
github.com/hairyhenderson/gomplate/v4@* and github.com/hashicorp/vault/api@v1.15.0
</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>:
github.com/hairyhenderson/gomplate/v4@*
<span class="list-paths__item__arrow"></span>
github.com/hashicorp/vault/api@v1.15.0
</span>
</li>
</ul><!-- .list-paths -->
</div><!-- .card__section -->
<hr/>
<!-- Overview -->
<h2 id="overview">Overview</h2>
<p>Affected versions of this package are vulnerable to Synchronous Access of Remote Resource without Timeout via the <code>rekey</code> and <code>recovery key</code> operations. An attacker can disrupt service availability by triggering uncontrolled cancellation actions during these processes, which can lead to denial of service.</p>
<h2 id="remediation">Remediation</h2>
<p>Upgrade <code>github.com/hashicorp/vault/api</code> to version 1.20.0 or higher.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/hashicorp/vault/commit/318f8582134a4a79a45ee2a6edad3072d865739b">GitHub Commit</a></li>
<li><a href="https://github.com/hashicorp/vault/pull/30794">GitHub PR</a></li>
<li><a href="https://discuss.hashicorp.com/t/hcsec-2025-11-vault-vulnerable-to-recovery-key-cancellation-denial-of-service/75570">HashiCorp Discuss</a></li>
</ul>
<hr/>
<div class="cta card__cta">
<p><a href="https://snyk.io/vuln/SNYK-GOLANG-GITHUBCOMHASHICORPVAULTAPI-10562144">More about this vulnerability</a></p>
</div>
</div><!-- .card -->
</div><!-- cards -->
</div>
</main><!-- .layout-stacked__content -->

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -201,15 +201,6 @@
padding: 1.5em;
}
.card__labels {
position: absolute;
top: 1.1em;
left: 0;
display: flex;
align-items: center;
gap: 8px;
}
.card .label {
background-color: #767676;
border: 2px solid #767676;
@ -267,7 +258,10 @@
padding-top: 4em;
}
.card--vuln .card__labels > .label:first-child {
.card--vuln .label {
left: 0;
position: absolute;
top: 1.1em;
padding-left: 1.9em;
padding-right: 1.9em;
border-radius: 0 0.25rem 0.25rem 0;
@ -295,7 +289,6 @@
.card--vuln .card__title {
font-size: 28px;
margin-top: 0;
margin-right: 100px; /* Ensure space for the risk score */
}
.card--vuln .card__cta p {
@ -303,30 +296,6 @@
text-align: right;
}
.risk-score-display {
position: absolute;
top: 1.5em;
right: 1.5em;
text-align: right;
z-index: 10;
}
.risk-score-display__label {
font-size: 0.7em;
font-weight: bold;
color: #586069;
text-transform: uppercase;
line-height: 1;
margin-bottom: 3px;
}
.risk-score-display__value {
font-size: 1.9em;
font-weight: 600;
color: #24292e;
line-height: 1;
}
.source-panel {
clear: both;
display: flex;
@ -487,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:24:43 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:23:46 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -201,15 +201,6 @@
padding: 1.5em;
}
.card__labels {
position: absolute;
top: 1.1em;
left: 0;
display: flex;
align-items: center;
gap: 8px;
}
.card .label {
background-color: #767676;
border: 2px solid #767676;
@ -267,7 +258,10 @@
padding-top: 4em;
}
.card--vuln .card__labels > .label:first-child {
.card--vuln .label {
left: 0;
position: absolute;
top: 1.1em;
padding-left: 1.9em;
padding-right: 1.9em;
border-radius: 0 0.25rem 0.25rem 0;
@ -295,7 +289,6 @@
.card--vuln .card__title {
font-size: 28px;
margin-top: 0;
margin-right: 100px; /* Ensure space for the risk score */
}
.card--vuln .card__cta p {
@ -303,30 +296,6 @@
text-align: right;
}
.risk-score-display {
position: absolute;
top: 1.5em;
right: 1.5em;
text-align: right;
z-index: 10;
}
.risk-score-display__label {
font-size: 0.7em;
font-weight: bold;
color: #586069;
text-transform: uppercase;
line-height: 1;
margin-bottom: 3px;
}
.risk-score-display__value {
font-size: 1.9em;
font-weight: 600;
color: #24292e;
line-height: 1;
}
.source-panel {
clear: both;
display: flex;
@ -487,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:24:48 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:23:51 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>

File diff suppressed because it is too large Load diff

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:37:15 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:33:38 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:37:25 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:33:47 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -201,15 +201,6 @@
padding: 1.5em;
}
.card__labels {
position: absolute;
top: 1.1em;
left: 0;
display: flex;
align-items: center;
gap: 8px;
}
.card .label {
background-color: #767676;
border: 2px solid #767676;
@ -267,7 +258,10 @@
padding-top: 4em;
}
.card--vuln .card__labels > .label:first-child {
.card--vuln .label {
left: 0;
position: absolute;
top: 1.1em;
padding-left: 1.9em;
padding-right: 1.9em;
border-radius: 0 0.25rem 0.25rem 0;
@ -295,7 +289,6 @@
.card--vuln .card__title {
font-size: 28px;
margin-top: 0;
margin-right: 100px; /* Ensure space for the risk score */
}
.card--vuln .card__cta p {
@ -303,30 +296,6 @@
text-align: right;
}
.risk-score-display {
position: absolute;
top: 1.5em;
right: 1.5em;
text-align: right;
z-index: 10;
}
.risk-score-display__label {
font-size: 0.7em;
font-weight: bold;
color: #586069;
text-transform: uppercase;
line-height: 1;
margin-bottom: 3px;
}
.risk-score-display__value {
font-size: 1.9em;
font-weight: 600;
color: #24292e;
line-height: 1;
}
.source-panel {
clear: both;
display: flex;
@ -487,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:35:08 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:31:33 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>
@ -512,10 +481,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -844,10 +811,8 @@
<h2 class="card__title">Server-side Request Forgery (SSRF)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -917,10 +882,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -1223,10 +1186,8 @@
<h2 class="card__title">LGPL-3.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1287,10 +1248,8 @@
<h2 class="card__title">Improper Validation of Syntactic Correctness of Input</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1648,7 +1607,7 @@
<p><a href="https://pkg.go.dev/golang.org/x/net/html">golang.org/x/net/html</a> is a package that implements an HTML5-compliant tokenizer and parser.</p>
<p>Affected versions of this package are vulnerable to Improper Validation of Syntactic Correctness of Input in the tokenizer in <code>token.go</code>, which incorrectly interprets tags as closing tags, allowing malicious input to be incorrectly processed and the DOM to be corrupted.</p>
<h2 id="details">Details</h2>
<p>Cross-site scripting (or XSS) is a code vulnerability that occurs when an attacker “injects” a malicious script into an otherwise trusted website. The injected script gets downloaded and executed by the end users browser when the user interacts with the compromised website.</p>
<p>A cross-site scripting attack occurs when the attacker tricks a legitimate web-based application or site to accept a request as originating from a trusted source.</p>
<p>This is done by escaping the context of the web application; the web application then delivers that data to its users along with other trusted dynamic content, without validating it. The browser unknowingly executes malicious script on the client side (through client-side languages; usually JavaScript or HTML) in order to perform actions that are otherwise typically blocked by the browsers Same Origin Policy.</p>
<p>Injecting malicious code is the most prevalent manner by which XSS is exploited; for this reason, escaping characters in order to prevent this manipulation is the top method for securing code against this vulnerability.</p>
<p>Escaping means that the application is coded to mark key characters, and particularly key characters included in user input, to prevent those characters from being interpreted in a dangerous context. For example, in HTML, <code>&lt;</code> can be coded as <code>&amp;lt</code>; and <code>&gt;</code> can be coded as <code>&amp;gt</code>; in order to be interpreted and displayed as themselves in text, while within the code itself, they are used for HTML tags. If malicious content is injected into an application that escapes special characters and that malicious content uses <code>&lt;</code> and <code>&gt;</code> as HTML tags, those characters are nonetheless not interpreted as HTML tags by the browser if theyve been correctly escaped in the application code and in this way the attempted attack is diverted.</p>
@ -1722,10 +1681,8 @@
<h2 class="card__title">Unexpected Status Code or Return Value</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1805,10 +1762,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1867,10 +1822,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1931,10 +1884,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2148,10 +2099,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2323,10 +2272,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2385,10 +2332,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2457,10 +2402,8 @@
<h2 class="card__title">Concurrent Execution using Shared Resource with Improper Synchronization (&#x27;Race Condition&#x27;)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2550,10 +2493,8 @@
<h2 class="card__title">Regular Expression Denial of Service (ReDoS)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2697,10 +2638,8 @@
<h2 class="card__title">Regular Expression Denial of Service (ReDoS)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -4003,10 +3942,8 @@
<h2 class="card__title">Arbitrary Code Injection</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -4080,10 +4017,8 @@
<h2 class="card__title">Insufficient Documentation of Error Handling Techniques</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -4157,10 +4092,8 @@
<h2 class="card__title">Insecure Randomness</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -4229,10 +4162,8 @@
<h2 class="card__title">Regular Expression Denial of Service (ReDoS)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -201,15 +201,6 @@
padding: 1.5em;
}
.card__labels {
position: absolute;
top: 1.1em;
left: 0;
display: flex;
align-items: center;
gap: 8px;
}
.card .label {
background-color: #767676;
border: 2px solid #767676;
@ -267,7 +258,10 @@
padding-top: 4em;
}
.card--vuln .card__labels > .label:first-child {
.card--vuln .label {
left: 0;
position: absolute;
top: 1.1em;
padding-left: 1.9em;
padding-right: 1.9em;
border-radius: 0 0.25rem 0.25rem 0;
@ -295,7 +289,6 @@
.card--vuln .card__title {
font-size: 28px;
margin-top: 0;
margin-right: 100px; /* Ensure space for the risk score */
}
.card--vuln .card__cta p {
@ -303,30 +296,6 @@
text-align: right;
}
.risk-score-display {
position: absolute;
top: 1.5em;
right: 1.5em;
text-align: right;
z-index: 10;
}
.risk-score-display__label {
font-size: 0.7em;
font-weight: bold;
color: #586069;
text-transform: uppercase;
line-height: 1;
margin-bottom: 3px;
}
.risk-score-display__value {
font-size: 1.9em;
font-weight: 600;
color: #24292e;
line-height: 1;
}
.source-panel {
clear: both;
display: flex;
@ -487,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:35:17 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:31:40 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>
@ -514,10 +483,8 @@
<h2 class="card__title">Incorrect Implementation of Authentication Algorithm</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--critical">
<span class="label__text">critical severity</span>
</div>
<div class="label label--critical">
<span class="label__text">critical severity</span>
</div>
<hr/>
@ -591,10 +558,8 @@
<h2 class="card__title">Access of Resource Using Incompatible Type (&#x27;Type Confusion&#x27;)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -750,10 +715,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -832,10 +795,8 @@
<h2 class="card__title">Server-side Request Forgery (SSRF)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -914,10 +875,8 @@
<h2 class="card__title">Denial of Service (DoS)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -999,10 +958,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -1071,10 +1028,8 @@
<h2 class="card__title">Asymmetric Resource Consumption (Amplification)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -1142,10 +1097,8 @@
<h2 class="card__title">Insertion of Sensitive Information into Log File</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1212,10 +1165,8 @@
<h2 class="card__title">Improper Validation of Syntactic Correctness of Input</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1265,7 +1216,7 @@
<p><a href="https://pkg.go.dev/golang.org/x/net/html">golang.org/x/net/html</a> is a package that implements an HTML5-compliant tokenizer and parser.</p>
<p>Affected versions of this package are vulnerable to Improper Validation of Syntactic Correctness of Input in the tokenizer in <code>token.go</code>, which incorrectly interprets tags as closing tags, allowing malicious input to be incorrectly processed and the DOM to be corrupted.</p>
<h2 id="details">Details</h2>
<p>Cross-site scripting (or XSS) is a code vulnerability that occurs when an attacker “injects” a malicious script into an otherwise trusted website. The injected script gets downloaded and executed by the end users browser when the user interacts with the compromised website.</p>
<p>A cross-site scripting attack occurs when the attacker tricks a legitimate web-based application or site to accept a request as originating from a trusted source.</p>
<p>This is done by escaping the context of the web application; the web application then delivers that data to its users along with other trusted dynamic content, without validating it. The browser unknowingly executes malicious script on the client side (through client-side languages; usually JavaScript or HTML) in order to perform actions that are otherwise typically blocked by the browsers Same Origin Policy.</p>
<p>Injecting malicious code is the most prevalent manner by which XSS is exploited; for this reason, escaping characters in order to prevent this manipulation is the top method for securing code against this vulnerability.</p>
<p>Escaping means that the application is coded to mark key characters, and particularly key characters included in user input, to prevent those characters from being interpreted in a dangerous context. For example, in HTML, <code>&lt;</code> can be coded as <code>&amp;lt</code>; and <code>&gt;</code> can be coded as <code>&amp;gt</code>; in order to be interpreted and displayed as themselves in text, while within the code itself, they are used for HTML tags. If malicious content is injected into an application that escapes special characters and that malicious content uses <code>&lt;</code> and <code>&gt;</code> as HTML tags, those characters are nonetheless not interpreted as HTML tags by the browser if theyve been correctly escaped in the application code and in this way the attempted attack is diverted.</p>
@ -1339,10 +1290,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1401,10 +1350,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1463,10 +1410,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1588,10 +1533,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1659,10 +1602,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1721,10 +1662,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1783,10 +1722,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1854,10 +1791,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1916,10 +1851,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1978,10 +1911,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2040,10 +1971,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2102,10 +2031,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2164,10 +2091,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2226,10 +2151,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2288,10 +2211,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2350,10 +2271,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2412,10 +2331,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2474,10 +2391,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2536,10 +2451,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2598,10 +2511,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2679,10 +2590,8 @@
<h2 class="card__title">CVE-2024-9143</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -2844,10 +2753,8 @@
<h2 class="card__title">CVE-2024-13176</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -3002,10 +2909,8 @@
<h2 class="card__title">CVE-2024-12797</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -3159,10 +3064,8 @@
<h2 class="card__title">CVE-2025-26519</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -201,15 +201,6 @@
padding: 1.5em;
}
.card__labels {
position: absolute;
top: 1.1em;
left: 0;
display: flex;
align-items: center;
gap: 8px;
}
.card .label {
background-color: #767676;
border: 2px solid #767676;
@ -267,7 +258,10 @@
padding-top: 4em;
}
.card--vuln .card__labels > .label:first-child {
.card--vuln .label {
left: 0;
position: absolute;
top: 1.1em;
padding-left: 1.9em;
padding-right: 1.9em;
border-radius: 0 0.25rem 0.25rem 0;
@ -295,7 +289,6 @@
.card--vuln .card__title {
font-size: 28px;
margin-top: 0;
margin-right: 100px; /* Ensure space for the risk score */
}
.card--vuln .card__cta p {
@ -303,30 +296,6 @@
text-align: right;
}
.risk-score-display {
position: absolute;
top: 1.5em;
right: 1.5em;
text-align: right;
z-index: 10;
}
.risk-score-display__label {
font-size: 0.7em;
font-weight: bold;
color: #586069;
text-transform: uppercase;
line-height: 1;
margin-bottom: 3px;
}
.risk-score-display__value {
font-size: 1.9em;
font-weight: 600;
color: #24292e;
line-height: 1;
}
.source-panel {
clear: both;
display: flex;
@ -487,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:35:20 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:31:44 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>
@ -520,10 +489,8 @@
<h2 class="card__title">Access of Resource Using Incompatible Type (&#x27;Type Confusion&#x27;)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -712,10 +679,8 @@
<h2 class="card__title">Use After Free</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -833,10 +798,8 @@
<h2 class="card__title">Use After Free</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -954,10 +917,8 @@
<h2 class="card__title">CVE-2024-4741</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -1149,10 +1110,8 @@
<h2 class="card__title">CVE-2024-5535</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -1380,10 +1339,8 @@
<h2 class="card__title">CVE-2024-9143</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -1578,10 +1535,8 @@
<h2 class="card__title">CVE-2024-13176</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -1769,10 +1724,8 @@
<h2 class="card__title">CVE-2024-12797</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -1959,10 +1912,8 @@
<h2 class="card__title">CVE-2025-26519</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -201,15 +201,6 @@
padding: 1.5em;
}
.card__labels {
position: absolute;
top: 1.1em;
left: 0;
display: flex;
align-items: center;
gap: 8px;
}
.card .label {
background-color: #767676;
border: 2px solid #767676;
@ -267,7 +258,10 @@
padding-top: 4em;
}
.card--vuln .card__labels > .label:first-child {
.card--vuln .label {
left: 0;
position: absolute;
top: 1.1em;
padding-left: 1.9em;
padding-right: 1.9em;
border-radius: 0 0.25rem 0.25rem 0;
@ -295,7 +289,6 @@
.card--vuln .card__title {
font-size: 28px;
margin-top: 0;
margin-right: 100px; /* Ensure space for the risk score */
}
.card--vuln .card__cta p {
@ -303,30 +296,6 @@
text-align: right;
}
.risk-score-display {
position: absolute;
top: 1.5em;
right: 1.5em;
text-align: right;
z-index: 10;
}
.risk-score-display__label {
font-size: 0.7em;
font-weight: bold;
color: #586069;
text-transform: uppercase;
line-height: 1;
margin-bottom: 3px;
}
.risk-score-display__value {
font-size: 1.9em;
font-weight: 600;
color: #24292e;
line-height: 1;
}
.source-panel {
clear: both;
display: flex;
@ -487,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:35:25 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:31:48 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>
@ -512,10 +481,8 @@
<h2 class="card__title">CVE-2024-9143</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -699,10 +666,8 @@
<h2 class="card__title">CVE-2024-13176</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -879,10 +844,8 @@
<h2 class="card__title">CVE-2024-12797</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -1058,10 +1021,8 @@
<h2 class="card__title">CVE-2025-26519</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>

File diff suppressed because it is too large Load diff

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -201,15 +201,6 @@
padding: 1.5em;
}
.card__labels {
position: absolute;
top: 1.1em;
left: 0;
display: flex;
align-items: center;
gap: 8px;
}
.card .label {
background-color: #767676;
border: 2px solid #767676;
@ -267,7 +258,10 @@
padding-top: 4em;
}
.card--vuln .card__labels > .label:first-child {
.card--vuln .label {
left: 0;
position: absolute;
top: 1.1em;
padding-left: 1.9em;
padding-right: 1.9em;
border-radius: 0 0.25rem 0.25rem 0;
@ -295,7 +289,6 @@
.card--vuln .card__title {
font-size: 28px;
margin-top: 0;
margin-right: 100px; /* Ensure space for the risk score */
}
.card--vuln .card__cta p {
@ -303,30 +296,6 @@
text-align: right;
}
.risk-score-display {
position: absolute;
top: 1.5em;
right: 1.5em;
text-align: right;
z-index: 10;
}
.risk-score-display__label {
font-size: 0.7em;
font-weight: bold;
color: #586069;
text-transform: uppercase;
line-height: 1;
margin-bottom: 3px;
}
.risk-score-display__value {
font-size: 1.9em;
font-weight: 600;
color: #24292e;
line-height: 1;
}
.source-panel {
clear: both;
display: flex;
@ -487,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:35:49 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:32:11 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>
@ -512,10 +481,8 @@
<h2 class="card__title">CVE-2024-9143</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -699,10 +666,8 @@
<h2 class="card__title">CVE-2024-13176</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -879,10 +844,8 @@
<h2 class="card__title">CVE-2024-12797</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -1058,10 +1021,8 @@
<h2 class="card__title">CVE-2025-26519</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:34:43 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:31:07 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:34:53 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:31:17 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -201,15 +201,6 @@
padding: 1.5em;
}
.card__labels {
position: absolute;
top: 1.1em;
left: 0;
display: flex;
align-items: center;
gap: 8px;
}
.card .label {
background-color: #767676;
border: 2px solid #767676;
@ -267,7 +258,10 @@
padding-top: 4em;
}
.card--vuln .card__labels > .label:first-child {
.card--vuln .label {
left: 0;
position: absolute;
top: 1.1em;
padding-left: 1.9em;
padding-right: 1.9em;
border-radius: 0 0.25rem 0.25rem 0;
@ -295,7 +289,6 @@
.card--vuln .card__title {
font-size: 28px;
margin-top: 0;
margin-right: 100px; /* Ensure space for the risk score */
}
.card--vuln .card__cta p {
@ -303,30 +296,6 @@
text-align: right;
}
.risk-score-display {
position: absolute;
top: 1.5em;
right: 1.5em;
text-align: right;
z-index: 10;
}
.risk-score-display__label {
font-size: 0.7em;
font-weight: bold;
color: #586069;
text-transform: uppercase;
line-height: 1;
margin-bottom: 3px;
}
.risk-score-display__value {
font-size: 1.9em;
font-weight: 600;
color: #24292e;
line-height: 1;
}
.source-panel {
clear: both;
display: flex;
@ -487,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:32:25 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:28:55 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>
@ -513,10 +482,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -845,10 +812,8 @@
<h2 class="card__title">LGPL-3.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -911,10 +876,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -973,10 +936,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1037,10 +998,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1254,10 +1213,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1429,10 +1386,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1491,10 +1446,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1565,10 +1518,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1637,10 +1588,8 @@
<h2 class="card__title">Regular Expression Denial of Service (ReDoS)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1784,10 +1733,8 @@
<h2 class="card__title">Regular Expression Denial of Service (ReDoS)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2125,10 +2072,8 @@
<h2 class="card__title">Arbitrary Code Injection</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -2202,10 +2147,8 @@
<h2 class="card__title">Insecure Randomness</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -2274,10 +2217,8 @@
<h2 class="card__title">Regular Expression Denial of Service (ReDoS)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -201,15 +201,6 @@
padding: 1.5em;
}
.card__labels {
position: absolute;
top: 1.1em;
left: 0;
display: flex;
align-items: center;
gap: 8px;
}
.card .label {
background-color: #767676;
border: 2px solid #767676;
@ -267,7 +258,10 @@
padding-top: 4em;
}
.card--vuln .card__labels > .label:first-child {
.card--vuln .label {
left: 0;
position: absolute;
top: 1.1em;
padding-left: 1.9em;
padding-right: 1.9em;
border-radius: 0 0.25rem 0.25rem 0;
@ -295,7 +289,6 @@
.card--vuln .card__title {
font-size: 28px;
margin-top: 0;
margin-right: 100px; /* Ensure space for the risk score */
}
.card--vuln .card__cta p {
@ -303,30 +296,6 @@
text-align: right;
}
.risk-score-display {
position: absolute;
top: 1.5em;
right: 1.5em;
text-align: right;
z-index: 10;
}
.risk-score-display__label {
font-size: 0.7em;
font-weight: bold;
color: #586069;
text-transform: uppercase;
line-height: 1;
margin-bottom: 3px;
}
.risk-score-display__value {
font-size: 1.9em;
font-weight: 600;
color: #24292e;
line-height: 1;
}
.source-panel {
clear: both;
display: flex;
@ -487,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:29:48 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:29:02 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>
@ -514,10 +483,8 @@
<h2 class="card__title">Incorrect Implementation of Authentication Algorithm</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--critical">
<span class="label__text">critical severity</span>
</div>
<div class="label label--critical">
<span class="label__text">critical severity</span>
</div>
<hr/>
@ -591,10 +558,8 @@
<h2 class="card__title">Access of Resource Using Incompatible Type (&#x27;Type Confusion&#x27;)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -750,10 +715,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -832,10 +795,8 @@
<h2 class="card__title">Server-side Request Forgery (SSRF)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -914,10 +875,8 @@
<h2 class="card__title">Denial of Service (DoS)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -999,10 +958,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -1071,10 +1028,8 @@
<h2 class="card__title">Asymmetric Resource Consumption (Amplification)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -1142,10 +1097,8 @@
<h2 class="card__title">Insertion of Sensitive Information into Log File</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1212,10 +1165,8 @@
<h2 class="card__title">Improper Validation of Syntactic Correctness of Input</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1265,7 +1216,7 @@
<p><a href="https://pkg.go.dev/golang.org/x/net/html">golang.org/x/net/html</a> is a package that implements an HTML5-compliant tokenizer and parser.</p>
<p>Affected versions of this package are vulnerable to Improper Validation of Syntactic Correctness of Input in the tokenizer in <code>token.go</code>, which incorrectly interprets tags as closing tags, allowing malicious input to be incorrectly processed and the DOM to be corrupted.</p>
<h2 id="details">Details</h2>
<p>Cross-site scripting (or XSS) is a code vulnerability that occurs when an attacker “injects” a malicious script into an otherwise trusted website. The injected script gets downloaded and executed by the end users browser when the user interacts with the compromised website.</p>
<p>A cross-site scripting attack occurs when the attacker tricks a legitimate web-based application or site to accept a request as originating from a trusted source.</p>
<p>This is done by escaping the context of the web application; the web application then delivers that data to its users along with other trusted dynamic content, without validating it. The browser unknowingly executes malicious script on the client side (through client-side languages; usually JavaScript or HTML) in order to perform actions that are otherwise typically blocked by the browsers Same Origin Policy.</p>
<p>Injecting malicious code is the most prevalent manner by which XSS is exploited; for this reason, escaping characters in order to prevent this manipulation is the top method for securing code against this vulnerability.</p>
<p>Escaping means that the application is coded to mark key characters, and particularly key characters included in user input, to prevent those characters from being interpreted in a dangerous context. For example, in HTML, <code>&lt;</code> can be coded as <code>&amp;lt</code>; and <code>&gt;</code> can be coded as <code>&amp;gt</code>; in order to be interpreted and displayed as themselves in text, while within the code itself, they are used for HTML tags. If malicious content is injected into an application that escapes special characters and that malicious content uses <code>&lt;</code> and <code>&gt;</code> as HTML tags, those characters are nonetheless not interpreted as HTML tags by the browser if theyve been correctly escaped in the application code and in this way the attempted attack is diverted.</p>
@ -1339,10 +1290,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1401,10 +1350,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1463,10 +1410,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1588,10 +1533,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1659,10 +1602,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1721,10 +1662,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1783,10 +1722,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1854,10 +1791,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1916,10 +1851,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1978,10 +1911,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2040,10 +1971,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2102,10 +2031,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2164,10 +2091,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2226,10 +2151,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2288,10 +2211,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2350,10 +2271,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2412,10 +2331,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2474,10 +2391,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2536,10 +2451,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2598,10 +2511,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2679,10 +2590,8 @@
<h2 class="card__title">CVE-2024-9143</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -2844,10 +2753,8 @@
<h2 class="card__title">CVE-2024-13176</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -3002,10 +2909,8 @@
<h2 class="card__title">CVE-2024-12797</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -3159,10 +3064,8 @@
<h2 class="card__title">CVE-2025-26519</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -201,15 +201,6 @@
padding: 1.5em;
}
.card__labels {
position: absolute;
top: 1.1em;
left: 0;
display: flex;
align-items: center;
gap: 8px;
}
.card .label {
background-color: #767676;
border: 2px solid #767676;
@ -267,7 +258,10 @@
padding-top: 4em;
}
.card--vuln .card__labels > .label:first-child {
.card--vuln .label {
left: 0;
position: absolute;
top: 1.1em;
padding-left: 1.9em;
padding-right: 1.9em;
border-radius: 0 0.25rem 0.25rem 0;
@ -295,7 +289,6 @@
.card--vuln .card__title {
font-size: 28px;
margin-top: 0;
margin-right: 100px; /* Ensure space for the risk score */
}
.card--vuln .card__cta p {
@ -303,30 +296,6 @@
text-align: right;
}
.risk-score-display {
position: absolute;
top: 1.5em;
right: 1.5em;
text-align: right;
z-index: 10;
}
.risk-score-display__label {
font-size: 0.7em;
font-weight: bold;
color: #586069;
text-transform: uppercase;
line-height: 1;
margin-bottom: 3px;
}
.risk-score-display__value {
font-size: 1.9em;
font-weight: 600;
color: #24292e;
line-height: 1;
}
.source-panel {
clear: both;
display: flex;
@ -487,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:32:39 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:29:07 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>
@ -520,10 +489,8 @@
<h2 class="card__title">Access of Resource Using Incompatible Type (&#x27;Type Confusion&#x27;)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -712,10 +679,8 @@
<h2 class="card__title">Use After Free</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -833,10 +798,8 @@
<h2 class="card__title">Use After Free</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -954,10 +917,8 @@
<h2 class="card__title">CVE-2024-4741</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -1149,10 +1110,8 @@
<h2 class="card__title">CVE-2024-5535</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -1380,10 +1339,8 @@
<h2 class="card__title">CVE-2024-9143</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -1578,10 +1535,8 @@
<h2 class="card__title">CVE-2024-13176</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -1769,10 +1724,8 @@
<h2 class="card__title">CVE-2024-12797</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -1959,10 +1912,8 @@
<h2 class="card__title">CVE-2025-26519</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -201,15 +201,6 @@
padding: 1.5em;
}
.card__labels {
position: absolute;
top: 1.1em;
left: 0;
display: flex;
align-items: center;
gap: 8px;
}
.card .label {
background-color: #767676;
border: 2px solid #767676;
@ -267,7 +258,10 @@
padding-top: 4em;
}
.card--vuln .card__labels > .label:first-child {
.card--vuln .label {
left: 0;
position: absolute;
top: 1.1em;
padding-left: 1.9em;
padding-right: 1.9em;
border-radius: 0 0.25rem 0.25rem 0;
@ -295,7 +289,6 @@
.card--vuln .card__title {
font-size: 28px;
margin-top: 0;
margin-right: 100px; /* Ensure space for the risk score */
}
.card--vuln .card__cta p {
@ -303,30 +296,6 @@
text-align: right;
}
.risk-score-display {
position: absolute;
top: 1.5em;
right: 1.5em;
text-align: right;
z-index: 10;
}
.risk-score-display__label {
font-size: 0.7em;
font-weight: bold;
color: #586069;
text-transform: uppercase;
line-height: 1;
margin-bottom: 3px;
}
.risk-score-display__value {
font-size: 1.9em;
font-weight: 600;
color: #24292e;
line-height: 1;
}
.source-panel {
clear: both;
display: flex;
@ -487,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:32:45 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:29:12 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>
@ -512,10 +481,8 @@
<h2 class="card__title">CVE-2024-9143</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -699,10 +666,8 @@
<h2 class="card__title">CVE-2024-13176</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -879,10 +844,8 @@
<h2 class="card__title">CVE-2024-12797</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -1058,10 +1021,8 @@
<h2 class="card__title">CVE-2025-26519</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -201,15 +201,6 @@
padding: 1.5em;
}
.card__labels {
position: absolute;
top: 1.1em;
left: 0;
display: flex;
align-items: center;
gap: 8px;
}
.card .label {
background-color: #767676;
border: 2px solid #767676;
@ -267,7 +258,10 @@
padding-top: 4em;
}
.card--vuln .card__labels > .label:first-child {
.card--vuln .label {
left: 0;
position: absolute;
top: 1.1em;
padding-left: 1.9em;
padding-right: 1.9em;
border-radius: 0 0.25rem 0.25rem 0;
@ -295,7 +289,6 @@
.card--vuln .card__title {
font-size: 28px;
margin-top: 0;
margin-right: 100px; /* Ensure space for the risk score */
}
.card--vuln .card__cta p {
@ -303,30 +296,6 @@
text-align: right;
}
.risk-score-display {
position: absolute;
top: 1.5em;
right: 1.5em;
text-align: right;
z-index: 10;
}
.risk-score-display__label {
font-size: 0.7em;
font-weight: bold;
color: #586069;
text-transform: uppercase;
line-height: 1;
margin-bottom: 3px;
}
.risk-score-display__value {
font-size: 1.9em;
font-weight: 600;
color: #24292e;
line-height: 1;
}
.source-panel {
clear: both;
display: flex;
@ -487,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:33:11 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:29:36 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>
@ -512,10 +481,8 @@
<h2 class="card__title">CVE-2024-9143</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -699,10 +666,8 @@
<h2 class="card__title">CVE-2024-13176</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -879,10 +844,8 @@
<h2 class="card__title">CVE-2024-12797</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -1058,10 +1021,8 @@
<h2 class="card__title">CVE-2025-26519</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>

File diff suppressed because it is too large Load diff

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:31:53 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:28:26 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>
@ -2861,7 +2861,7 @@
</li>
<li class="card__meta__item">
Line number: 26467
Line number: 26461
</li>
</ul>

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:32:03 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:28:36 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>
@ -2815,7 +2815,7 @@
</li>
<li class="card__meta__item">
Line number: 2354
Line number: 2348
</li>
</ul>

View file

@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Snyk test report</title>
<meta name="description" content="15 known vulnerabilities found in 105 vulnerable dependency paths.">
<meta name="description" content="17 known vulnerabilities found in 108 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">
@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -201,15 +201,6 @@
padding: 1.5em;
}
.card__labels {
position: absolute;
top: 1.1em;
left: 0;
display: flex;
align-items: center;
gap: 8px;
}
.card .label {
background-color: #767676;
border: 2px solid #767676;
@ -267,7 +258,10 @@
padding-top: 4em;
}
.card--vuln .card__labels > .label:first-child {
.card--vuln .label {
left: 0;
position: absolute;
top: 1.1em;
padding-left: 1.9em;
padding-right: 1.9em;
border-radius: 0 0.25rem 0.25rem 0;
@ -295,7 +289,6 @@
.card--vuln .card__title {
font-size: 28px;
margin-top: 0;
margin-right: 100px; /* Ensure space for the risk score */
}
.card--vuln .card__cta p {
@ -303,30 +296,6 @@
text-align: right;
}
.risk-score-display {
position: absolute;
top: 1.5em;
right: 1.5em;
text-align: right;
z-index: 10;
}
.risk-score-display__label {
font-size: 0.7em;
font-weight: bold;
color: #586069;
text-transform: uppercase;
line-height: 1;
margin-bottom: 3px;
}
.risk-score-display__value {
font-size: 1.9em;
font-weight: 600;
color: #24292e;
line-height: 1;
}
.source-panel {
clear: both;
display: flex;
@ -487,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:29:39 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:26:16 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>
@ -499,9 +468,9 @@
</div>
<div class="meta-counts">
<div class="meta-count"><span>15</span> <span>known vulnerabilities</span></div>
<div class="meta-count"><span>105 vulnerable dependency paths</span></div>
<div class="meta-count"><span>2085</span> <span>dependencies</span></div>
<div class="meta-count"><span>17</span> <span>known vulnerabilities</span></div>
<div class="meta-count"><span>108 vulnerable dependency paths</span></div>
<div class="meta-count"><span>2079</span> <span>dependencies</span></div>
</div><!-- .meta-counts -->
</div><!-- .layout-container--short -->
</header><!-- .project__header -->
@ -513,10 +482,8 @@
<h2 class="card__title">Prototype Pollution</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -681,10 +648,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -1031,10 +996,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -1262,10 +1225,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -1804,15 +1765,158 @@
<p><a href="https://snyk.io/vuln/SNYK-GOLANG-GITHUBCOMEXPRLANGEXPRCONF-9460818">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">LGPL-3.0 license</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: /argo-cd/argoproj/argo-cd/v3 <span class="list-paths__item__arrow"></span> go.mod
</li>
<li class="card__meta__item">
Package Manager: golang
</li>
<li class="card__meta__item">
Module:
gopkg.in/retry.v1
</li>
<li class="card__meta__item">Introduced through:
github.com/argoproj/argo-cd/v3@0.0.0, github.com/Azure/kubelogin/pkg/token@0.1.9 and others
</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>:
github.com/argoproj/argo-cd/v3@0.0.0
<span class="list-paths__item__arrow"></span>
github.com/Azure/kubelogin/pkg/token@0.1.9
<span class="list-paths__item__arrow"></span>
github.com/Azure/kubelogin/pkg/internal/token@0.1.9
<span class="list-paths__item__arrow"></span>
gopkg.in/retry.v1@1.0.3
</span>
</li>
</ul><!-- .list-paths -->
</div><!-- .card__section -->
<hr/>
<!-- Overview -->
<p>LGPL-3.0 license</p>
<hr/>
<div class="cta card__cta">
<p><a href="https://snyk.io/vuln/snyk:lic:golang:gopkg.in:retry.v1:LGPL-3.0">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">Unexpected Status Code or Return Value</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: /argo-cd/argoproj/argo-cd/v3 <span class="list-paths__item__arrow"></span> go.mod
</li>
<li class="card__meta__item">
Package Manager: golang
</li>
<li class="card__meta__item">
Vulnerable module:
github.com/redis/go-redis/v9
</li>
<li class="card__meta__item">Introduced through:
github.com/argoproj/argo-cd/v3@0.0.0 and github.com/redis/go-redis/v9@9.7.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>:
github.com/argoproj/argo-cd/v3@0.0.0
<span class="list-paths__item__arrow"></span>
github.com/redis/go-redis/v9@9.7.1
</span>
</li>
<li>
<span class="list-paths__item__introduced"><em>Introduced through</em>:
github.com/argoproj/argo-cd/v3@0.0.0
<span class="list-paths__item__arrow"></span>
github.com/go-redis/cache/v9@9.0.0
<span class="list-paths__item__arrow"></span>
github.com/redis/go-redis/v9@9.7.1
</span>
</li>
</ul><!-- .list-paths -->
</div><!-- .card__section -->
<hr/>
<!-- Overview -->
<h2 id="overview">Overview</h2>
<p>Affected versions of this package are vulnerable to Unexpected Status Code or Return Value in <code>initConn()</code>, which causes out of order responses when <code>CLIENT SETINFO</code> times out while establishing a connection.</p>
<h2 id="workaround">Workaround</h2>
<p>This vulnerability can be avoided by setting <code>DisableIndentity</code> to true when initializing a client.</p>
<h2 id="remediation">Remediation</h2>
<p>Upgrade <code>github.com/redis/go-redis/v9</code> to version 9.5.5, 9.6.3, 9.7.2 or higher.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/redis/go-redis/commit/d236865b0cfa1b752ea4b7da666b1fdcd0acebb6">GitHub Commit</a></li>
<li><a href="https://github.com/redis/go-redis/pull/3295">GitHub PR</a></li>
</ul>
<hr/>
<div class="cta card__cta">
<p><a href="https://snyk.io/vuln/SNYK-GOLANG-GITHUBCOMREDISGOREDISV9-9486471">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">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1871,10 +1975,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1935,10 +2037,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2152,10 +2252,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2327,10 +2425,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2389,10 +2485,8 @@
<h2 class="card__title">Regular Expression Denial of Service (ReDoS)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2536,10 +2630,8 @@
<h2 class="card__title">Regular Expression Denial of Service (ReDoS)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2877,10 +2969,8 @@
<h2 class="card__title">Arbitrary Code Injection</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -2954,10 +3044,8 @@
<h2 class="card__title">Insecure Randomness</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -3026,10 +3114,8 @@
<h2 class="card__title">Cross-site Scripting (XSS)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -3099,7 +3185,7 @@
);
</code></pre>
<h2 id="details">Details</h2>
<p>Cross-site scripting (or XSS) is a code vulnerability that occurs when an attacker “injects” a malicious script into an otherwise trusted website. The injected script gets downloaded and executed by the end users browser when the user interacts with the compromised website.</p>
<p>A cross-site scripting attack occurs when the attacker tricks a legitimate web-based application or site to accept a request as originating from a trusted source.</p>
<p>This is done by escaping the context of the web application; the web application then delivers that data to its users along with other trusted dynamic content, without validating it. The browser unknowingly executes malicious script on the client side (through client-side languages; usually JavaScript or HTML) in order to perform actions that are otherwise typically blocked by the browsers Same Origin Policy.</p>
<p>Injecting malicious code is the most prevalent manner by which XSS is exploited; for this reason, escaping characters in order to prevent this manipulation is the top method for securing code against this vulnerability.</p>
<p>Escaping means that the application is coded to mark key characters, and particularly key characters included in user input, to prevent those characters from being interpreted in a dangerous context. For example, in HTML, <code>&lt;</code> can be coded as <code>&amp;lt</code>; and <code>&gt;</code> can be coded as <code>&amp;gt</code>; in order to be interpreted and displayed as themselves in text, while within the code itself, they are used for HTML tags. If malicious content is injected into an application that escapes special characters and that malicious content uses <code>&lt;</code> and <code>&gt;</code> as HTML tags, those characters are nonetheless not interpreted as HTML tags by the browser if theyve been correctly escaped in the application code and in this way the attempted attack is diverted.</p>
@ -3173,10 +3259,8 @@
<h2 class="card__title">Regular Expression Denial of Service (ReDoS)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -201,15 +201,6 @@
padding: 1.5em;
}
.card__labels {
position: absolute;
top: 1.1em;
left: 0;
display: flex;
align-items: center;
gap: 8px;
}
.card .label {
background-color: #767676;
border: 2px solid #767676;
@ -267,7 +258,10 @@
padding-top: 4em;
}
.card--vuln .card__labels > .label:first-child {
.card--vuln .label {
left: 0;
position: absolute;
top: 1.1em;
padding-left: 1.9em;
padding-right: 1.9em;
border-radius: 0 0.25rem 0.25rem 0;
@ -295,7 +289,6 @@
.card--vuln .card__title {
font-size: 28px;
margin-top: 0;
margin-right: 100px; /* Ensure space for the risk score */
}
.card--vuln .card__cta p {
@ -303,30 +296,6 @@
text-align: right;
}
.risk-score-display {
position: absolute;
top: 1.5em;
right: 1.5em;
text-align: right;
z-index: 10;
}
.risk-score-display__label {
font-size: 0.7em;
font-weight: bold;
color: #586069;
text-transform: uppercase;
line-height: 1;
margin-bottom: 3px;
}
.risk-score-display__value {
font-size: 1.9em;
font-weight: 600;
color: #24292e;
line-height: 1;
}
.source-panel {
clear: both;
display: flex;
@ -487,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:32:33 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:26:25 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following paths:</span>
@ -514,10 +483,8 @@
<h2 class="card__title">Incorrect Implementation of Authentication Algorithm</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--critical">
<span class="label__text">critical severity</span>
</div>
<div class="label label--critical">
<span class="label__text">critical severity</span>
</div>
<hr/>
@ -591,10 +558,8 @@
<h2 class="card__title">Access of Resource Using Incompatible Type (&#x27;Type Confusion&#x27;)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -750,10 +715,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -832,10 +795,8 @@
<h2 class="card__title">Server-side Request Forgery (SSRF)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -914,10 +875,8 @@
<h2 class="card__title">Denial of Service (DoS)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -999,10 +958,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -1071,10 +1028,8 @@
<h2 class="card__title">Asymmetric Resource Consumption (Amplification)</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<div class="label label--high">
<span class="label__text">high severity</span>
</div>
<hr/>
@ -1142,10 +1097,8 @@
<h2 class="card__title">Insertion of Sensitive Information into Log File</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1212,10 +1165,8 @@
<h2 class="card__title">Improper Validation of Syntactic Correctness of Input</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1265,7 +1216,7 @@
<p><a href="https://pkg.go.dev/golang.org/x/net/html">golang.org/x/net/html</a> is a package that implements an HTML5-compliant tokenizer and parser.</p>
<p>Affected versions of this package are vulnerable to Improper Validation of Syntactic Correctness of Input in the tokenizer in <code>token.go</code>, which incorrectly interprets tags as closing tags, allowing malicious input to be incorrectly processed and the DOM to be corrupted.</p>
<h2 id="details">Details</h2>
<p>Cross-site scripting (or XSS) is a code vulnerability that occurs when an attacker “injects” a malicious script into an otherwise trusted website. The injected script gets downloaded and executed by the end users browser when the user interacts with the compromised website.</p>
<p>A cross-site scripting attack occurs when the attacker tricks a legitimate web-based application or site to accept a request as originating from a trusted source.</p>
<p>This is done by escaping the context of the web application; the web application then delivers that data to its users along with other trusted dynamic content, without validating it. The browser unknowingly executes malicious script on the client side (through client-side languages; usually JavaScript or HTML) in order to perform actions that are otherwise typically blocked by the browsers Same Origin Policy.</p>
<p>Injecting malicious code is the most prevalent manner by which XSS is exploited; for this reason, escaping characters in order to prevent this manipulation is the top method for securing code against this vulnerability.</p>
<p>Escaping means that the application is coded to mark key characters, and particularly key characters included in user input, to prevent those characters from being interpreted in a dangerous context. For example, in HTML, <code>&lt;</code> can be coded as <code>&amp;lt</code>; and <code>&gt;</code> can be coded as <code>&amp;gt</code>; in order to be interpreted and displayed as themselves in text, while within the code itself, they are used for HTML tags. If malicious content is injected into an application that escapes special characters and that malicious content uses <code>&lt;</code> and <code>&gt;</code> as HTML tags, those characters are nonetheless not interpreted as HTML tags by the browser if theyve been correctly escaped in the application code and in this way the attempted attack is diverted.</p>
@ -1339,10 +1290,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1401,10 +1350,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1463,10 +1410,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1588,10 +1533,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1659,10 +1602,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1721,10 +1662,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1783,10 +1722,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1854,10 +1791,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1916,10 +1851,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -1978,10 +1911,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2040,10 +1971,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2102,10 +2031,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2164,10 +2091,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2226,10 +2151,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2288,10 +2211,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2350,10 +2271,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2412,10 +2331,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2474,10 +2391,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2536,10 +2451,8 @@
<h2 class="card__title">MPL-2.0 license</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2598,10 +2511,8 @@
<h2 class="card__title">Allocation of Resources Without Limits or Throttling</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<div class="label label--medium">
<span class="label__text">medium severity</span>
</div>
<hr/>
@ -2679,10 +2590,8 @@
<h2 class="card__title">CVE-2024-9143</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -2844,10 +2753,8 @@
<h2 class="card__title">CVE-2024-13176</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -3002,10 +2909,8 @@
<h2 class="card__title">CVE-2024-12797</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
@ -3159,10 +3064,8 @@
<h2 class="card__title">CVE-2025-26519</h2>
<div class="card__section">
<div class="card__labels">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>

View file

@ -141,7 +141,7 @@
padding-top: 2em;
}
.project__header {
background-color: #030328;
background-color: #4b45a9;
color: #fff;
margin-bottom: -1px;
padding-top: 1em;
@ -201,15 +201,6 @@
padding: 1.5em;
}
.card__labels {
position: absolute;
top: 1.1em;
left: 0;
display: flex;
align-items: center;
gap: 8px;
}
.card .label {
background-color: #767676;
border: 2px solid #767676;
@ -267,7 +258,10 @@
padding-top: 4em;
}
.card--vuln .card__labels > .label:first-child {
.card--vuln .label {
left: 0;
position: absolute;
top: 1.1em;
padding-left: 1.9em;
padding-right: 1.9em;
border-radius: 0 0.25rem 0.25rem 0;
@ -295,7 +289,6 @@
.card--vuln .card__title {
font-size: 28px;
margin-top: 0;
margin-right: 100px; /* Ensure space for the risk score */
}
.card--vuln .card__cta p {
@ -303,30 +296,6 @@
text-align: right;
}
.risk-score-display {
position: absolute;
top: 1.5em;
right: 1.5em;
text-align: right;
z-index: 10;
}
.risk-score-display__label {
font-size: 0.7em;
font-weight: bold;
color: #586069;
text-transform: uppercase;
line-height: 1;
margin-bottom: 3px;
}
.risk-score-display__value {
font-size: 1.9em;
font-weight: 600;
color: #24292e;
line-height: 1;
}
.source-panel {
clear: both;
display: flex;
@ -487,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">July 13th 2025, 12:29:51 am (UTC+00:00)</p>
<p class="timestamp">June 15th 2025, 12:26:28 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>

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