mirror of
https://github.com/argoproj/argo-cd
synced 2026-04-21 17:07:16 +00:00
Signed-off-by: Patroklos Papapetrou <ppapapetrou76@gmail.com> Co-authored-by: Papapetrou Patroklos <1743100+ppapapetrou76@users.noreply.github.com> Co-authored-by: Jann Fischer <jann@mistrust.net>
This commit is contained in:
parent
a77c1501fe
commit
2512512b0c
2 changed files with 230 additions and 8 deletions
|
|
@ -73,6 +73,9 @@ const (
|
|||
ReconcileRequeueOnValidationError = time.Minute * 3
|
||||
ReverseDeletionOrder = "Reverse"
|
||||
AllAtOnceDeletionOrder = "AllAtOnce"
|
||||
revisionAndSpecChangedMsg = "Application has pending changes (revision and spec differ), setting status to Waiting"
|
||||
revisionChangedMsg = "Application has pending changes, setting status to Waiting"
|
||||
specChangedMsg = "Application has pending changes (spec differs), setting status to Waiting"
|
||||
)
|
||||
|
||||
var defaultPreservedFinalizers = []string{
|
||||
|
|
@ -960,7 +963,7 @@ func (r *ApplicationSetReconciler) removeOwnerReferencesOnDeleteAppSet(ctx conte
|
|||
func (r *ApplicationSetReconciler) performProgressiveSyncs(ctx context.Context, logCtx *log.Entry, appset argov1alpha1.ApplicationSet, applications []argov1alpha1.Application, desiredApplications []argov1alpha1.Application) (map[string]bool, error) {
|
||||
appDependencyList, appStepMap := r.buildAppDependencyList(logCtx, appset, desiredApplications)
|
||||
|
||||
_, err := r.updateApplicationSetApplicationStatus(ctx, logCtx, &appset, applications, appStepMap)
|
||||
_, err := r.updateApplicationSetApplicationStatus(ctx, logCtx, &appset, applications, desiredApplications, appStepMap)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update applicationset app status: %w", err)
|
||||
}
|
||||
|
|
@ -1139,10 +1142,16 @@ func getAppStep(appName string, appStepMap map[string]int) int {
|
|||
}
|
||||
|
||||
// check the status of each Application's status and promote Applications to the next status if needed
|
||||
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx context.Context, logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, applications []argov1alpha1.Application, appStepMap map[string]int) ([]argov1alpha1.ApplicationSetApplicationStatus, error) {
|
||||
func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx context.Context, logCtx *log.Entry, applicationSet *argov1alpha1.ApplicationSet, applications []argov1alpha1.Application, desiredApplications []argov1alpha1.Application, appStepMap map[string]int) ([]argov1alpha1.ApplicationSetApplicationStatus, error) {
|
||||
now := metav1.Now()
|
||||
appStatuses := make([]argov1alpha1.ApplicationSetApplicationStatus, 0, len(applications))
|
||||
|
||||
// Build a map of desired applications for quick lookup
|
||||
desiredAppsMap := make(map[string]*argov1alpha1.Application)
|
||||
for i := range desiredApplications {
|
||||
desiredAppsMap[desiredApplications[i].Name] = &desiredApplications[i]
|
||||
}
|
||||
|
||||
for _, app := range applications {
|
||||
appHealthStatus := app.Status.Health.Status
|
||||
appSyncStatus := app.Status.Sync.Status
|
||||
|
|
@ -1177,10 +1186,27 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
|
|||
newAppStatus := currentAppStatus.DeepCopy()
|
||||
newAppStatus.Step = strconv.Itoa(getAppStep(newAppStatus.Application, appStepMap))
|
||||
|
||||
if !reflect.DeepEqual(currentAppStatus.TargetRevisions, app.Status.GetRevisions()) {
|
||||
// A new version is available in the application and we need to re-sync the application
|
||||
revisionsChanged := !reflect.DeepEqual(currentAppStatus.TargetRevisions, app.Status.GetRevisions())
|
||||
|
||||
// Check if the desired Application spec differs from the current Application spec
|
||||
specChanged := false
|
||||
if desiredApp, ok := desiredAppsMap[app.Name]; ok {
|
||||
// Compare the desired spec with the current spec to detect non-Git changes
|
||||
// This will catch changes to generator parameters like image tags, helm values, etc.
|
||||
specChanged = !cmp.Equal(desiredApp.Spec, app.Spec, cmpopts.EquateEmpty(), cmpopts.EquateComparable(argov1alpha1.ApplicationDestination{}))
|
||||
}
|
||||
|
||||
if revisionsChanged || specChanged {
|
||||
newAppStatus.TargetRevisions = app.Status.GetRevisions()
|
||||
newAppStatus.Message = "Application has pending changes, setting status to Waiting"
|
||||
|
||||
switch {
|
||||
case revisionsChanged && specChanged:
|
||||
newAppStatus.Message = revisionAndSpecChangedMsg
|
||||
case revisionsChanged:
|
||||
newAppStatus.Message = revisionChangedMsg
|
||||
default:
|
||||
newAppStatus.Message = specChangedMsg
|
||||
}
|
||||
newAppStatus.Status = argov1alpha1.ProgressiveSyncWaiting
|
||||
newAppStatus.LastTransitionTime = &now
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4637,6 +4637,12 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
newAppWithSpec := func(name string, health health.HealthStatusCode, sync v1alpha1.SyncStatusCode, revision string, opState *v1alpha1.OperationState, spec v1alpha1.ApplicationSpec) v1alpha1.Application {
|
||||
app := newApp(name, health, sync, revision, opState)
|
||||
app.Spec = spec
|
||||
return app
|
||||
}
|
||||
|
||||
newOperationState := func(phase common.OperationPhase) *v1alpha1.OperationState {
|
||||
finishedAt := &metav1.Time{Time: time.Now().Add(-1 * time.Second)}
|
||||
if !phase.Completed() {
|
||||
|
|
@ -4653,6 +4659,7 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
|||
name string
|
||||
appSet v1alpha1.ApplicationSet
|
||||
apps []v1alpha1.Application
|
||||
desiredApps []v1alpha1.Application
|
||||
appStepMap map[string]int
|
||||
expectedAppStatus []v1alpha1.ApplicationSetApplicationStatus
|
||||
}{
|
||||
|
|
@ -4806,14 +4813,14 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
|||
expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "Application has pending changes, setting status to Waiting",
|
||||
Message: revisionChangedMsg,
|
||||
Status: v1alpha1.ProgressiveSyncWaiting,
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"next"},
|
||||
},
|
||||
{
|
||||
Application: "app2-multisource",
|
||||
Message: "Application has pending changes, setting status to Waiting",
|
||||
Message: revisionChangedMsg,
|
||||
Status: v1alpha1.ProgressiveSyncWaiting,
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"next"},
|
||||
|
|
@ -5253,6 +5260,191 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "detects spec changes when image tag changes in generator (same Git revision)",
|
||||
appSet: newDefaultAppSet(2, []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "",
|
||||
Status: v1alpha1.ProgressiveSyncHealthy,
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"abc123"},
|
||||
},
|
||||
}),
|
||||
apps: []v1alpha1.Application{
|
||||
newAppWithSpec("app1", health.HealthStatusHealthy, v1alpha1.SyncStatusCodeOutOfSync, "abc123", nil, // Changed to OutOfSync
|
||||
v1alpha1.ApplicationSpec{
|
||||
Source: &v1alpha1.ApplicationSource{
|
||||
RepoURL: "https://example.com/repo.git",
|
||||
TargetRevision: "master",
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
Parameters: []v1alpha1.HelmParameter{
|
||||
{Name: "image.tag", Value: "v1.0.0"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Destination: v1alpha1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "default",
|
||||
},
|
||||
}),
|
||||
},
|
||||
desiredApps: []v1alpha1.Application{
|
||||
newAppWithSpec("app1", health.HealthStatusHealthy, v1alpha1.SyncStatusCodeOutOfSync, "abc123", nil, // Changed to OutOfSync
|
||||
v1alpha1.ApplicationSpec{
|
||||
Source: &v1alpha1.ApplicationSource{
|
||||
RepoURL: "https://example.com/repo.git",
|
||||
TargetRevision: "master",
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
Parameters: []v1alpha1.HelmParameter{
|
||||
{Name: "image.tag", Value: "v2.0.0"}, // Different value
|
||||
},
|
||||
},
|
||||
},
|
||||
Destination: v1alpha1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "default",
|
||||
},
|
||||
}),
|
||||
},
|
||||
appStepMap: map[string]int{
|
||||
"app1": 0,
|
||||
},
|
||||
expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: specChangedMsg,
|
||||
Status: v1alpha1.ProgressiveSyncWaiting,
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"abc123"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "does not detect changes when spec is identical (same Git revision)",
|
||||
appSet: newDefaultAppSet(2, []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "",
|
||||
Status: v1alpha1.ProgressiveSyncHealthy,
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"abc123"},
|
||||
},
|
||||
}),
|
||||
apps: []v1alpha1.Application{
|
||||
newAppWithSpec("app1", health.HealthStatusHealthy, v1alpha1.SyncStatusCodeSynced, "abc123", nil,
|
||||
v1alpha1.ApplicationSpec{
|
||||
Source: &v1alpha1.ApplicationSource{
|
||||
RepoURL: "https://example.com/repo.git",
|
||||
TargetRevision: "master",
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
Parameters: []v1alpha1.HelmParameter{
|
||||
{Name: "image.tag", Value: "v1.0.0"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Destination: v1alpha1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "default",
|
||||
},
|
||||
}),
|
||||
},
|
||||
appStepMap: map[string]int{
|
||||
"app1": 0,
|
||||
},
|
||||
// Desired apps have identical spec
|
||||
desiredApps: []v1alpha1.Application{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Source: &v1alpha1.ApplicationSource{
|
||||
RepoURL: "https://example.com/repo.git",
|
||||
TargetRevision: "master",
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
Parameters: []v1alpha1.HelmParameter{
|
||||
{Name: "image.tag", Value: "v1.0.0"}, // Same value
|
||||
},
|
||||
},
|
||||
},
|
||||
Destination: v1alpha1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "default",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "",
|
||||
Status: v1alpha1.ProgressiveSyncHealthy,
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"abc123"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "detects both spec and revision changes",
|
||||
appSet: newDefaultAppSet(2, []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "",
|
||||
Status: v1alpha1.ProgressiveSyncHealthy,
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"abc123"}, // OLD revision in status
|
||||
},
|
||||
}),
|
||||
apps: []v1alpha1.Application{
|
||||
newAppWithSpec("app1", health.HealthStatusHealthy, v1alpha1.SyncStatusCodeOutOfSync, "def456", nil, // NEW revision, but OutOfSync
|
||||
v1alpha1.ApplicationSpec{
|
||||
Source: &v1alpha1.ApplicationSource{
|
||||
RepoURL: "https://example.com/repo.git",
|
||||
TargetRevision: "master",
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
Parameters: []v1alpha1.HelmParameter{
|
||||
{Name: "image.tag", Value: "v1.0.0"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Destination: v1alpha1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "default",
|
||||
},
|
||||
}),
|
||||
},
|
||||
desiredApps: []v1alpha1.Application{
|
||||
newAppWithSpec("app1", health.HealthStatusHealthy, v1alpha1.SyncStatusCodeOutOfSync, "def456", nil,
|
||||
v1alpha1.ApplicationSpec{
|
||||
Source: &v1alpha1.ApplicationSource{
|
||||
RepoURL: "https://example.com/repo.git",
|
||||
TargetRevision: "master",
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
Parameters: []v1alpha1.HelmParameter{
|
||||
{Name: "image.tag", Value: "v2.0.0"}, // Changed value
|
||||
},
|
||||
},
|
||||
},
|
||||
Destination: v1alpha1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "default",
|
||||
},
|
||||
}),
|
||||
},
|
||||
appStepMap: map[string]int{
|
||||
"app1": 0,
|
||||
},
|
||||
expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: revisionAndSpecChangedMsg,
|
||||
Status: v1alpha1.ProgressiveSyncWaiting,
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"def456"},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(cc.name, func(t *testing.T) {
|
||||
kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...)
|
||||
|
|
@ -5272,7 +5464,11 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
|||
Metrics: metrics,
|
||||
}
|
||||
|
||||
appStatuses, err := r.updateApplicationSetApplicationStatus(t.Context(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.apps, cc.appStepMap)
|
||||
desiredApps := cc.desiredApps
|
||||
if desiredApps == nil {
|
||||
desiredApps = cc.apps
|
||||
}
|
||||
appStatuses, err := r.updateApplicationSetApplicationStatus(t.Context(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.apps, desiredApps, cc.appStepMap)
|
||||
|
||||
// opt out of testing the LastTransitionTime is accurate
|
||||
for i := range appStatuses {
|
||||
|
|
|
|||
Loading…
Reference in a new issue