Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wait for templates creation in Release controller #484

Merged
merged 1 commit into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions api/v1alpha1/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)

const (
// SucceededReason indicates a condition or event observed a success, for example when declared desired state
// matches actual state, or a performed action succeeded.
SucceededReason string = "Succeeded"

// FailedReason indicates a condition or event observed a failure, for example when declared state does not match
// actual state, or a performed action failed.
FailedReason string = "Failed"

// ProgressingReason indicates a condition or event observed progression, for example when the reconciliation of a
// resource or an action has started.
ProgressingReason string = "Progressing"
)

type (
// Providers hold different types of CAPI providers.
Providers struct {
Expand Down
14 changes: 0 additions & 14 deletions api/v1alpha1/managedcluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,6 @@ const (
ReadyCondition string = "Ready"
)

const (
// SucceededReason indicates a condition or event observed a success, for example when declared desired state
// matches actual state, or a performed action succeeded.
SucceededReason string = "Succeeded"

// FailedReason indicates a condition or event observed a failure, for example when declared state does not match
// actual state, or a performed action failed.
FailedReason string = "Failed"

// ProgressingReason indicates a condition or event observed progression, for example when the reconciliation of a
// resource or an action has started.
ProgressingReason string = "Progressing"
)

// ManagedClusterSpec defines the desired state of ManagedCluster
type ManagedClusterSpec struct {
// Config allows to provide parameters for template customization.
Expand Down
11 changes: 9 additions & 2 deletions api/v1alpha1/release_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
ReleaseKind = "Release"

// TemplatesCreatedCondition indicates that all templates associated with the Release are created.
TemplatesCreatedCondition = "TemplatesCreated"
)

// ReleaseSpec defines the desired state of Release
type ReleaseSpec struct {
// Version of the HMC Release in the semver format.
Expand Down Expand Up @@ -52,12 +59,12 @@ func (in *Release) ProviderTemplate(name string) string {

// ReleaseStatus defines the observed state of Release
type ReleaseStatus struct {
// Templates indicates the status of templates associated with the Release.
Templates ComponentStatus `json:"templates,omitempty"`
// Conditions contains details for the current state of the Release
Conditions []metav1.Condition `json:"conditions,omitempty"`
// Ready indicates whether HMC is ready to be upgraded to this Release.
Ready bool `json:"ready,omitempty"`
// ObservedGeneration is the last observed generation.
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
}

// +kubebuilder:object:root=true
Expand Down
1 change: 0 additions & 1 deletion api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

92 changes: 59 additions & 33 deletions internal/controller/release_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,18 @@ import (
"fmt"

hcv2 "github.com/fluxcd/helm-controller/api/v2"
fluxmeta "github.com/fluxcd/pkg/apis/meta"
fluxconditions "github.com/fluxcd/pkg/runtime/conditions"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/storage/driver"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
Expand Down Expand Up @@ -61,18 +65,31 @@ type ReleaseReconciler struct {
CreateTemplates bool
}

func (r *ReleaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
func (r *ReleaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) {
l := ctrl.LoggerFrom(ctx).WithValues("controller", "ReleaseController")
l.Info("Reconciling Release")
defer l.Info("Release reconcile is finished")

err := r.reconcileHMCTemplates(ctx, req)
release := &hmc.Release{}
if req.Name != "" {
if err := r.Client.Get(ctx, req.NamespacedName, release); err != nil {
l.Error(err, "failed to get Release")
return ctrl.Result{}, err
}
defer func() {
release.Status.ObservedGeneration = release.Generation
err = errors.Join(err, r.Status().Update(ctx, release))
}()
}

err = r.reconcileHMCTemplates(ctx, release.Name, release.Spec.Version, release.UID)
r.updateTemplatesCondition(release, err)
if err != nil {
l.Error(err, "failed to reconcile HMC Templates")
return ctrl.Result{}, err
}

if initialReconcile(req) {
if release.Name == "" {
if err := r.ensureManagement(ctx); err != nil {
l.Error(err, "failed to get or create Management object")
return ctrl.Result{}, err
Expand All @@ -81,8 +98,23 @@ func (r *ReleaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
return ctrl.Result{}, nil
}

func initialReconcile(req ctrl.Request) bool {
return req.Name == ""
func (r *ReleaseReconciler) updateTemplatesCondition(release *hmc.Release, err error) {
condition := metav1.Condition{
Type: hmc.TemplatesCreatedCondition,
Status: metav1.ConditionTrue,
ObservedGeneration: release.Generation,
Reason: hmc.SucceededReason,
Message: "All templates have been created",
}
if !r.CreateTemplates {
condition.Message = "Templates creation is disabled"
}
if err != nil {
condition.Status = metav1.ConditionFalse
condition.Message = err.Error()
condition.Reason = hmc.FailedReason
}
meta.SetStatusCondition(&release.Status.Conditions, condition)
}

func (r *ReleaseReconciler) ensureManagement(ctx context.Context) error {
Expand Down Expand Up @@ -150,45 +182,36 @@ func (r *ReleaseReconciler) ensureManagement(ctx context.Context) error {
return nil
}

func (r *ReleaseReconciler) reconcileHMCTemplates(ctx context.Context, req ctrl.Request) error {
func (r *ReleaseReconciler) reconcileHMCTemplates(ctx context.Context, releaseName, releaseVersion string, releaseUID types.UID) error {
l := ctrl.LoggerFrom(ctx)
if !r.CreateTemplates {
l.Info("Templates creation is disabled")
return nil
}
if initialReconcile(req) && !r.CreateRelease {
if releaseName == "" && !r.CreateRelease {
l.Info("Initial creation of HMC Release is skipped")
return nil
}
releaseName := utils.ReleaseNameFromVersion(build.Version)
version := build.Version
var ownerRefs []metav1.OwnerReference
release := &hmc.Release{}
if !initialReconcile(req) {
if err := r.Client.Get(ctx, req.NamespacedName, release); err != nil {
l.Error(err, "failed to get Release")
if releaseName == "" {
releaseName = utils.ReleaseNameFromVersion(build.Version)
releaseVersion = build.Version
err := helm.ReconcileHelmRepository(ctx, r.Client, defaultRepoName, r.SystemNamespace, r.DefaultRegistryConfig.HelmRepositorySpec())
if err != nil {
l.Error(err, "Failed to reconcile default HelmRepository", "namespace", r.SystemNamespace)
return err
}
releaseName = req.Name
version = release.Spec.Version
} else {
ownerRefs = []metav1.OwnerReference{
{
APIVersion: hmc.GroupVersion.String(),
Kind: release.Kind,
Name: release.Name,
UID: release.UID,
Kind: hmc.ReleaseKind,
Name: releaseName,
UID: releaseUID,
},
}
}

if initialReconcile(req) {
err := helm.ReconcileHelmRepository(ctx, r.Client, defaultRepoName, r.SystemNamespace, r.DefaultRegistryConfig.HelmRepositorySpec())
if err != nil {
l.Error(err, "Failed to reconcile default HelmRepository", "namespace", r.SystemNamespace)
return err
}
}

hmcTemplatesName := utils.TemplatesChartFromReleaseName(releaseName)
helmChart := &sourcev1.HelmChart{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -205,7 +228,7 @@ func (r *ReleaseReconciler) reconcileHMCTemplates(ctx context.Context, req ctrl.
helmChart.Labels[hmc.HMCManagedLabelKey] = hmc.HMCManagedLabelValue
helmChart.Spec = sourcev1.HelmChartSpec{
Chart: r.HMCTemplatesChartName,
Version: version,
Version: releaseVersion,
SourceRef: sourcev1.LocalHelmChartSourceReference{
Kind: sourcev1.HelmRepositoryKind,
Name: defaultRepoName,
Expand All @@ -221,10 +244,6 @@ func (r *ReleaseReconciler) reconcileHMCTemplates(ctx context.Context, req ctrl.
l.Info(fmt.Sprintf("Successfully %s %s/%s HelmChart", operation, r.SystemNamespace, hmcTemplatesName))
}

if _, err := helm.ArtifactReady(helmChart); err != nil {
return fmt.Errorf("HelmChart %s/%s Artifact is not ready: %w", r.SystemNamespace, hmcTemplatesName, err)
}

opts := helm.ReconcileHelmReleaseOpts{
ChartRef: &hcv2.CrossNamespaceSourceReference{
Kind: helmChart.Kind,
Expand All @@ -233,7 +252,7 @@ func (r *ReleaseReconciler) reconcileHMCTemplates(ctx context.Context, req ctrl.
},
}

if initialReconcile(req) {
if releaseName == "" {
createReleaseValues := map[string]any{
"createRelease": true,
}
Expand All @@ -244,13 +263,20 @@ func (r *ReleaseReconciler) reconcileHMCTemplates(ctx context.Context, req ctrl.
opts.Values = &apiextensionsv1.JSON{Raw: raw}
}

_, operation, err = helm.ReconcileHelmRelease(ctx, r.Client, hmcTemplatesName, r.SystemNamespace, opts)
hr, operation, err := helm.ReconcileHelmRelease(ctx, r.Client, hmcTemplatesName, r.SystemNamespace, opts)
if err != nil {
return err
}
if operation == controllerutil.OperationResultCreated || operation == controllerutil.OperationResultUpdated {
l.Info(fmt.Sprintf("Successfully %s %s/%s HelmRelease", operation, r.SystemNamespace, hmcTemplatesName))
}
hrReadyCondition := fluxconditions.Get(hr, fluxmeta.ReadyCondition)
if hrReadyCondition == nil || hrReadyCondition.ObservedGeneration != hr.Generation {
return fmt.Errorf("HelmRelease %s/%s is not ready yet. Waiting for reconciliation", r.SystemNamespace, hmcTemplatesName)
}
if hrReadyCondition.Status == metav1.ConditionFalse {
return fmt.Errorf("HelmRelease %s/%s is not ready yet. %s", r.SystemNamespace, hmcTemplatesName, hrReadyCondition.Message)
}
return nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,26 +145,14 @@ spec:
- type
type: object
type: array
observedGeneration:
description: ObservedGeneration is the last observed generation.
format: int64
type: integer
ready:
description: Ready indicates whether HMC is ready to be upgraded to
this Release.
type: boolean
templates:
description: Templates indicates the status of templates associated
with the Release.
properties:
error:
description: Error stores as error message in case of failed installation
type: string
success:
description: Success represents if a component installation was
successful
type: boolean
template:
description: Template is the name of the Template associated with
this component.
type: string
type: object
type: object
type: object
served: true
Expand Down
6 changes: 6 additions & 0 deletions templates/provider/hmc/templates/rbac/controller/roles.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ rules:
- get
- list
- watch
- apiGroups:
- hmc.mirantis.com
resources:
- releases/status
verbs:
- update
- apiGroups:
- hmc.mirantis.com
resources:
Expand Down