Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
int128 committed Jan 1, 2024
1 parent cf15278 commit ec37400
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 474 deletions.
10 changes: 0 additions & 10 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,4 @@ resources:
kind: Notification
path: github.com/int128/argocd-commenter/api/v1
version: v1
- controller: true
domain: int128.github.io
group: argocdcommenter
kind: NotificationComment
version: v1
- controller: true
domain: int128.github.io
group: argocdcommenter
kind: NotificationDeployment
version: v1
version: "3"
3 changes: 0 additions & 3 deletions api/v1/notification_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ type NotificationSpec struct{}

// NotificationStatus defines the observed state of Notification
type NotificationStatus struct {
// +optional
State NotificationState `json:"state,omitempty"`

// +optional
CommentState NotificationState `json:"commentState,omitempty"`

Expand Down
23 changes: 4 additions & 19 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,38 +116,23 @@ func main() {
}

if err = (&controller.ApplicationPhaseReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ApplicationPhase")
os.Exit(1)
}

if err = (&controller.ApplicationHealthReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ApplicationHealth")
os.Exit(1)
}

if err = (&controller.NotificationCommentReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Notification: notificationClient,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "NotificationComment")
setupLog.Error(err, "unable to create controller", "controller", "ApplicationPhase")
os.Exit(1)
}

if err = (&controller.NotificationDeploymentReconciler{
if err = (&controller.ApplicationHealthReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Notification: notificationClient,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "NotificationDeployment")
setupLog.Error(err, "unable to create controller", "controller", "ApplicationHealth")
os.Exit(1)
}

//+kubebuilder:scaffold:builder

if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ spec:
type: string
deploymentState:
type: string
state:
type: string
type: object
type: object
served: true
Expand Down
26 changes: 0 additions & 26 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,6 @@ kind: ClusterRole
metadata:
name: manager-role
rules:
- apiGroups:
- argocdcommenter.int128.github.io
resources:
- notifications
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- argocdcommenter.int128.github.io
resources:
- notifications/finalizers
verbs:
- update
- apiGroups:
- argocdcommenter.int128.github.io
resources:
- notifications/status
verbs:
- get
- patch
- update
- apiGroups:
- argoproj.io
resources:
Expand Down
96 changes: 63 additions & 33 deletions internal/controller/applicationhealth_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@ package controller

import (
"context"
"time"

argocdv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/gitops-engine/pkg/health"
argocdcommenterv1 "github.com/int128/argocd-commenter/api/v1"
"github.com/int128/argocd-commenter/internal/argocd"
"github.com/int128/argocd-commenter/internal/controller/predicates"
"github.com/int128/argocd-commenter/internal/notification"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -32,11 +35,24 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log"
)

// ApplicationHealthReconciler reconciles a ApplicationHealth object
var (
// When the GitHub Deployment is not found, this action will retry by this interval
// until the application is synced with a valid GitHub Deployment.
// This should be reasonable to avoid the rate limit of GitHub API.
requeueIntervalWhenDeploymentNotFound = 30 * time.Second

Check failure on line 42 in internal/controller/applicationhealth_controller.go

View workflow job for this annotation

GitHub Actions / go / lint

var `requeueIntervalWhenDeploymentNotFound` is unused (unused)

// When the GitHub Deployment is not found, this action will retry by this timeout.
// Argo CD refreshes an application every 3 minutes by default.
// This should be reasonable to avoid the rate limit of GitHub API.
requeueTimeoutWhenDeploymentNotFound = 10 * time.Minute
)

// ApplicationHealthReconciler reconciles an Application object
type ApplicationHealthReconciler struct {
client.Client
Scheme *runtime.Scheme
Recorder record.EventRecorder
Scheme *runtime.Scheme
Recorder record.EventRecorder
Notification notification.Client
}

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

appHealth := app.Status.Health.Status
switch {
case appHealth == health.HealthStatusProgressing:
patch := client.MergeFrom(appNotification.DeepCopy())
appNotification.Status.State = argocdcommenterv1.NotificationStateProgressing
if err := r.Client.Status().Patch(ctx, &appNotification, patch); err != nil {
logger.Error(err, "unable to patch ApplicationHealth")
return ctrl.Result{}, err
}
logger.Info("patched the Notification", "state", appNotification.Status.State)
return ctrl.Result{}, nil
r.notifyComment(ctx, app)
r.notifyDeployment(ctx, app)
return ctrl.Result{}, nil
}

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

case appHealth == health.HealthStatusDegraded:
patch := client.MergeFrom(appNotification.DeepCopy())
appNotification.Status.State = argocdcommenterv1.NotificationStateDegraded
if err := r.Client.Status().Patch(ctx, &appNotification, patch); err != nil {
logger.Error(err, "unable to patch ApplicationHealth")
return ctrl.Result{}, err
argocdURL, err := argocd.GetExternalURL(ctx, r.Client, app.Namespace)
if err != nil {
logger.Info("unable to determine Argo CD URL", "error", err)
}
comments := notification.NewCommentsOnOnHealthChanged(app, argocdURL)
if len(comments) == 0 {
logger.Info("no comment on this health event")
return
}
for _, comment := range comments {
if err := r.Notification.CreateComment(ctx, comment, app); err != nil {
logger.Error(err, "unable to create a comment")
r.Recorder.Eventf(&app, corev1.EventTypeWarning, "CreateCommentError",
"unable to create a comment by %s: %s", app.Status.Health.Status, err)
} else {
r.Recorder.Eventf(&app, corev1.EventTypeNormal, "CreatedComment", "created a comment by %s", app.Status.Health.Status)
}
logger.Info("patched the Notification", "state", appNotification.Status.State)
return ctrl.Result{}, nil
}
return ctrl.Result{}, nil
}

func (r *ApplicationHealthReconciler) notifyDeployment(ctx context.Context, app argocdv1alpha1.Application) {
logger := log.FromContext(ctx)

argocdURL, err := argocd.GetExternalURL(ctx, r.Client, app.Namespace)
if err != nil {
logger.Info("unable to determine Argo CD URL", "error", err)
}
ds := notification.NewDeploymentStatusOnHealthChanged(app, argocdURL)
if ds == nil {
logger.Info("no deployment status on this health event")
return
}
if err := r.Notification.CreateDeployment(ctx, *ds); err != nil {
logger.Error(err, "unable to create a deployment status")
r.Recorder.Eventf(&app, corev1.EventTypeWarning, "CreateDeploymentError",
"unable to create a deployment status by %s: %s", app.Status.Health.Status, err)
} else {
r.Recorder.Eventf(&app, corev1.EventTypeNormal, "CreatedDeployment", "created a deployment status by %s", app.Status.Health.Status)
}
}

// SetupWithManager sets up the controller with the Manager.
Expand Down
82 changes: 50 additions & 32 deletions internal/controller/applicationphase_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ import (
"context"

argocdv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
argocdcommenterv1 "github.com/int128/argocd-commenter/api/v1"
"github.com/int128/argocd-commenter/internal/argocd"
"github.com/int128/argocd-commenter/internal/controller/predicates"
"github.com/int128/argocd-commenter/internal/notification"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -33,11 +34,12 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log"
)

// ApplicationPhaseReconciler reconciles a ApplicationPhase object
// ApplicationPhaseReconciler reconciles an Application object
type ApplicationPhaseReconciler struct {
client.Client
Scheme *runtime.Scheme
Recorder record.EventRecorder
Scheme *runtime.Scheme
Recorder record.EventRecorder
Notification notification.Client
}

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

switch argocd.GetOperationPhase(app) {
case synccommon.OperationRunning:
patch := client.MergeFrom(appNotification.DeepCopy())
appNotification.Status.State = argocdcommenterv1.NotificationStateSyncing
if err := r.Client.Status().Patch(ctx, &appNotification, patch); err != nil {
logger.Error(err, "unable to patch ApplicationHealth")
return ctrl.Result{}, err
}
logger.Info("patched the Notification", "state", appNotification.Status.State)
return ctrl.Result{}, nil
r.notifyComment(ctx, app)
r.notifyDeployment(ctx, app)
return ctrl.Result{}, nil
}

case synccommon.OperationSucceeded:
patch := client.MergeFrom(appNotification.DeepCopy())
appNotification.Status.State = argocdcommenterv1.NotificationStateSynced
if err := r.Client.Status().Patch(ctx, &appNotification, patch); err != nil {
logger.Error(err, "unable to patch ApplicationHealth")
return ctrl.Result{}, err
}
logger.Info("patched the Notification", "state", appNotification.Status.State)
return ctrl.Result{}, nil
func (r *ApplicationPhaseReconciler) notifyComment(ctx context.Context, app argocdv1alpha1.Application) {
logger := log.FromContext(ctx)

case synccommon.OperationFailed:
patch := client.MergeFrom(appNotification.DeepCopy())
appNotification.Status.State = argocdcommenterv1.NotificationStateSyncFailed
if err := r.Client.Status().Patch(ctx, &appNotification, patch); err != nil {
logger.Error(err, "unable to patch ApplicationHealth")
return ctrl.Result{}, err
phase := argocd.GetOperationPhase(app)
argocdURL, err := argocd.GetExternalURL(ctx, r.Client, app.Namespace)
if err != nil {
logger.Info("unable to determine Argo CD URL", "error", err)
}
comments := notification.NewCommentsOnOnPhaseChanged(app, argocdURL)
if len(comments) == 0 {
logger.Info("no comment on this phase event", "phase", phase)
return
}
for _, comment := range comments {
if err := r.Notification.CreateComment(ctx, comment, app); err != nil {
logger.Error(err, "unable to create a comment")
r.Recorder.Eventf(&app, corev1.EventTypeWarning, "CreateCommentError",
"unable to create a comment by %s: %s", phase, err)
} else {
r.Recorder.Eventf(&app, corev1.EventTypeNormal, "CreatedComment", "created a comment by %s", phase)
}
logger.Info("patched the Notification", "state", appNotification.Status.State)
return ctrl.Result{}, nil
}
return ctrl.Result{}, nil
}

func (r *ApplicationPhaseReconciler) notifyDeployment(ctx context.Context, app argocdv1alpha1.Application) {
logger := log.FromContext(ctx)

argocdURL, err := argocd.GetExternalURL(ctx, r.Client, app.Namespace)
if err != nil {
logger.Info("unable to determine Argo CD URL", "error", err)
}
ds := notification.NewDeploymentStatusOnPhaseChanged(app, argocdURL)
if ds == nil {
logger.Info("no deployment status on this phase event")
return
}
if err := r.Notification.CreateDeployment(ctx, *ds); err != nil {
logger.Error(err, "unable to create a deployment status")
r.Recorder.Eventf(&app, corev1.EventTypeWarning, "CreateDeploymentError",
"unable to create a deployment status by %s: %s", app.Status.Health.Status, err)
} else {
r.Recorder.Eventf(&app, corev1.EventTypeNormal, "CreatedDeployment", "created a deployment status by %s", app.Status.Health.Status)
}
}

// SetupWithManager sets up the controller with the Manager.
Expand Down
Loading

0 comments on commit ec37400

Please sign in to comment.