Skip to content
Closed
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
127 changes: 93 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,97 @@ func (c *acrService) ChangeRevision(ctx context.Context, a *application.Applicat
return nil
}

revision, err := c.calculateRevision(ctx, app)
currentRevision, previousRevision := c.getRevisions(ctx, a)
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

changeRevisionsJSON, err := json.Marshal(changeRevisions)
if err != nil {
return fmt.Errorf("failed to marshall changeRevisions %v: %w", changeRevisions, err)
}
gitRevisionsJSON, err := json.Marshal(gitRevisions)
if err != nil {
return fmt.Errorf("failed to marshall gitRevisions %v: %w", gitRevisions, err)
}

addPatchIfNeeded(annotations, currentAnnotations, CHANGE_REVISION_ANN, changeRevision)
addPatchIfNeeded(annotations, currentAnnotations, CHANGE_REVISIONS_ANN, string(changeRevisionsJSON))
addPatchIfNeeded(annotations, currentAnnotations, GIT_REVISION_ANN, gitRevision)
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 +185,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 +216,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 +228,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
6 changes: 3 additions & 3 deletions acr_controller/service/acr_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ func Test_ChangeRevision(r *testing.T) {
acrService := newTestACRService(client)
app := createTestApp(syncedAppWithHistory)

err := acrService.ChangeRevision(r.Context(), app)
err := acrService.ChangeRevision(r.Context(), app, false)
require.NoError(t, err)

app, err = acrService.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace).Get(r.Context(), app.Name, metav1.GetOptions{})
Expand All @@ -296,15 +296,15 @@ func Test_ChangeRevision(r *testing.T) {

app := createTestApp(syncedAppWithHistory)

err := acrService.ChangeRevision(r.Context(), app)
err := acrService.ChangeRevision(r.Context(), app, false)
require.NoError(t, err)

app, err = acrService.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace).Get(r.Context(), app.Name, metav1.GetOptions{})
require.NoError(t, err)

assert.Equal(t, "new-revision", app.Status.OperationState.Operation.Sync.ChangeRevision)

err = acrService.ChangeRevision(r.Context(), app)
err = acrService.ChangeRevision(r.Context(), app, false)

require.NoError(t, err)

Expand Down
53 changes: 53 additions & 0 deletions assets/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func NewCommand() *cobra.Command {
applicationNamespaces []string
argocdToken string
rootpath string
disableAnnotations bool
)
command := &cobra.Command{
Use: cliName,
Expand Down Expand Up @@ -114,6 +115,7 @@ func NewCommand() *cobra.Command {
RedisClient: redisClient,
ApplicationNamespaces: applicationNamespaces,
ApplicationServiceClient: getApplicationClient(applicationServerAddress, argocdToken, rootpath),
DisableAnnotations: disableAnnotations,
}

log.Info("Starting change revision controller server")
Expand Down Expand Up @@ -145,6 +147,7 @@ func NewCommand() *cobra.Command {
command.Flags().IntVar(&listenPort, "port", common.DefaultPortACRServer, "Listen on given port")
command.Flags().StringVar(&contentSecurityPolicy, "content-security-policy", env.StringFromEnv("ACR_CONTROLLER_CONTENT_SECURITY_POLICY", "frame-ancestors 'self';"), "Set Content-Security-Policy header in HTTP responses to `value`. To disable, set to \"\".")
command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", env.StringsFromEnv("ARGOCD_APPLICATION_NAMESPACES", []string{}, ","), "List of additional namespaces where application resources can be managed in")
command.Flags().BoolVar(&disableAnnotations, "disable-annotations", env.ParseBoolFromEnv("ACR_CONTROLLER_DISABLE_AMMOTATIONS", false), "Disable generating the monorepo-controller compatible annotations")
cacheSrc = servercache.AddCacheFlagsToCmd(command, cacheutil.Options{
OnClientCreated: func(client *redis.Client) {
redisClient = client
Expand Down
Loading
Loading