@@ -18,11 +18,14 @@ package controller
18
18
19
19
import (
20
20
"context"
21
+ "time"
21
22
22
23
argocdv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
23
- "github.com/argoproj/gitops-engine/pkg/health"
24
24
argocdcommenterv1 "github.com/int128/argocd-commenter/api/v1"
25
+ "github.com/int128/argocd-commenter/internal/argocd"
25
26
"github.com/int128/argocd-commenter/internal/controller/predicates"
27
+ "github.com/int128/argocd-commenter/internal/notification"
28
+ corev1 "k8s.io/api/core/v1"
26
29
apierrors "k8s.io/apimachinery/pkg/api/errors"
27
30
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28
31
"k8s.io/apimachinery/pkg/runtime"
@@ -32,11 +35,24 @@ import (
32
35
"sigs.k8s.io/controller-runtime/pkg/log"
33
36
)
34
37
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
36
51
type ApplicationHealthReconciler struct {
37
52
client.Client
38
- Scheme * runtime.Scheme
39
- Recorder record.EventRecorder
53
+ Scheme * runtime.Scheme
54
+ Recorder record.EventRecorder
55
+ Notification notification.Client
40
56
}
41
57
42
58
//+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
74
90
logger .Info ("created an Notification" )
75
91
}
76
92
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
+ }
88
97
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 )
98
100
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 )
105
117
}
106
- logger .Info ("patched the Notification" , "state" , appNotification .Status .State )
107
- return ctrl.Result {}, nil
108
118
}
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
+ }
110
140
}
111
141
112
142
// SetupWithManager sets up the controller with the Manager.
0 commit comments