Skip to content

Commit ec37400

Browse files
committed
Refactor
1 parent cf15278 commit ec37400

10 files changed

+118
-474
lines changed

PROJECT

-10
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,4 @@ resources:
3131
kind: Notification
3232
path: github.com/int128/argocd-commenter/api/v1
3333
version: v1
34-
- controller: true
35-
domain: int128.github.io
36-
group: argocdcommenter
37-
kind: NotificationComment
38-
version: v1
39-
- controller: true
40-
domain: int128.github.io
41-
group: argocdcommenter
42-
kind: NotificationDeployment
43-
version: v1
4434
version: "3"

api/v1/notification_types.go

-3
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ type NotificationSpec struct{}
2525

2626
// NotificationStatus defines the observed state of Notification
2727
type NotificationStatus struct {
28-
// +optional
29-
State NotificationState `json:"state,omitempty"`
30-
3128
// +optional
3229
CommentState NotificationState `json:"commentState,omitempty"`
3330

cmd/main.go

+4-19
Original file line numberDiff line numberDiff line change
@@ -116,38 +116,23 @@ func main() {
116116
}
117117

118118
if err = (&controller.ApplicationPhaseReconciler{
119-
Client: mgr.GetClient(),
120-
Scheme: mgr.GetScheme(),
121-
}).SetupWithManager(mgr); err != nil {
122-
setupLog.Error(err, "unable to create controller", "controller", "ApplicationPhase")
123-
os.Exit(1)
124-
}
125-
126-
if err = (&controller.ApplicationHealthReconciler{
127-
Client: mgr.GetClient(),
128-
Scheme: mgr.GetScheme(),
129-
}).SetupWithManager(mgr); err != nil {
130-
setupLog.Error(err, "unable to create controller", "controller", "ApplicationHealth")
131-
os.Exit(1)
132-
}
133-
134-
if err = (&controller.NotificationCommentReconciler{
135119
Client: mgr.GetClient(),
136120
Scheme: mgr.GetScheme(),
137121
Notification: notificationClient,
138122
}).SetupWithManager(mgr); err != nil {
139-
setupLog.Error(err, "unable to create controller", "controller", "NotificationComment")
123+
setupLog.Error(err, "unable to create controller", "controller", "ApplicationPhase")
140124
os.Exit(1)
141125
}
142126

143-
if err = (&controller.NotificationDeploymentReconciler{
127+
if err = (&controller.ApplicationHealthReconciler{
144128
Client: mgr.GetClient(),
145129
Scheme: mgr.GetScheme(),
146130
Notification: notificationClient,
147131
}).SetupWithManager(mgr); err != nil {
148-
setupLog.Error(err, "unable to create controller", "controller", "NotificationDeployment")
132+
setupLog.Error(err, "unable to create controller", "controller", "ApplicationHealth")
149133
os.Exit(1)
150134
}
135+
151136
//+kubebuilder:scaffold:builder
152137

153138
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {

config/crd/bases/argocdcommenter.int128.github.io_notifications.yaml

-2
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ spec:
4141
type: string
4242
deploymentState:
4343
type: string
44-
state:
45-
type: string
4644
type: object
4745
type: object
4846
served: true

config/rbac/role.yaml

-26
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,6 @@ kind: ClusterRole
44
metadata:
55
name: manager-role
66
rules:
7-
- apiGroups:
8-
- argocdcommenter.int128.github.io
9-
resources:
10-
- notifications
11-
verbs:
12-
- create
13-
- delete
14-
- get
15-
- list
16-
- patch
17-
- update
18-
- watch
19-
- apiGroups:
20-
- argocdcommenter.int128.github.io
21-
resources:
22-
- notifications/finalizers
23-
verbs:
24-
- update
25-
- apiGroups:
26-
- argocdcommenter.int128.github.io
27-
resources:
28-
- notifications/status
29-
verbs:
30-
- get
31-
- patch
32-
- update
337
- apiGroups:
348
- argoproj.io
359
resources:

internal/controller/applicationhealth_controller.go

+63-33
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@ package controller
1818

1919
import (
2020
"context"
21+
"time"
2122

2223
argocdv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
23-
"github.com/argoproj/gitops-engine/pkg/health"
2424
argocdcommenterv1 "github.com/int128/argocd-commenter/api/v1"
25+
"github.com/int128/argocd-commenter/internal/argocd"
2526
"github.com/int128/argocd-commenter/internal/controller/predicates"
27+
"github.com/int128/argocd-commenter/internal/notification"
28+
corev1 "k8s.io/api/core/v1"
2629
apierrors "k8s.io/apimachinery/pkg/api/errors"
2730
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2831
"k8s.io/apimachinery/pkg/runtime"
@@ -32,11 +35,24 @@ import (
3235
"sigs.k8s.io/controller-runtime/pkg/log"
3336
)
3437

35-
// ApplicationHealthReconciler reconciles a ApplicationHealth object
38+
var (
39+
// When the GitHub Deployment is not found, this action will retry by this interval
40+
// until the application is synced with a valid GitHub Deployment.
41+
// This should be reasonable to avoid the rate limit of GitHub API.
42+
requeueIntervalWhenDeploymentNotFound = 30 * time.Second
43+
44+
// When the GitHub Deployment is not found, this action will retry by this timeout.
45+
// Argo CD refreshes an application every 3 minutes by default.
46+
// This should be reasonable to avoid the rate limit of GitHub API.
47+
requeueTimeoutWhenDeploymentNotFound = 10 * time.Minute
48+
)
49+
50+
// ApplicationHealthReconciler reconciles an Application object
3651
type ApplicationHealthReconciler struct {
3752
client.Client
38-
Scheme *runtime.Scheme
39-
Recorder record.EventRecorder
53+
Scheme *runtime.Scheme
54+
Recorder record.EventRecorder
55+
Notification notification.Client
4056
}
4157

4258
//+kubebuilder:rbac:groups=argoproj.io,resources=applications,verbs=get;watch;list
@@ -74,39 +90,53 @@ func (r *ApplicationHealthReconciler) Reconcile(ctx context.Context, req ctrl.Re
7490
logger.Info("created an Notification")
7591
}
7692

77-
appHealth := app.Status.Health.Status
78-
switch {
79-
case appHealth == health.HealthStatusProgressing:
80-
patch := client.MergeFrom(appNotification.DeepCopy())
81-
appNotification.Status.State = argocdcommenterv1.NotificationStateProgressing
82-
if err := r.Client.Status().Patch(ctx, &appNotification, patch); err != nil {
83-
logger.Error(err, "unable to patch ApplicationHealth")
84-
return ctrl.Result{}, err
85-
}
86-
logger.Info("patched the Notification", "state", appNotification.Status.State)
87-
return ctrl.Result{}, nil
93+
r.notifyComment(ctx, app)
94+
r.notifyDeployment(ctx, app)
95+
return ctrl.Result{}, nil
96+
}
8897

89-
case appHealth == health.HealthStatusHealthy:
90-
patch := client.MergeFrom(appNotification.DeepCopy())
91-
appNotification.Status.State = argocdcommenterv1.NotificationStateHealthy
92-
if err := r.Client.Status().Patch(ctx, &appNotification, patch); err != nil {
93-
logger.Error(err, "unable to patch ApplicationHealth")
94-
return ctrl.Result{}, err
95-
}
96-
logger.Info("patched the Notification", "state", appNotification.Status.State)
97-
return ctrl.Result{}, nil
98+
func (r *ApplicationHealthReconciler) notifyComment(ctx context.Context, app argocdv1alpha1.Application) {
99+
logger := log.FromContext(ctx)
98100

99-
case appHealth == health.HealthStatusDegraded:
100-
patch := client.MergeFrom(appNotification.DeepCopy())
101-
appNotification.Status.State = argocdcommenterv1.NotificationStateDegraded
102-
if err := r.Client.Status().Patch(ctx, &appNotification, patch); err != nil {
103-
logger.Error(err, "unable to patch ApplicationHealth")
104-
return ctrl.Result{}, err
101+
argocdURL, err := argocd.GetExternalURL(ctx, r.Client, app.Namespace)
102+
if err != nil {
103+
logger.Info("unable to determine Argo CD URL", "error", err)
104+
}
105+
comments := notification.NewCommentsOnOnHealthChanged(app, argocdURL)
106+
if len(comments) == 0 {
107+
logger.Info("no comment on this health event")
108+
return
109+
}
110+
for _, comment := range comments {
111+
if err := r.Notification.CreateComment(ctx, comment, app); err != nil {
112+
logger.Error(err, "unable to create a comment")
113+
r.Recorder.Eventf(&app, corev1.EventTypeWarning, "CreateCommentError",
114+
"unable to create a comment by %s: %s", app.Status.Health.Status, err)
115+
} else {
116+
r.Recorder.Eventf(&app, corev1.EventTypeNormal, "CreatedComment", "created a comment by %s", app.Status.Health.Status)
105117
}
106-
logger.Info("patched the Notification", "state", appNotification.Status.State)
107-
return ctrl.Result{}, nil
108118
}
109-
return ctrl.Result{}, nil
119+
}
120+
121+
func (r *ApplicationHealthReconciler) notifyDeployment(ctx context.Context, app argocdv1alpha1.Application) {
122+
logger := log.FromContext(ctx)
123+
124+
argocdURL, err := argocd.GetExternalURL(ctx, r.Client, app.Namespace)
125+
if err != nil {
126+
logger.Info("unable to determine Argo CD URL", "error", err)
127+
}
128+
ds := notification.NewDeploymentStatusOnHealthChanged(app, argocdURL)
129+
if ds == nil {
130+
logger.Info("no deployment status on this health event")
131+
return
132+
}
133+
if err := r.Notification.CreateDeployment(ctx, *ds); err != nil {
134+
logger.Error(err, "unable to create a deployment status")
135+
r.Recorder.Eventf(&app, corev1.EventTypeWarning, "CreateDeploymentError",
136+
"unable to create a deployment status by %s: %s", app.Status.Health.Status, err)
137+
} else {
138+
r.Recorder.Eventf(&app, corev1.EventTypeNormal, "CreatedDeployment", "created a deployment status by %s", app.Status.Health.Status)
139+
}
110140
}
111141

112142
// SetupWithManager sets up the controller with the Manager.

internal/controller/applicationphase_controller.go

+50-32
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ import (
2020
"context"
2121

2222
argocdv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
23-
synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
2423
argocdcommenterv1 "github.com/int128/argocd-commenter/api/v1"
2524
"github.com/int128/argocd-commenter/internal/argocd"
2625
"github.com/int128/argocd-commenter/internal/controller/predicates"
26+
"github.com/int128/argocd-commenter/internal/notification"
27+
corev1 "k8s.io/api/core/v1"
2728
apierrors "k8s.io/apimachinery/pkg/api/errors"
2829
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2930
"k8s.io/apimachinery/pkg/runtime"
@@ -33,11 +34,12 @@ import (
3334
"sigs.k8s.io/controller-runtime/pkg/log"
3435
)
3536

36-
// ApplicationPhaseReconciler reconciles a ApplicationPhase object
37+
// ApplicationPhaseReconciler reconciles an Application object
3738
type ApplicationPhaseReconciler struct {
3839
client.Client
39-
Scheme *runtime.Scheme
40-
Recorder record.EventRecorder
40+
Scheme *runtime.Scheme
41+
Recorder record.EventRecorder
42+
Notification notification.Client
4143
}
4244

4345
//+kubebuilder:rbac:groups=argoproj.io,resources=applications,verbs=get;watch;list
@@ -75,38 +77,54 @@ func (r *ApplicationPhaseReconciler) Reconcile(ctx context.Context, req ctrl.Req
7577
logger.Info("created an Notification")
7678
}
7779

78-
switch argocd.GetOperationPhase(app) {
79-
case synccommon.OperationRunning:
80-
patch := client.MergeFrom(appNotification.DeepCopy())
81-
appNotification.Status.State = argocdcommenterv1.NotificationStateSyncing
82-
if err := r.Client.Status().Patch(ctx, &appNotification, patch); err != nil {
83-
logger.Error(err, "unable to patch ApplicationHealth")
84-
return ctrl.Result{}, err
85-
}
86-
logger.Info("patched the Notification", "state", appNotification.Status.State)
87-
return ctrl.Result{}, nil
80+
r.notifyComment(ctx, app)
81+
r.notifyDeployment(ctx, app)
82+
return ctrl.Result{}, nil
83+
}
8884

89-
case synccommon.OperationSucceeded:
90-
patch := client.MergeFrom(appNotification.DeepCopy())
91-
appNotification.Status.State = argocdcommenterv1.NotificationStateSynced
92-
if err := r.Client.Status().Patch(ctx, &appNotification, patch); err != nil {
93-
logger.Error(err, "unable to patch ApplicationHealth")
94-
return ctrl.Result{}, err
95-
}
96-
logger.Info("patched the Notification", "state", appNotification.Status.State)
97-
return ctrl.Result{}, nil
85+
func (r *ApplicationPhaseReconciler) notifyComment(ctx context.Context, app argocdv1alpha1.Application) {
86+
logger := log.FromContext(ctx)
9887

99-
case synccommon.OperationFailed:
100-
patch := client.MergeFrom(appNotification.DeepCopy())
101-
appNotification.Status.State = argocdcommenterv1.NotificationStateSyncFailed
102-
if err := r.Client.Status().Patch(ctx, &appNotification, patch); err != nil {
103-
logger.Error(err, "unable to patch ApplicationHealth")
104-
return ctrl.Result{}, err
88+
phase := argocd.GetOperationPhase(app)
89+
argocdURL, err := argocd.GetExternalURL(ctx, r.Client, app.Namespace)
90+
if err != nil {
91+
logger.Info("unable to determine Argo CD URL", "error", err)
92+
}
93+
comments := notification.NewCommentsOnOnPhaseChanged(app, argocdURL)
94+
if len(comments) == 0 {
95+
logger.Info("no comment on this phase event", "phase", phase)
96+
return
97+
}
98+
for _, comment := range comments {
99+
if err := r.Notification.CreateComment(ctx, comment, app); err != nil {
100+
logger.Error(err, "unable to create a comment")
101+
r.Recorder.Eventf(&app, corev1.EventTypeWarning, "CreateCommentError",
102+
"unable to create a comment by %s: %s", phase, err)
103+
} else {
104+
r.Recorder.Eventf(&app, corev1.EventTypeNormal, "CreatedComment", "created a comment by %s", phase)
105105
}
106-
logger.Info("patched the Notification", "state", appNotification.Status.State)
107-
return ctrl.Result{}, nil
108106
}
109-
return ctrl.Result{}, nil
107+
}
108+
109+
func (r *ApplicationPhaseReconciler) notifyDeployment(ctx context.Context, app argocdv1alpha1.Application) {
110+
logger := log.FromContext(ctx)
111+
112+
argocdURL, err := argocd.GetExternalURL(ctx, r.Client, app.Namespace)
113+
if err != nil {
114+
logger.Info("unable to determine Argo CD URL", "error", err)
115+
}
116+
ds := notification.NewDeploymentStatusOnPhaseChanged(app, argocdURL)
117+
if ds == nil {
118+
logger.Info("no deployment status on this phase event")
119+
return
120+
}
121+
if err := r.Notification.CreateDeployment(ctx, *ds); err != nil {
122+
logger.Error(err, "unable to create a deployment status")
123+
r.Recorder.Eventf(&app, corev1.EventTypeWarning, "CreateDeploymentError",
124+
"unable to create a deployment status by %s: %s", app.Status.Health.Status, err)
125+
} else {
126+
r.Recorder.Eventf(&app, corev1.EventTypeNormal, "CreatedDeployment", "created a deployment status by %s", app.Status.Health.Status)
127+
}
110128
}
111129

112130
// SetupWithManager sets up the controller with the Manager.

0 commit comments

Comments
 (0)