Skip to content

Commit db1b6f6

Browse files
committed
feat: support for monorepo-controller compatible annotations
Signed-off-by: Eugene Doudine <eugene.doudine@octopus.com>
1 parent 7a6cc3d commit db1b6f6

File tree

5 files changed

+104
-40
lines changed

5 files changed

+104
-40
lines changed

acr_controller/controller/controller.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ type ACRController interface {
2424
type applicationChangeRevisionController struct {
2525
appBroadcaster Broadcaster
2626
acrService service.ACRService
27+
useAnnotations bool
2728
}
2829

29-
func NewApplicationChangeRevisionController(appInformer cache.SharedIndexInformer, applicationServiceClient appclient.ApplicationClient, applicationClientset appclientset.Interface) ACRController {
30+
func NewApplicationChangeRevisionController(appInformer cache.SharedIndexInformer, applicationServiceClient appclient.ApplicationClient, applicationClientset appclientset.Interface, useAnnotations bool) ACRController {
3031
appBroadcaster := NewBroadcaster()
3132
_, err := appInformer.AddEventHandler(appBroadcaster)
3233
if err != nil {
@@ -35,6 +36,7 @@ func NewApplicationChangeRevisionController(appInformer cache.SharedIndexInforme
3536
return &applicationChangeRevisionController{
3637
appBroadcaster: appBroadcaster,
3738
acrService: service.NewACRService(applicationClientset, applicationServiceClient),
39+
useAnnotations: useAnnotations,
3840
}
3941
}
4042

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

49-
return c.acrService.ChangeRevision(ctx, &a)
51+
return c.acrService.ChangeRevision(ctx, &a, c.useAnnotations)
5052
}
5153

5254
// TODO: move to abstraction

acr_controller/server.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ type ACRServerOpts struct {
5454
ApplicationNamespaces []string
5555
BaseHRef string
5656
RootPath string
57+
DisableAnnotations bool
5758
}
5859

5960
type handlerSwitcher struct {
@@ -96,7 +97,7 @@ func (a *ACRServer) Init(ctx context.Context) {
9697
}
9798

9899
func (a *ACRServer) RunController(ctx context.Context) {
99-
controller := acr_controller.NewApplicationChangeRevisionController(a.appInformer, a.ApplicationServiceClient, a.applicationClientset)
100+
controller := acr_controller.NewApplicationChangeRevisionController(a.appInformer, a.ApplicationServiceClient, a.applicationClientset, !a.DisableAnnotations)
100101
go controller.Run(ctx)
101102
}
102103

acr_controller/service/acr_service.go

Lines changed: 92 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package service
33
import (
44
"context"
55
"encoding/json"
6+
"fmt"
67
"sync"
78

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

20+
const (
21+
CHANGE_REVISION_ANN = "mrp-controller.argoproj.io/change-revision"
22+
CHANGE_REVISIONS_ANN = "mrp-controller.argoproj.io/change-revisions"
23+
GIT_REVISION_ANN = "mrp-controller.argoproj.io/git-revision"
24+
GIT_REVISIONS_ANN = "mrp-controller.argoproj.io/git-revisions"
25+
)
26+
1927
type ACRService interface {
20-
ChangeRevision(ctx context.Context, application *application.Application) error
28+
ChangeRevision(ctx context.Context, application *application.Application, useAnnotations bool) error
2129
}
2230

2331
type acrService struct {
@@ -55,7 +63,7 @@ func getChangeRevision(app *application.Application) string {
5563
return ""
5664
}
5765

58-
func (c *acrService) ChangeRevision(ctx context.Context, a *application.Application) error {
66+
func (c *acrService) ChangeRevision(ctx context.Context, a *application.Application, useAnnotations bool) error {
5967
c.lock.Lock()
6068
defer c.lock.Unlock()
6169

@@ -73,36 +81,96 @@ func (c *acrService) ChangeRevision(ctx context.Context, a *application.Applicat
7381
return nil
7482
}
7583

76-
revision, err := c.calculateRevision(ctx, app)
84+
currentRevision, previousRevision := c.getRevisions(ctx, a)
85+
revision, err := c.calculateRevision(ctx, app, currentRevision, previousRevision)
7786
if err != nil {
7887
return err
7988
}
8089

90+
var revisions []string
8191
if revision == nil || *revision == "" {
8292
c.logger.Infof("Revision for application %s is empty", app.Name)
83-
return nil
93+
} else {
94+
c.logger.Infof("Change revision for application %s is %s", app.Name, *revision)
95+
revisions = []string{*revision}
8496
}
8597

86-
c.logger.Infof("Change revision for application %s is %s", app.Name, *revision)
87-
8898
app, err = c.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace).Get(ctx, app.Name, metav1.GetOptions{})
8999
if err != nil {
90100
return err
91101
}
92102

93-
revisions := []string{*revision}
103+
patchMap := make(map[string]any, 2)
94104

95-
if app.Status.OperationState != nil && app.Status.OperationState.Operation.Sync != nil {
96-
c.logger.Infof("Patch operation status for application %s", app.Name)
97-
return c.patchOperationSyncResultWithChangeRevision(ctx, app, revisions)
105+
if len(revisions) > 0 {
106+
if app.Status.OperationState != nil && app.Status.OperationState.Operation.Sync != nil {
107+
c.logger.Infof("Patch operation status for application %s", app.Name)
108+
patchMap = c.patchOperationSyncResultWithChangeRevision(ctx, app, revisions)
109+
} else {
110+
c.logger.Infof("Patch operation for application %s", app.Name)
111+
patchMap = c.patchOperationWithChangeRevision(ctx, app, revisions)
112+
}
113+
}
114+
if useAnnotations {
115+
err = c.addAnnotationPatch(patchMap, app, *revision, revisions, currentRevision, []string{currentRevision})
116+
if err != nil {
117+
return err
118+
}
98119
}
120+
if len(patchMap) > 0 {
121+
c.logger.Infof("patching resource: %v", patchMap)
122+
patch, err := json.Marshal(patchMap)
123+
if err != nil {
124+
return err
125+
}
126+
_, err = c.applicationClientset.ArgoprojV1alpha1().Applications(a.Namespace).Patch(ctx, a.Name, types.MergePatchType, patch, metav1.PatchOptions{})
127+
return err
128+
} else {
129+
c.logger.Infof("no patch needed")
130+
return nil
131+
}
132+
}
99133

100-
c.logger.Infof("Patch operation for application %s", app.Name)
101-
return c.patchOperationWithChangeRevision(ctx, app, revisions)
134+
func addPatchIfNeeded(annotations map[string]string, currentAnnotations map[string]string, key string, val string) {
135+
currentVal, ok := currentAnnotations[key]
136+
if !ok || currentVal != val {
137+
annotations[key] = val
138+
}
102139
}
103140

104-
func (c *acrService) calculateRevision(ctx context.Context, a *application.Application) (*string, error) {
105-
currentRevision, previousRevision := c.getRevisions(ctx, a)
141+
func (c *acrService) addAnnotationPatch(m map[string]any,
142+
a *application.Application,
143+
changeRevision string,
144+
changeRevisions []string,
145+
gitRevision string,
146+
gitRevisions []string) error {
147+
c.logger.Infof("annotating application '%s', changeRevision=%s, changeRevisions=%v, gitRevision=%s, gitRevisions=%v", a.Name, changeRevision, changeRevisions, gitRevision, gitRevisions)
148+
annotations := map[string]string{}
149+
currentAnnotations := a.Annotations
150+
151+
changeRevisionsJson, err := json.Marshal(changeRevisions)
152+
if err != nil {
153+
return fmt.Errorf("Failed to marshall changeRevisions %v: %v", changeRevisions, err)
154+
}
155+
gitRevisionsJson, err := json.Marshal(gitRevisions)
156+
if err != nil {
157+
return fmt.Errorf("Failed to marshall gitRevisions %v: %v", gitRevisions, err)
158+
}
159+
160+
addPatchIfNeeded(annotations, currentAnnotations, CHANGE_REVISION_ANN, changeRevision)
161+
addPatchIfNeeded(annotations, currentAnnotations, CHANGE_REVISIONS_ANN, string(changeRevisionsJson))
162+
addPatchIfNeeded(annotations, currentAnnotations, GIT_REVISION_ANN, gitRevision)
163+
addPatchIfNeeded(annotations, currentAnnotations, GIT_REVISIONS_ANN, string(gitRevisionsJson))
164+
165+
if len(annotations) == 0 {
166+
c.logger.Info("no need to add annotations")
167+
}
168+
c.logger.Infof("added annotations to application %s patch: %v", a.Name, annotations)
169+
m["metadata"] = map[string]any{"annotations": annotations}
170+
return nil
171+
}
172+
173+
func (c *acrService) calculateRevision(ctx context.Context, a *application.Application, currentRevision string, previousRevision string) (*string, error) {
106174
c.logger.Infof("Calculate revision for application '%s', current revision '%s', previous revision '%s'", a.Name, currentRevision, previousRevision)
107175
changeRevisionResult, err := c.applicationServiceClient.GetChangeRevision(ctx, &appclient.ChangeRevisionRequest{
108176
AppName: ptr.To(a.GetName()),
@@ -116,33 +184,28 @@ func (c *acrService) calculateRevision(ctx context.Context, a *application.Appli
116184
return changeRevisionResult.Revision, nil
117185
}
118186

119-
func (c *acrService) patchOperationWithChangeRevision(ctx context.Context, a *application.Application, revisions []string) error {
187+
func (c *acrService) patchOperationWithChangeRevision(ctx context.Context, a *application.Application, revisions []string) map[string]any {
120188
if len(revisions) == 1 {
121-
patch, _ := json.Marshal(map[string]any{
189+
return map[string]any{
122190
"operation": map[string]any{
123191
"sync": map[string]any{
124192
"changeRevision": revisions[0],
125193
},
126194
},
127-
})
128-
_, err := c.applicationClientset.ArgoprojV1alpha1().Applications(a.Namespace).Patch(ctx, a.Name, types.MergePatchType, patch, metav1.PatchOptions{})
129-
return err
195+
}
130196
}
131-
132-
patch, _ := json.Marshal(map[string]any{
197+
return map[string]any{
133198
"operation": map[string]any{
134199
"sync": map[string]any{
135200
"changeRevisions": revisions,
136201
},
137202
},
138-
})
139-
_, err := c.applicationClientset.ArgoprojV1alpha1().Applications(a.Namespace).Patch(ctx, a.Name, types.MergePatchType, patch, metav1.PatchOptions{})
140-
return err
203+
}
141204
}
142205

143-
func (c *acrService) patchOperationSyncResultWithChangeRevision(ctx context.Context, a *application.Application, revisions []string) error {
206+
func (c *acrService) patchOperationSyncResultWithChangeRevision(ctx context.Context, a *application.Application, revisions []string) map[string]any {
144207
if len(revisions) == 1 {
145-
patch, _ := json.Marshal(map[string]any{
208+
return map[string]any{
146209
"status": map[string]any{
147210
"operationState": map[string]any{
148211
"operation": map[string]any{
@@ -152,12 +215,9 @@ func (c *acrService) patchOperationSyncResultWithChangeRevision(ctx context.Cont
152215
},
153216
},
154217
},
155-
})
156-
_, err := c.applicationClientset.ArgoprojV1alpha1().Applications(a.Namespace).Patch(ctx, a.Name, types.MergePatchType, patch, metav1.PatchOptions{})
157-
return err
218+
}
158219
}
159-
160-
patch, _ := json.Marshal(map[string]any{
220+
return map[string]any{
161221
"status": map[string]any{
162222
"operationState": map[string]any{
163223
"operation": map[string]any{
@@ -167,9 +227,7 @@ func (c *acrService) patchOperationSyncResultWithChangeRevision(ctx context.Cont
167227
},
168228
},
169229
},
170-
})
171-
_, err := c.applicationClientset.ArgoprojV1alpha1().Applications(a.Namespace).Patch(ctx, a.Name, types.MergePatchType, patch, metav1.PatchOptions{})
172-
return err
230+
}
173231
}
174232

175233
func getCurrentRevisionFromOperation(a *application.Application) string {

acr_controller/service/acr_service_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ func Test_ChangeRevision(r *testing.T) {
274274
acrService := newTestACRService(client)
275275
app := createTestApp(syncedAppWithHistory)
276276

277-
err := acrService.ChangeRevision(r.Context(), app)
277+
err := acrService.ChangeRevision(r.Context(), app, false)
278278
require.NoError(t, err)
279279

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

297297
app := createTestApp(syncedAppWithHistory)
298298

299-
err := acrService.ChangeRevision(r.Context(), app)
299+
err := acrService.ChangeRevision(r.Context(), app, false)
300300
require.NoError(t, err)
301301

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

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

307-
err = acrService.ChangeRevision(r.Context(), app)
307+
err = acrService.ChangeRevision(r.Context(), app, false)
308308

309309
require.NoError(t, err)
310310

cmd/application-change-revision-controller/commands/application_change_revision_controller.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ func NewCommand() *cobra.Command {
6161
applicationNamespaces []string
6262
argocdToken string
6363
rootpath string
64+
disableAnnotations bool
6465
)
6566
command := &cobra.Command{
6667
Use: cliName,
@@ -114,6 +115,7 @@ func NewCommand() *cobra.Command {
114115
RedisClient: redisClient,
115116
ApplicationNamespaces: applicationNamespaces,
116117
ApplicationServiceClient: getApplicationClient(applicationServerAddress, argocdToken, rootpath),
118+
DisableAnnotations: disableAnnotations,
117119
}
118120

119121
log.Info("Starting change revision controller server")
@@ -145,6 +147,7 @@ func NewCommand() *cobra.Command {
145147
command.Flags().IntVar(&listenPort, "port", common.DefaultPortACRServer, "Listen on given port")
146148
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 \"\".")
147149
command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", env.StringsFromEnv("ARGOCD_APPLICATION_NAMESPACES", []string{}, ","), "List of additional namespaces where application resources can be managed in")
150+
command.Flags().BoolVar(&disableAnnotations, "disable-annotations", env.ParseBoolFromEnv("ACR_CONTROLLER_DISABLE_AMMOTATIONS", false), "Disable generating the monorepo-controller compatible annotations")
148151
cacheSrc = servercache.AddCacheFlagsToCmd(command, cacheutil.Options{
149152
OnClientCreated: func(client *redis.Client) {
150153
redisClient = client

0 commit comments

Comments
 (0)