зеркало из https://github.com/Azure/orkestra.git
Added comments to the reconciler and types (#142)
Signed-off-by: Nitish Malhotra <nitishm@microsoft.com> Co-authored-by: Nitish Malhotra <nitishm@microsoft.com>
This commit is contained in:
Родитель
07e92c982e
Коммит
85c37d828d
|
@ -10,12 +10,13 @@ import (
|
|||
|
||||
// ApplicationSpec defines the desired state of Application
|
||||
type ApplicationSpec struct {
|
||||
// Namespace to which the HelmRelease object will be deployed
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
// Subcharts provides the dependency order among the subcharts of the application
|
||||
Subcharts []DAG `json:"subcharts,omitempty"`
|
||||
GroupID string `json:"groupID,omitempty"`
|
||||
|
||||
// ChartRepoNickname is used to lookup the repository config in the registries config map
|
||||
ChartRepoNickname string `json:"repo,omitempty"`
|
||||
|
||||
// XXX (nitishm) **IMPORTANT**: DO NOT USE HelmReleaseSpec.Values!!!
|
||||
// ApplicationSpec.Overlays field replaces HelmReleaseSpec.Values field.
|
||||
// Setting the HelmReleaseSpec.Values field will not reflect in the deployed Application object
|
||||
|
@ -36,37 +37,56 @@ type ApplicationSpec struct {
|
|||
// Artifactory for instance utilizes folders to store charts
|
||||
RepoPath string `json:"repoPath,omitempty"`
|
||||
|
||||
// Inline HelmReleaseSpec from the flux helm-operator package
|
||||
helmopv1.HelmReleaseSpec `json:",inline"`
|
||||
}
|
||||
|
||||
// ChartStatus denotes the current status of the Application Reconciliation
|
||||
// ChartStatus shows the current status of the Application Reconciliation process
|
||||
type ChartStatus struct {
|
||||
Phase helmopv1.HelmReleasePhase `json:"phase,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Staged bool `json:"staged,omitempty"`
|
||||
// Phase reflects the current state of the HelmRelease
|
||||
Phase helmopv1.HelmReleasePhase `json:"phase,omitempty"`
|
||||
// Error string from the error during reconciliation (if any)
|
||||
Error string `json:"error,omitempty"`
|
||||
// Version of the chart/subchart
|
||||
Version string `json:"version,omitempty"`
|
||||
// Staged if true denotes that the chart/subchart has been pushed to the
|
||||
// staging helm repo
|
||||
Staged bool `json:"staged,omitempty"`
|
||||
}
|
||||
|
||||
// ApplicationGroupSpec defines the desired state of ApplicationGroup
|
||||
type ApplicationGroupSpec struct {
|
||||
// Applications that make up the application group
|
||||
Applications []Application `json:"applications,omitempty"`
|
||||
}
|
||||
|
||||
// Application spec and dependency on other applications
|
||||
type Application struct {
|
||||
DAG `json:",inline"`
|
||||
// DAG contains the dependency information
|
||||
DAG `json:",inline"`
|
||||
// Spec contains the application spec including the chart info and overlay values
|
||||
Spec ApplicationSpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
// DAG contains the dependency information
|
||||
type DAG struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
// Name of the application
|
||||
Name string `json:"name,omitempty"`
|
||||
// Dependencies on other applications by name
|
||||
Dependencies []string `json:"dependencies,omitempty"`
|
||||
}
|
||||
|
||||
// ApplicationStatus shows the current status of the application helm release
|
||||
type ApplicationStatus struct {
|
||||
Name string `json:"name"`
|
||||
// Name of the application
|
||||
Name string `json:"name"`
|
||||
// ChartStatus for the application helm chart
|
||||
ChartStatus `json:",inline"`
|
||||
Subcharts map[string]ChartStatus `json:"subcharts,omitempty"`
|
||||
// Subcharts contains the subchart chart status
|
||||
Subcharts map[string]ChartStatus `json:"subcharts,omitempty"`
|
||||
}
|
||||
|
||||
// ReconciliationPhase is an enum
|
||||
type ReconciliationPhase string
|
||||
|
||||
const (
|
||||
|
@ -79,11 +99,17 @@ const (
|
|||
|
||||
// ApplicationGroupStatus defines the observed state of ApplicationGroup
|
||||
type ApplicationGroupStatus struct {
|
||||
Checksums map[string]string `json:"checksums,omitempty"`
|
||||
// Checksums for each application are calculated from the application spec
|
||||
// The status/metadata information is ignored
|
||||
Checksums map[string]string `json:"checksums,omitempty"`
|
||||
// Applications status
|
||||
Applications []ApplicationStatus `json:"status,omitempty"`
|
||||
Phase ReconciliationPhase `json:"phase,omitempty"`
|
||||
Update bool `json:"update,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
// Phase is the reconciliation phase
|
||||
Phase ReconciliationPhase `json:"phase,omitempty"`
|
||||
// Update is an internal flag used to trigger a workflow update
|
||||
Update bool `json:"update,omitempty"`
|
||||
// Error string from errors during reconciliation
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
|
|
@ -66,6 +66,9 @@ type ApplicationGroupReconciler struct {
|
|||
// Recorder generates kubernetes events
|
||||
Recorder record.EventRecorder
|
||||
|
||||
// lastSuccessfulApplicationGroup holds the applicationgroup spec body from the last
|
||||
// successful reconciliation of the ApplicationGroup. This is set after every successful
|
||||
// reconciliation.
|
||||
lastSuccessfulApplicationGroup *orkestrav1alpha1.ApplicationGroup
|
||||
}
|
||||
|
||||
|
@ -89,6 +92,9 @@ func (r *ApplicationGroupReconciler) Reconcile(req ctrl.Request) (ctrl.Result, e
|
|||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// Check if this is an update event to the ApplicationGroup
|
||||
// in which case unmarshal the last successful spec into a
|
||||
// variable
|
||||
if appGroup.GetAnnotations() != nil {
|
||||
last := &orkestrav1alpha1.ApplicationGroup{}
|
||||
if s, ok := appGroup.Annotations[lastSuccessfulApplicationGroupKey]; ok {
|
||||
|
@ -97,13 +103,17 @@ func (r *ApplicationGroupReconciler) Reconcile(req ctrl.Request) (ctrl.Result, e
|
|||
}
|
||||
}
|
||||
|
||||
// handle deletes if deletion timestamp is non-zero
|
||||
// handle deletes if deletion timestamp is non-zero.
|
||||
// controller-runtime cannot guarantee the order of events
|
||||
// , so it is upto us to determine the type of event
|
||||
if !appGroup.DeletionTimestamp.IsZero() {
|
||||
// If finalizer is found, remove it and requeue
|
||||
if appGroup.Finalizers != nil {
|
||||
logr.Info("cleaning up the applicationgroup resource")
|
||||
|
||||
// Reverse the entire workflow to remove all the Helm Releases
|
||||
|
||||
// unset the last successful spec annotation
|
||||
r.lastSuccessfulApplicationGroup = nil
|
||||
if _, ok := appGroup.Annotations[lastSuccessfulApplicationGroupKey]; ok {
|
||||
appGroup.Annotations[lastSuccessfulApplicationGroupKey] = ""
|
||||
|
@ -128,6 +138,9 @@ func (r *ApplicationGroupReconciler) Reconcile(req ctrl.Request) (ctrl.Result, e
|
|||
return ctrl.Result{Requeue: true}, nil
|
||||
}
|
||||
|
||||
// If the (needs) Rollback phase is present in the reconciled version,
|
||||
// we must rollback the application group to the last successful spec.
|
||||
// This should only happen on updates and not during installs.
|
||||
if appGroup.Status.Phase == orkestrav1alpha1.Rollback {
|
||||
logr.Info("Rolling back to last successful application group spec")
|
||||
appGroup.Spec = r.lastSuccessfulApplicationGroup.DeepCopy().Spec
|
||||
|
@ -145,8 +158,10 @@ func (r *ApplicationGroupReconciler) Reconcile(req ctrl.Request) (ctrl.Result, e
|
|||
// handle first time install and subsequent updates
|
||||
checksums, err := pkg.Checksum(&appGroup)
|
||||
if err != nil {
|
||||
// determine if the spec has changed
|
||||
if errors.Is(err, pkg.ErrChecksumAppGroupSpecMismatch) {
|
||||
if appGroup.Status.Checksums != nil {
|
||||
// flag this as requiring workflow updates for the reconciler
|
||||
appGroup.Status.Update = true
|
||||
}
|
||||
requeue, err = r.reconcile(ctx, logr, r.WorkflowNS, &appGroup)
|
||||
|
@ -184,7 +199,13 @@ func (r *ApplicationGroupReconciler) Reconcile(req ctrl.Request) (ctrl.Result, e
|
|||
|
||||
appGroup.Status.Checksums = checksums
|
||||
|
||||
// Calculate the cumulative status of the generated Workflow
|
||||
// and the generated HelmRelease objects
|
||||
|
||||
// Lookup Workflow by ownership and heritage labels
|
||||
// We are expecting to find at most one workflow in
|
||||
// the returned list that is associated with this
|
||||
// ApplicationGroup object.
|
||||
wfs := v1alpha12.WorkflowList{}
|
||||
listOption := client.MatchingLabels{
|
||||
workflow.OwnershipLabel: appGroup.Name,
|
||||
|
@ -198,13 +219,13 @@ func (r *ApplicationGroupReconciler) Reconcile(req ctrl.Request) (ctrl.Result, e
|
|||
}
|
||||
|
||||
if wfs.Items.Len() == 0 {
|
||||
// appGroup.Status.Phase = wfs.Items[0].Status.Phase
|
||||
err = fmt.Errorf("listed workflows len is 0")
|
||||
logr.Error(err, "no associated workflow found")
|
||||
requeue = false
|
||||
return r.handleResponseAndEvent(ctx, logr, appGroup, requeue, err)
|
||||
}
|
||||
|
||||
// determine the associated/generated workflow status
|
||||
var phase orkestrav1alpha1.ReconciliationPhase
|
||||
wfStatus := wfs.Items[0].Status.Phase
|
||||
switch wfStatus {
|
||||
|
@ -249,6 +270,7 @@ func (r *ApplicationGroupReconciler) Reconcile(req ctrl.Request) (ctrl.Result, e
|
|||
}
|
||||
}
|
||||
|
||||
// Update each application status using the HelmRelease status
|
||||
v := make([]orkestrav1alpha1.ApplicationStatus, 0)
|
||||
for _, app := range appGroup.Status.Applications {
|
||||
app.ChartStatus.Phase = helmReleaseStatusMap[app.Name]
|
||||
|
@ -333,28 +355,6 @@ func (r *ApplicationGroupReconciler) handleResponseAndEvent(ctx context.Context,
|
|||
return reconcile.Result{Requeue: requeue}, err
|
||||
}
|
||||
|
||||
// func isDependenciesEmbedded(ch *chart.Chart) bool {
|
||||
// // TODO (nitishm) This does not support a mix of remote and embedded dependency subcharts
|
||||
// isURI := false
|
||||
// for _, d := range ch.Metadata.Dependencies {
|
||||
// if _, err := url.ParseRequestURI(d.Repository); err == nil {
|
||||
// // If this is an "assembled" chart (https://helm.sh/docs/chart_best_practices/dependencies/#versions) we must stage the embedded subchart
|
||||
// if strings.Contains(d.Repository, "file://") {
|
||||
// isURI = false
|
||||
// break
|
||||
// }
|
||||
// isURI = true
|
||||
// }
|
||||
// }
|
||||
|
||||
// if !isURI {
|
||||
// if len(ch.Dependencies()) > 0 {
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
|
||||
func initApplications(appGroup *orkestrav1alpha1.ApplicationGroup) {
|
||||
// Initialize the Status fields if not already setup
|
||||
if len(appGroup.Status.Applications) == 0 {
|
||||
|
@ -382,8 +382,11 @@ func initApplications(appGroup *orkestrav1alpha1.ApplicationGroup) {
|
|||
}
|
||||
|
||||
func (r *ApplicationGroupReconciler) handleRemediation(ctx context.Context, logr logr.Logger, g orkestrav1alpha1.ApplicationGroup, err error) (ctrl.Result, error) {
|
||||
// Rollback to previous successful spec
|
||||
// Rollback to previous successful spec since the annotation was set and this is
|
||||
// an UPDATE event
|
||||
if r.lastSuccessfulApplicationGroup != nil {
|
||||
// If this is a HelmRelease failure then we must remediate by cleaning up
|
||||
// all the helm releases deployed by the workflow and helm operator
|
||||
if errors.Is(err, ErrHelmReleaseInFailureStatus) {
|
||||
// Delete the HelmRelease(s) - parent and subchart(s)
|
||||
// Lookup charts using the label selector.
|
||||
|
@ -412,6 +415,9 @@ func (r *ApplicationGroupReconciler) handleRemediation(ctx context.Context, logr
|
|||
}
|
||||
}
|
||||
}
|
||||
// mark the object as requiring rollback so that we can rollback
|
||||
// to the previous versions of all the applications in the ApplicationGroup
|
||||
// using the last successful spec
|
||||
g.Status.Phase = orkestrav1alpha1.Rollback
|
||||
_ = r.Status().Update(ctx, &g)
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
)
|
||||
|
||||
func (c *Client) PullChart(l logr.Logger, repoKey, repoPath, chartName, version string) (string, *chart.Chart, error) {
|
||||
// logic is derived from the "helm pull" command from the helm cli package
|
||||
l.WithValues("repo-key", repoKey, "chart-name", chartName, "chart-version", version)
|
||||
|
||||
l.V(3).Info("pulling chart")
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
|
||||
// PushChart pushes the chart to the repository specified by the repoKey. The repository setting is fetched from the associated registry config file
|
||||
func (c *Client) PushChart(l logr.Logger, repoKey, pkgPath string, ch *chart.Chart) error {
|
||||
// logic is derived from the "helm push" extension from the chartmuseum folks
|
||||
chartName := ch.Name()
|
||||
version := ch.Metadata.Version
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче