Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions acr_controller/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ type ACRController interface {
type applicationChangeRevisionController struct {
appBroadcaster Broadcaster
acrService service.ACRService
useAnnotations bool
}

func NewApplicationChangeRevisionController(appInformer cache.SharedIndexInformer, applicationServiceClient appclient.ApplicationClient, applicationClientset appclientset.Interface) ACRController {
func NewApplicationChangeRevisionController(appInformer cache.SharedIndexInformer, applicationServiceClient appclient.ApplicationClient, applicationClientset appclientset.Interface, useAnnotations bool) ACRController {
appBroadcaster := NewBroadcaster()
_, err := appInformer.AddEventHandler(appBroadcaster)
if err != nil {
Expand All @@ -35,6 +36,7 @@ func NewApplicationChangeRevisionController(appInformer cache.SharedIndexInforme
return &applicationChangeRevisionController{
appBroadcaster: appBroadcaster,
acrService: service.NewACRService(applicationClientset, applicationServiceClient),
useAnnotations: useAnnotations,
}
}

Expand All @@ -46,7 +48,7 @@ func (c *applicationChangeRevisionController) Run(ctx context.Context) {
return nil // ignore this event
}

return c.acrService.ChangeRevision(ctx, &a)
return c.acrService.ChangeRevision(ctx, &a, c.useAnnotations)
}

// TODO: move to abstraction
Expand Down
3 changes: 2 additions & 1 deletion acr_controller/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type ACRServerOpts struct {
ApplicationNamespaces []string
BaseHRef string
RootPath string
DisableAnnotations bool
}

type handlerSwitcher struct {
Expand Down Expand Up @@ -96,7 +97,7 @@ func (a *ACRServer) Init(ctx context.Context) {
}

func (a *ACRServer) RunController(ctx context.Context) {
controller := acr_controller.NewApplicationChangeRevisionController(a.appInformer, a.ApplicationServiceClient, a.applicationClientset)
controller := acr_controller.NewApplicationChangeRevisionController(a.appInformer, a.ApplicationServiceClient, a.applicationClientset, !a.DisableAnnotations)
go controller.Run(ctx)
}

Expand Down
138 changes: 104 additions & 34 deletions acr_controller/service/acr_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package service
import (
"context"
"encoding/json"
"fmt"
"sync"

log "github.com/sirupsen/logrus"
Expand All @@ -16,8 +17,15 @@ import (
appclientset "github.com/argoproj/argo-cd/v3/pkg/client/clientset/versioned"
)

const (
CHANGE_REVISION_ANN = "mrp-controller.argoproj.io/change-revision"
CHANGE_REVISIONS_ANN = "mrp-controller.argoproj.io/change-revisions"
GIT_REVISION_ANN = "mrp-controller.argoproj.io/git-revision"
GIT_REVISIONS_ANN = "mrp-controller.argoproj.io/git-revisions"
)

type ACRService interface {
ChangeRevision(ctx context.Context, application *application.Application) error
ChangeRevision(ctx context.Context, application *application.Application, useAnnotations bool) error
}

type acrService struct {
Expand Down Expand Up @@ -55,7 +63,7 @@ func getChangeRevision(app *application.Application) string {
return ""
}

func (c *acrService) ChangeRevision(ctx context.Context, a *application.Application) error {
func (c *acrService) ChangeRevision(ctx context.Context, a *application.Application, useAnnotations bool) error {
c.lock.Lock()
defer c.lock.Unlock()

Expand All @@ -73,36 +81,108 @@ func (c *acrService) ChangeRevision(ctx context.Context, a *application.Applicat
return nil
}

revision, err := c.calculateRevision(ctx, app)
currentRevision, previousRevision := c.getRevisions(ctx, a)
if currentRevision == "" {
c.logger.Infof("Got empty current revision for application %s, is it an unsupported multisource or helm repo based application?", app.Name)
return nil
}
revision, err := c.calculateRevision(ctx, app, currentRevision, previousRevision)
if err != nil {
return err
}

var revisions []string
if revision == nil || *revision == "" {
c.logger.Infof("Revision for application %s is empty", app.Name)
return nil
} else {
c.logger.Infof("Change revision for application %s is %s", app.Name, *revision)
revisions = []string{*revision}
}

c.logger.Infof("Change revision for application %s is %s", app.Name, *revision)

app, err = c.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace).Get(ctx, app.Name, metav1.GetOptions{})
if err != nil {
return err
}

revisions := []string{*revision}
patchMap := make(map[string]any, 2)

if app.Status.OperationState != nil && app.Status.OperationState.Operation.Sync != nil {
c.logger.Infof("Patch operation status for application %s", app.Name)
return c.patchOperationSyncResultWithChangeRevision(ctx, app, revisions)
if len(revisions) > 0 {
if app.Status.OperationState != nil && app.Status.OperationState.Operation.Sync != nil {
c.logger.Infof("Patch operation status for application %s", app.Name)
patchMap = c.patchOperationSyncResultWithChangeRevision(revisions)
} else {
c.logger.Infof("Patch operation for application %s", app.Name)
patchMap = c.patchOperationWithChangeRevision(revisions)
}
}
if useAnnotations {
err = c.addAnnotationPatch(patchMap, app, *revision, revisions, currentRevision, []string{currentRevision})
if err != nil {
return err
}
}
if len(patchMap) > 0 {
c.logger.Infof("Patching resource: %v", patchMap)
patch, err := json.Marshal(patchMap)
if err != nil {
return err
}
_, err = c.applicationClientset.ArgoprojV1alpha1().Applications(a.Namespace).Patch(ctx, a.Name, types.MergePatchType, patch, metav1.PatchOptions{})
return err
}
c.logger.Infof("No patch needed")
return nil
}

c.logger.Infof("Patch operation for application %s", app.Name)
return c.patchOperationWithChangeRevision(ctx, app, revisions)
func addPatchIfNeeded(annotations map[string]string, currentAnnotations map[string]string, key string, val string) {
currentVal, ok := currentAnnotations[key]
if !ok || currentVal != val {
annotations[key] = val
}
}

func (c *acrService) calculateRevision(ctx context.Context, a *application.Application) (*string, error) {
currentRevision, previousRevision := c.getRevisions(ctx, a)
func (c *acrService) addAnnotationPatch(m map[string]any,
a *application.Application,
changeRevision string,
changeRevisions []string,
gitRevision string,
gitRevisions []string,
) error {
c.logger.Infof("annotating application '%s', changeRevision=%s, changeRevisions=%v, gitRevision=%s, gitRevisions=%v", a.Name, changeRevision, changeRevisions, gitRevision, gitRevisions)
annotations := map[string]string{}
currentAnnotations := a.Annotations

if changeRevision != "" {
addPatchIfNeeded(annotations, currentAnnotations, CHANGE_REVISION_ANN, changeRevision)
}
if len(changeRevisions) > 0 {
changeRevisionsJSON, err := json.Marshal(changeRevisions)
if err != nil {
return fmt.Errorf("failed to marshall changeRevisions %v: %w", changeRevisions, err)
}
addPatchIfNeeded(annotations, currentAnnotations, CHANGE_REVISIONS_ANN, string(changeRevisionsJSON))
}
if gitRevision != "" {
addPatchIfNeeded(annotations, currentAnnotations, GIT_REVISION_ANN, gitRevision)
}
if len(gitRevisions) > 0 {
gitRevisionsJSON, err := json.Marshal(gitRevisions)
if err != nil {
return fmt.Errorf("failed to marshall gitRevisions %v: %w", gitRevisions, err)
}
addPatchIfNeeded(annotations, currentAnnotations, GIT_REVISIONS_ANN, string(gitRevisionsJSON))
}

if len(annotations) == 0 {
c.logger.Info("no need to add annotations")
} else {
c.logger.Infof("added annotations to application %s patch: %v", a.Name, annotations)
m["metadata"] = map[string]any{"annotations": annotations}
}
return nil
}

func (c *acrService) calculateRevision(ctx context.Context, a *application.Application, currentRevision string, previousRevision string) (*string, error) {
c.logger.Infof("Calculate revision for application '%s', current revision '%s', previous revision '%s'", a.Name, currentRevision, previousRevision)
changeRevisionResult, err := c.applicationServiceClient.GetChangeRevision(ctx, &appclient.ChangeRevisionRequest{
AppName: ptr.To(a.GetName()),
Expand All @@ -116,33 +196,28 @@ func (c *acrService) calculateRevision(ctx context.Context, a *application.Appli
return changeRevisionResult.Revision, nil
}

func (c *acrService) patchOperationWithChangeRevision(ctx context.Context, a *application.Application, revisions []string) error {
func (c *acrService) patchOperationWithChangeRevision(revisions []string) map[string]any {
if len(revisions) == 1 {
patch, _ := json.Marshal(map[string]any{
return map[string]any{
"operation": map[string]any{
"sync": map[string]any{
"changeRevision": revisions[0],
},
},
})
_, err := c.applicationClientset.ArgoprojV1alpha1().Applications(a.Namespace).Patch(ctx, a.Name, types.MergePatchType, patch, metav1.PatchOptions{})
return err
}
}

patch, _ := json.Marshal(map[string]any{
return map[string]any{
"operation": map[string]any{
"sync": map[string]any{
"changeRevisions": revisions,
},
},
})
_, err := c.applicationClientset.ArgoprojV1alpha1().Applications(a.Namespace).Patch(ctx, a.Name, types.MergePatchType, patch, metav1.PatchOptions{})
return err
}
}

func (c *acrService) patchOperationSyncResultWithChangeRevision(ctx context.Context, a *application.Application, revisions []string) error {
func (c *acrService) patchOperationSyncResultWithChangeRevision(revisions []string) map[string]any {
if len(revisions) == 1 {
patch, _ := json.Marshal(map[string]any{
return map[string]any{
"status": map[string]any{
"operationState": map[string]any{
"operation": map[string]any{
Expand All @@ -152,12 +227,9 @@ func (c *acrService) patchOperationSyncResultWithChangeRevision(ctx context.Cont
},
},
},
})
_, err := c.applicationClientset.ArgoprojV1alpha1().Applications(a.Namespace).Patch(ctx, a.Name, types.MergePatchType, patch, metav1.PatchOptions{})
return err
}
}

patch, _ := json.Marshal(map[string]any{
return map[string]any{
"status": map[string]any{
"operationState": map[string]any{
"operation": map[string]any{
Expand All @@ -167,9 +239,7 @@ func (c *acrService) patchOperationSyncResultWithChangeRevision(ctx context.Cont
},
},
},
})
_, err := c.applicationClientset.ArgoprojV1alpha1().Applications(a.Namespace).Patch(ctx, a.Name, types.MergePatchType, patch, metav1.PatchOptions{})
return err
}
}

func getCurrentRevisionFromOperation(a *application.Application) string {
Expand Down
Loading