Skip to content

Commit

Permalink
feat: add helm chart support
Browse files Browse the repository at this point in the history
  • Loading branch information
Skarlso committed Jul 5, 2023
1 parent 5dbdc97 commit 2e9fed8
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 34 deletions.
16 changes: 5 additions & 11 deletions api/v1alpha1/condition_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ const (
// ParseReferencesFailedReason is used when the resource references cannot be parsed.
ParseReferencesFailedReason = "ParseReferencesFailed"

// ReconcileMuationObjectFailed is used when the mutation object cannot be reconciled.
ReconcileMuationObjectFailedReason = "ReconcileMuationObjectFailed"
// ReconcileMutationObjectFailedReason is used when the mutation object cannot be reconciled.
ReconcileMutationObjectFailedReason = "ReconcileMutationObjectFailed"

// SourceReasonNotATarArchiveReason is used when the source resource is not a tar archive.
SourceReasonNotATarArchiveReason = "SourceReasonNotATarArchive"
Expand All @@ -47,18 +47,12 @@ const (
// CreateOrUpdateSnapshotFailedReason is used when the snapshot cannot be created or updated.
CreateOrUpdateSnapshotFailedReason = "CreateOrUpdateSnapshotFailed"

// CreateOrUpdateOCIRepositoryFailedReason is used when the OCIRepository cannot be created or updated.
CreateOrUpdateOCIRepositoryFailedReason = "CreateOrUpdateOCIRepositoryFailed"

// CreateOrUpdateKustomizationFailedReason is used when the Kustomization cannot be created or updated.
CreateOrUpdateKustomizationFailedReason = "CreateOrUpdateKustomizationFailed"

// CreateOrUpdateHelmFailedReason is used when the Kustomization cannot be created or updated.
CreateOrUpdateHelmFailedReason = "CreateOrUpdateHelmFailed"

// CreateRepositoryNameReason is used when the generating a new repository name fails.
CreateRepositoryNameReason = "CreateRepositoryNameFailed"

// PatchSnapshotFailedReason is used when the snapshot cannot be patched.
PatchSnapshotFailedReason = "PatchSnapshotFailed"

// SnapshotFailedReason is used when the snapshot cannot be created.
SnapshotFailedReason = "SnapshotFailed"
)
15 changes: 14 additions & 1 deletion api/v1alpha1/fluxdeployer_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package v1alpha1

import (
helmv1 "github.com/fluxcd/helm-controller/api/v2beta1"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
Expand All @@ -14,9 +15,21 @@ type FluxDeployerSpec struct {
// +required
SourceRef ObjectReference `json:"sourceRef"`

// The interval at which to reconcile the Kustomization.
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$"
// +required
Interval metav1.Duration `json:"interval"`

// +kubebuilder:pruning:PreserveUnknownFields
// +kubebuilder:validation:Schemaless
KustomizationTemplate kustomizev1.KustomizationSpec `json:"kustomizationTemplate"`
// +optional
KustomizationTemplate *kustomizev1.KustomizationSpec `json:"kustomizationTemplate,omitempty"`

// +kubebuilder:pruning:PreserveUnknownFields
// +kubebuilder:validation:Schemaless
// +optional
HelmReleaseTemplate *helmv1.HelmReleaseSpec `json:"helmReleaseTemplate,omitempty"`
}

// FluxDeployerStatus defines the observed state of FluxDeployer
Expand Down
13 changes: 12 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.

3 changes: 2 additions & 1 deletion config/crd/bases/delivery.ocm.software_fluxdeployers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ spec:
spec:
description: FluxDeployerSpec defines the desired state of FluxDeployer
properties:
helmReleaseTemplate:
x-kubernetes-preserve-unknown-fields: true
kustomizationTemplate:
x-kubernetes-preserve-unknown-fields: true
sourceRef:
Expand Down Expand Up @@ -121,7 +123,6 @@ spec:
- name
type: object
required:
- kustomizationTemplate
- sourceRef
type: object
status:
Expand Down
2 changes: 1 addition & 1 deletion controllers/configuration_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ func (r *ConfigurationReconciler) reconcile(ctx context.Context, obj *v1alpha1.C
}

err = fmt.Errorf("failed to reconcile mutation object: %w", err)
conditions.MarkFalse(obj, meta.ReadyCondition, v1alpha1.ReconcileMuationObjectFailedReason, err.Error())
conditions.MarkFalse(obj, meta.ReadyCondition, v1alpha1.ReconcileMutationObjectFailedReason, err.Error())
return ctrl.Result{}, err
}

Expand Down
131 changes: 113 additions & 18 deletions controllers/fluxdeployer_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"fmt"
"time"

helmv1 "github.com/fluxcd/helm-controller/api/v2beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
Expand Down Expand Up @@ -151,24 +152,28 @@ func (r *FluxDeployerReconciler) reconcile(ctx context.Context, obj *v1alpha1.Fl

snapshotURL := fmt.Sprintf("oci://%s/%s", r.RegistryServiceName, snapshotRepo)

// create oci registry
if err := r.reconcileOCIRepo(ctx, obj, snapshotURL, snapshot.Spec.Tag); err != nil {
msg := "failed to create or update oci repository"
logger.Error(err, msg)
conditions.MarkFalse(obj, meta.ReadyCondition, v1alpha1.CreateOrUpdateOCIRepositoryFailedReason, err.Error())
conditions.MarkStalled(obj, v1alpha1.CreateOrUpdateOCIRepositoryFailedReason, err.Error())
event.New(r.EventRecorder, obj, eventv1.EventSeverityError, msg, nil)
return ctrl.Result{}, err
// create kustomization
if obj.Spec.KustomizationTemplate != nil {
// create oci registry
if err := r.createKustomizationSources(ctx, obj, snapshotURL, snapshot.Spec.Tag); err != nil {
msg := "failed to create kustomization sources"
logger.Error(err, msg)
conditions.MarkFalse(obj, meta.ReadyCondition, v1alpha1.CreateOrUpdateKustomizationFailedReason, err.Error())
conditions.MarkStalled(obj, v1alpha1.CreateOrUpdateKustomizationFailedReason, err.Error())
event.New(r.EventRecorder, obj, eventv1.EventSeverityError, msg, nil)
return ctrl.Result{}, err
}
}

// create kustomization
if err = r.reconcileKustomization(ctx, obj); err != nil {
msg := "failed to create or update kustomization"
logger.Error(err, msg)
conditions.MarkFalse(obj, meta.ReadyCondition, v1alpha1.CreateOrUpdateOCIRepositoryFailedReason, err.Error())
conditions.MarkStalled(obj, v1alpha1.CreateOrUpdateOCIRepositoryFailedReason, err.Error())
event.New(r.EventRecorder, obj, eventv1.EventSeverityError, msg, nil)
return ctrl.Result{}, err
if obj.Spec.HelmReleaseTemplate != nil {
if err := r.createHelmSources(ctx, obj, snapshotURL); err != nil {
msg := "failed to create helm sources"
logger.Error(err, msg)
conditions.MarkFalse(obj, meta.ReadyCondition, v1alpha1.CreateOrUpdateHelmFailedReason, err.Error())
conditions.MarkStalled(obj, v1alpha1.CreateOrUpdateHelmFailedReason, err.Error())
event.New(r.EventRecorder, obj, eventv1.EventSeverityError, msg, nil)
return ctrl.Result{}, err
}
}

msg := fmt.Sprintf("FluxDeployer '%s' is ready", obj.Name)
Expand All @@ -177,6 +182,32 @@ func (r *FluxDeployerReconciler) reconcile(ctx context.Context, obj *v1alpha1.Fl
return ctrl.Result{}, nil
}

func (r *FluxDeployerReconciler) createKustomizationSources(ctx context.Context, obj *v1alpha1.FluxDeployer, url, tag string) error {
// create oci registry
if err := r.reconcileOCIRepo(ctx, obj, url, tag); err != nil {
return fmt.Errorf("failed to create OCI repository: %w", err)
}

if err := r.reconcileKustomization(ctx, obj); err != nil {
return fmt.Errorf("failed to create Kustomization object :%w", err)
}

return nil
}

func (r *FluxDeployerReconciler) createHelmSources(ctx context.Context, obj *v1alpha1.FluxDeployer, url string) error {
// create oci registry
if err := r.reconcileHelmRepository(ctx, obj, url); err != nil {
return fmt.Errorf("failed to create OCI repository: %w", err)
}

if err := r.reconcileHelmRelease(ctx, obj); err != nil {
return fmt.Errorf("failed to create Kustomization object :%w", err)
}

return nil
}

func (r *FluxDeployerReconciler) reconcileOCIRepo(ctx context.Context, obj *v1alpha1.FluxDeployer, url, tag string) error {
ociRepoCR := &sourcev1beta2.OCIRepository{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -192,7 +223,7 @@ func (r *FluxDeployerReconciler) reconcileOCIRepo(ctx context.Context, obj *v1al
}
}
ociRepoCR.Spec = sourcev1beta2.OCIRepositorySpec{
Interval: obj.Spec.KustomizationTemplate.Interval,
Interval: obj.Spec.Interval,
CertSecretRef: &meta.LocalObjectReference{
Name: r.CertSecretName,
},
Expand Down Expand Up @@ -225,7 +256,7 @@ func (r *FluxDeployerReconciler) reconcileKustomization(ctx context.Context, obj
return fmt.Errorf("failed to set owner reference on oci repository source: %w", err)
}
}
kust.Spec = obj.Spec.KustomizationTemplate
kust.Spec = *obj.Spec.KustomizationTemplate
kust.Spec.SourceRef.Kind = sourcev1beta2.OCIRepositoryKind
kust.Spec.SourceRef.Namespace = obj.GetNamespace()
kust.Spec.SourceRef.Name = obj.GetName()
Expand Down Expand Up @@ -314,3 +345,67 @@ func (r *FluxDeployerReconciler) findObjects(sourceKey string) func(client.Objec
return requests
}
}

func (r *FluxDeployerReconciler) reconcileHelmRelease(ctx context.Context, obj *deliveryv1alpha1.FluxDeployer) error {
helmRelease := &helmv1.HelmRelease{
ObjectMeta: metav1.ObjectMeta{
Namespace: obj.GetNamespace(),
Name: obj.GetName(),
},
}

_, err := controllerutil.CreateOrUpdate(ctx, r.Client, helmRelease, func() error {
if helmRelease.ObjectMeta.CreationTimestamp.IsZero() {
if err := controllerutil.SetOwnerReference(obj, helmRelease, r.Scheme); err != nil {
return fmt.Errorf("failed to set owner reference on oci repository source: %w", err)
}
}
helmRelease.Spec = *obj.Spec.HelmReleaseTemplate
helmRelease.Spec.Chart.Spec.SourceRef = helmv1.CrossNamespaceObjectReference{
Kind: "HelmRepository",
Name: obj.GetName(),
Namespace: obj.GetNamespace(),
}
return nil
})

if err != nil {
return fmt.Errorf("failed to create reconcile kustomization: %w", err)
}

obj.Status.Kustomization = helmRelease.GetNamespace() + "/" + helmRelease.GetName()

return nil
}

func (r *FluxDeployerReconciler) reconcileHelmRepository(ctx context.Context, obj *deliveryv1alpha1.FluxDeployer, url string) error {
helmRepository := &sourcev1beta2.HelmRepository{
ObjectMeta: metav1.ObjectMeta{
Namespace: obj.GetNamespace(),
Name: obj.GetName(),
},
}

_, err := controllerutil.CreateOrUpdate(ctx, r.Client, helmRepository, func() error {
if helmRepository.ObjectMeta.CreationTimestamp.IsZero() {
if err := controllerutil.SetOwnerReference(obj, helmRepository, r.Scheme); err != nil {
return fmt.Errorf("failed to set owner reference on helm repository source: %w", err)
}
}
helmRepository.Spec = sourcev1beta2.HelmRepositorySpec{
Interval: obj.Spec.Interval,
SecretRef: &meta.LocalObjectReference{
Name: r.CertSecretName,
},
URL: url,
Type: "oci",
}
return nil
})

if err != nil {
return fmt.Errorf("failed to create reconcile oci repo: %w", err)
}

return nil
}
2 changes: 1 addition & 1 deletion controllers/localization_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ func (r *LocalizationReconciler) reconcile(ctx context.Context, obj *v1alpha1.Lo
}

err = fmt.Errorf("failed to reconcile mutation object: %w", err)
conditions.MarkFalse(obj, meta.ReadyCondition, v1alpha1.ReconcileMuationObjectFailedReason, err.Error())
conditions.MarkFalse(obj, meta.ReadyCondition, v1alpha1.ReconcileMutationObjectFailedReason, err.Error())
event.New(r.EventRecorder, obj, eventv1.EventSeverityError, err.Error(), nil)

return ctrl.Result{}, err
Expand Down

0 comments on commit 2e9fed8

Please sign in to comment.