зеркало из https://github.com/Azure/orkestra.git
172 строки
6.2 KiB
Go
172 строки
6.2 KiB
Go
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
package controllers
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/Azure/Orkestra/pkg/helpers"
|
|
|
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
|
|
|
"github.com/Azure/Orkestra/api/v1alpha1"
|
|
"github.com/Azure/Orkestra/pkg/registry"
|
|
"github.com/Azure/Orkestra/pkg/workflow"
|
|
"github.com/go-logr/logr"
|
|
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/client-go/tools/record"
|
|
ctrl "sigs.k8s.io/controller-runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
|
)
|
|
|
|
// ApplicationGroupReconciler reconciles a ApplicationGroup object
|
|
type ApplicationGroupReconciler struct {
|
|
client.Client
|
|
Log logr.Logger
|
|
Scheme *runtime.Scheme
|
|
|
|
// RegistryClient interacts with the helm registries to pull and push charts
|
|
RegistryClient *registry.Client
|
|
|
|
// StagingRepoName is the nickname for the repository used for staging artifacts before being deployed using the HelmRelease object
|
|
StagingRepoName string
|
|
|
|
WorkflowClientBuilder *workflow.Builder
|
|
|
|
// TargetDir to stage the charts before pushing
|
|
TargetDir string
|
|
|
|
// Recorder generates kubernetes events
|
|
Recorder record.EventRecorder
|
|
|
|
// DisableRemediation for debugging purposes
|
|
// The object and associated Workflow, HelmReleases will
|
|
// not be cleaned up
|
|
DisableRemediation bool
|
|
|
|
// CleanupDownloadedCharts signals the controller to delete the
|
|
// fetched charts after they have been repackaged and pushed to staging
|
|
CleanupDownloadedCharts bool
|
|
}
|
|
|
|
// +kubebuilder:rbac:groups=orkestra.azure.microsoft.com,resources=applicationgroups,verbs=get;list;watch;create;update;patch;delete
|
|
// +kubebuilder:rbac:groups=orkestra.azure.microsoft.com,resources=applicationgroups/status,verbs=get;update;patch
|
|
|
|
func (r *ApplicationGroupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
|
appGroup := &v1alpha1.ApplicationGroup{}
|
|
|
|
logr := r.Log.WithValues(v1alpha1.AppGroupNameKey, req.NamespacedName.Name)
|
|
|
|
if err := r.Get(ctx, req.NamespacedName, appGroup); err != nil {
|
|
if kerrors.IsNotFound(err) {
|
|
logr.V(3).Info("skip reconciliation since AppGroup instance not found on the cluster")
|
|
return ctrl.Result{}, nil
|
|
}
|
|
logr.Error(err, "unable to fetch ApplicationGroup instance")
|
|
return ctrl.Result{}, err
|
|
}
|
|
patch := client.MergeFrom(appGroup.DeepCopy())
|
|
|
|
statusHelper := helpers.StatusHelper{
|
|
Client: r.Client,
|
|
Logger: logr,
|
|
PatchFrom: patch,
|
|
Recorder: r.Recorder,
|
|
}
|
|
reconcileHelper := helpers.ReconcileHelper{
|
|
Client: r.Client,
|
|
Logger: logr,
|
|
Instance: appGroup,
|
|
WorkflowClientBuilder: r.WorkflowClientBuilder,
|
|
RegistryClient: r.RegistryClient,
|
|
RegistryOptions: helpers.RegistryClientOptions{
|
|
StagingRepoName: r.StagingRepoName,
|
|
TargetDir: r.TargetDir,
|
|
CleanupDownloadedCharts: r.CleanupDownloadedCharts,
|
|
},
|
|
}
|
|
|
|
if !appGroup.DeletionTimestamp.IsZero() {
|
|
if err := statusHelper.MarkReversing(ctx, appGroup); err != nil {
|
|
logr.Error(err, "failed to mark the app group into a reversing state")
|
|
return statusHelper.Failed(ctx, appGroup, err)
|
|
}
|
|
result, err := reconcileHelper.Reverse(ctx)
|
|
if !result.Requeue && err == nil {
|
|
// Remove the finalizer because we have finished reversing
|
|
controllerutil.RemoveFinalizer(appGroup, v1alpha1.AppGroupFinalizer)
|
|
if err := r.Patch(ctx, appGroup, patch); err != nil {
|
|
return ctrl.Result{}, nil
|
|
}
|
|
}
|
|
return result, err
|
|
}
|
|
// Add finalizer if it doesn't already exist
|
|
if appGroup.Finalizers == nil {
|
|
controllerutil.AddFinalizer(appGroup, v1alpha1.AppGroupFinalizer)
|
|
if err := r.Patch(ctx, appGroup, patch); err != nil {
|
|
logr.Error(err, "failed to patch the release with the appgroup finalizer")
|
|
return statusHelper.Failed(ctx, appGroup, err)
|
|
}
|
|
}
|
|
|
|
// If we have not yet seen this generation, we should reconcile and create the workflow
|
|
// Only do this if we have successfully completed a rollback
|
|
if appGroup.Generation != appGroup.Status.ObservedGeneration {
|
|
// Change the app group spec into a progressing state
|
|
if err := statusHelper.Progressing(ctx, appGroup); err != nil {
|
|
logr.Error(err, "failed to patch the status into a progressing state")
|
|
return statusHelper.Failed(ctx, appGroup, err)
|
|
}
|
|
if err := reconcileHelper.CreateOrUpdate(ctx); err != nil {
|
|
logr.Error(err, "failed to reconcile creating or updating the appgroup")
|
|
return statusHelper.Failed(ctx, appGroup, err)
|
|
}
|
|
}
|
|
|
|
// Update the status based on the current state of the helm charts
|
|
if err := statusHelper.UpdateStatus(ctx, appGroup); err != nil {
|
|
logr.Error(err, "failed to update the status of the app group")
|
|
return statusHelper.Failed(ctx, appGroup, fmt.Errorf("failed to update the status of the progressing application group with err: %v", err))
|
|
}
|
|
|
|
requeueDuration := v1alpha1.GetInterval(appGroup)
|
|
var shouldRemediate bool
|
|
var err error
|
|
|
|
// While ready is progressing, we get the state of the workflow
|
|
if appGroup.Generation != appGroup.Status.LastSucceededGeneration {
|
|
shouldRemediate, requeueDuration, err = statusHelper.UpdateStatusWithWorkflow(ctx, appGroup)
|
|
if err != nil {
|
|
logr.Error(err, "failed to update the status based on the workflow status")
|
|
return statusHelper.Failed(ctx, appGroup, err)
|
|
}
|
|
if !r.DisableRemediation && shouldRemediate {
|
|
if lastSuccessfulSpec := appGroup.GetLastSuccessful(); lastSuccessfulSpec != nil {
|
|
if err := statusHelper.RollingBack(ctx, appGroup); err != nil {
|
|
logr.Error(err, "failed to mark the app group status as rolling back")
|
|
return statusHelper.Failed(ctx, appGroup, err)
|
|
}
|
|
return reconcileHelper.Rollback(ctx, patch, fmt.Errorf(""))
|
|
}
|
|
if err := statusHelper.MarkReversing(ctx, appGroup); err != nil {
|
|
logr.Error(err, "failed to mark the app group status as reversing")
|
|
return statusHelper.Failed(ctx, appGroup, err)
|
|
}
|
|
return reconcileHelper.Reverse(ctx)
|
|
}
|
|
}
|
|
return ctrl.Result{RequeueAfter: requeueDuration}, nil
|
|
}
|
|
|
|
func (r *ApplicationGroupReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
|
return ctrl.NewControllerManagedBy(mgr).
|
|
For(&v1alpha1.ApplicationGroup{}).
|
|
WithEventFilter(predicate.GenerationChangedPredicate{}).
|
|
Complete(r)
|
|
}
|