Skip to content

Commit c3ad8e3

Browse files
authored
fix: revert updates to autonomous apps on the control plane (#567)
Signed-off-by: Chetan Banavikalmutt <[email protected]>
1 parent 86f71a0 commit c3ad8e3

File tree

6 files changed

+671
-16
lines changed

6 files changed

+671
-16
lines changed

internal/manager/application/application.go

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -626,9 +626,9 @@ func (m *ApplicationManager) RevertManagedAppChanges(ctx context.Context, app *v
626626
sourceUID, exists := app.Annotations[manager.SourceUIDAnnotation]
627627
if exists && m.mode == manager.ManagerModeManaged {
628628
if cachedAppSpec, ok := appCache.GetApplicationSpec(ty.UID(sourceUID), logCtx); ok {
629-
logCtx.Debugf("Application: %s is available in agent cache", app.Name)
629+
logCtx.Debugf("Application %s is available in agent cache", app.Name)
630630

631-
if diff := reflect.DeepEqual(cachedAppSpec, app.Spec); !diff {
631+
if isEqual := reflect.DeepEqual(cachedAppSpec, app.Spec); !isEqual {
632632
app.Spec = cachedAppSpec
633633
logCtx.Infof("Reverting modifications done in application: %s", app.Name)
634634
if _, err := m.UpdateManagedApp(ctx, app); err != nil {
@@ -638,12 +638,45 @@ func (m *ApplicationManager) RevertManagedAppChanges(ctx context.Context, app *v
638638
return true
639639
}
640640
} else {
641-
logCtx.Debugf("Application: %s is not available in agent cache", app.Name)
641+
logCtx.Errorf("Application %s is not available in agent cache", app.Name)
642642
}
643643
}
644644
return false
645645
}
646646

647+
// RevertAutonomousAppChanges compares the actual spec with expected spec stored in cache,
648+
// if actual spec doesn't match with cache, then it is reverted to be in sync with cache, which is same as agent cluster.
649+
func (m *ApplicationManager) RevertAutonomousAppChanges(ctx context.Context, app *v1alpha1.Application) bool {
650+
logCtx := log().WithFields(logrus.Fields{
651+
"component": "RevertAutonomousAppChanges",
652+
"application": app.QualifiedName(),
653+
"resourceVersion": app.ResourceVersion,
654+
})
655+
656+
sourceUID, exists := app.Annotations[manager.SourceUIDAnnotation]
657+
if !exists {
658+
return false
659+
}
660+
661+
if cachedAppSpec, ok := appCache.GetApplicationSpec(ty.UID(sourceUID), logCtx); ok {
662+
logCtx.Debugf("Application %s is available in agent cache", app.Name)
663+
664+
if isEqual := reflect.DeepEqual(cachedAppSpec, app.Spec); !isEqual {
665+
app.Spec = cachedAppSpec
666+
logCtx.Infof("Reverting modifications to the application: %s", app.Name)
667+
if _, err := m.UpdateAutonomousApp(ctx, app.Namespace, app); err != nil {
668+
logCtx.Errorf("Unable to revert modifications done in application: %s. Error: %v", app.Name, err)
669+
return false
670+
}
671+
return true
672+
}
673+
} else {
674+
logCtx.Errorf("Application %s is not available in agent cache", app.Name)
675+
}
676+
677+
return false
678+
}
679+
647680
func log() *logrus.Entry {
648681
return logrus.WithField("component", "AppManager")
649682
}

principal/callbacks.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
package principal
1616

1717
import (
18+
"fmt"
19+
1820
"github.com/argoproj-labs/argocd-agent/internal/event"
1921
"github.com/argoproj-labs/argocd-agent/internal/manager"
2022
"github.com/argoproj-labs/argocd-agent/internal/manager/appproject"
@@ -25,6 +27,7 @@ import (
2527
corev1 "k8s.io/api/core/v1"
2628
"k8s.io/apimachinery/pkg/api/errors"
2729
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30+
k8stypes "k8s.io/apimachinery/pkg/types"
2831
)
2932

3033
// newAppCallback is executed when a new application event was emitted from
@@ -70,6 +73,26 @@ func (s *Server) updateAppCallback(old *v1alpha1.Application, new *v1alpha1.Appl
7073
"event": "application_update",
7174
"application_name": old.Name,
7275
})
76+
77+
if isResourceFromAutonomousAgent(new) {
78+
// Remove finalizers from autonomous agent applications if it is being deleted
79+
if new.DeletionTimestamp != nil && len(new.Finalizers) > 0 {
80+
updated, err := s.appManager.RemoveFinalizers(s.ctx, new)
81+
if err != nil {
82+
logCtx.WithError(err).Error("Failed to remove finalizers from autonomous application")
83+
} else {
84+
logCtx.Debug("Removed finalizers from autonomous application to allow deletion")
85+
new = updated
86+
}
87+
}
88+
89+
// Revert modifications on autonomous agent applications
90+
if s.appManager.RevertAutonomousAppChanges(s.ctx, new) {
91+
logCtx.Trace("Modifications to the application are reverted")
92+
return
93+
}
94+
}
95+
7396
if s.appManager.IsChangeIgnored(new.QualifiedName(), new.ResourceVersion) {
7497
logCtx.WithField("resource_version", new.ResourceVersion).Debugf("Resource version has already been seen")
7598
return
@@ -103,6 +126,14 @@ func (s *Server) deleteAppCallback(outbound *v1alpha1.Application) {
103126
"application_name": outbound.Name,
104127
})
105128

129+
// Revert user-initiated deletion on autonomous agent applications
130+
if isResourceFromAutonomousAgent(outbound) {
131+
if s.revertUserInitiatedDeletion(outbound, logCtx) {
132+
logCtx.Trace("Deleted application is recreated")
133+
return
134+
}
135+
}
136+
106137
s.resources.Remove(outbound.Namespace, resources.NewResourceKeyFromApp(outbound))
107138

108139
if !s.queues.HasQueuePair(outbound.Namespace) {
@@ -586,3 +617,30 @@ func isResourceFromAutonomousAgent(resource metav1.Object) bool {
586617
_, ok := annotations[manager.SourceUIDAnnotation]
587618
return ok
588619
}
620+
621+
func (s *Server) revertUserInitiatedDeletion(outbound *v1alpha1.Application, logCtx *logrus.Entry) bool {
622+
appKey := fmt.Sprintf("%s/%s", outbound.Namespace, outbound.Name)
623+
624+
// Check if this deletion was expected (agent-initiated)
625+
if s.isExpectedDeletion(appKey) {
626+
logCtx.Debugf("Expected deletion from autonomous agent - allowing it to proceed")
627+
// This is a legitimate deletion from the agent, let it proceed normally
628+
return false
629+
}
630+
631+
logCtx.Warnf("Unauthorized deletion detected for autonomous agent application - recreating")
632+
// This is an unauthorized deletion (user-initiated), recreate the app
633+
app := outbound.DeepCopy()
634+
app.ResourceVersion = ""
635+
app.DeletionTimestamp = nil
636+
sourceUID := outbound.Annotations[manager.SourceUIDAnnotation]
637+
app.SetUID(k8stypes.UID(sourceUID))
638+
_, err := s.appManager.Create(s.ctx, app)
639+
if err != nil {
640+
logCtx.WithError(err).Error("failed to recreate application after unauthorized deletion")
641+
} else {
642+
logCtx.Infof("Recreated application %s after unauthorized deletion", outbound.Name)
643+
}
644+
645+
return true
646+
}

0 commit comments

Comments
 (0)