Skip to content

Commit

Permalink
separate tests for dry-run and normal reconciliation
Browse files Browse the repository at this point in the history
Signed-off-by: Artem Bortnikov <[email protected]>
  • Loading branch information
BROngineer committed Dec 27, 2024
1 parent bf5cf89 commit 8010dd2
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 25 deletions.
144 changes: 122 additions & 22 deletions internal/controller/clusterdeployment_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,18 @@ import (
"time"

hcv2 "github.com/fluxcd/helm-controller/api/v2"
meta2 "github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart"
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/rest"
"sigs.k8s.io/cluster-api/api/v1beta1"
. "sigs.k8s.io/controller-runtime/pkg/envtest/komega"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

Expand All @@ -37,7 +39,7 @@ import (

type fakeHelmActor struct{}

func (f *fakeHelmActor) DownloadChartFromArtifact(_ context.Context, _ *sourcev1.Artifact) (*chart.Chart, error) {
func (*fakeHelmActor) DownloadChartFromArtifact(_ context.Context, _ *sourcev1.Artifact) (*chart.Chart, error) {
return &chart.Chart{
Metadata: &chart.Metadata{
APIVersion: "v2",
Expand All @@ -47,11 +49,11 @@ func (f *fakeHelmActor) DownloadChartFromArtifact(_ context.Context, _ *sourcev1
}, nil
}

func (f *fakeHelmActor) InitializeConfiguration(clusterDeployment *hmc.ClusterDeployment, _ action.DebugLog) (*action.Configuration, error) {
func (*fakeHelmActor) InitializeConfiguration(_ *hmc.ClusterDeployment, _ action.DebugLog) (*action.Configuration, error) {
return &action.Configuration{}, nil
}

func (f *fakeHelmActor) EnsureReleaseWithValues(_ context.Context, _ *action.Configuration, _ *chart.Chart, _ *hmc.ClusterDeployment) error {
func (*fakeHelmActor) EnsureReleaseWithValues(_ context.Context, _ *action.Configuration, _ *chart.Chart, _ *hmc.ClusterDeployment) error {
return nil
}

Expand All @@ -76,7 +78,7 @@ var _ = Describe("ClusterDeployment Controller", func() {
)

BeforeEach(func() {
By("Ensure namespace", func() {
By("ensure namespace", func() {
namespace = corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "test-namespace-",
Expand All @@ -85,7 +87,7 @@ var _ = Describe("ClusterDeployment Controller", func() {
Expect(k8sClient.Create(ctx, &namespace)).To(Succeed())
})

By("Ensure HelmRepository resource", func() {
By("ensure HelmRepository resource", func() {
helmRepo = sourcev1.HelmRepository{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "test-repository-",
Expand All @@ -104,7 +106,7 @@ var _ = Describe("ClusterDeployment Controller", func() {
Expect(k8sClient.Create(ctx, &helmRepo)).To(Succeed())
})

By("Ensure HelmChart resources", func() {
By("ensure HelmChart resources", func() {
clusterTemplateHelmChart = sourcev1.HelmChart{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "test-cluster-template-chart-",
Expand Down Expand Up @@ -146,7 +148,7 @@ var _ = Describe("ClusterDeployment Controller", func() {
Expect(k8sClient.Create(ctx, &serviceTemplateHelmChart)).To(Succeed())
})

By("Ensure ClusterTemplate resource", func() {
By("ensure ClusterTemplate resource", func() {
clusterTemplate = hmc.ClusterTemplate{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "test-cluster-template-",
Expand All @@ -168,16 +170,13 @@ var _ = Describe("ClusterDeployment Controller", func() {
TemplateValidationStatus: hmc.TemplateValidationStatus{
Valid: true,
},
Config: &apiextensionsv1.JSON{
Raw: []byte(`{"foo":"bar"}`),
},
},
Providers: hmc.Providers{"infrastructure-aws"},
}
Expect(k8sClient.Status().Update(ctx, &clusterTemplate)).To(Succeed())
})

By("Ensure ServiceTemplate resource", func() {
By("ensure ServiceTemplate resource", func() {
serviceTemplate = hmc.ServiceTemplate{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "test-service-template-",
Expand Down Expand Up @@ -209,7 +208,7 @@ var _ = Describe("ClusterDeployment Controller", func() {
Expect(k8sClient.Status().Update(ctx, &serviceTemplate)).To(Succeed())
})

By("Ensure Credential resource", func() {
By("ensure Credential resource", func() {
credential = hmc.Credential{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "test-credential-",
Expand Down Expand Up @@ -256,7 +255,7 @@ var _ = Describe("ClusterDeployment Controller", func() {
})

AfterEach(func() {
By("Cleanup", func() {
By("cleanup", func() {
controllerReconciler := &ClusterDeploymentReconciler{
Client: mgrClient,
HelmActor: &fakeHelmActor{},
Expand All @@ -274,32 +273,36 @@ var _ = Describe("ClusterDeployment Controller", func() {
})
})

It("should successfully reconcile the resource", func() {
By("Reconciling the created resource")
It("should successfully dry-run reconciliation", func() {
controllerReconciler := &ClusterDeploymentReconciler{
Client: mgrClient,
HelmActor: &fakeHelmActor{},
Config: &rest.Config{},
}

By("Ensure finalizer is added", func() {
By("patching ClusterDeployment resource to dry-run mode", func() {
clusterDeployment.Spec.DryRun = true
Expect(k8sClient.Update(ctx, &clusterDeployment)).To(Succeed())
})

By("ensuring finalizer is added", func() {
Eventually(func(g Gomega) {
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: clusterDeploymentKey,
})
Expect(err).NotTo(HaveOccurred())
g.Expect(err).NotTo(HaveOccurred())
g.Expect(Object(&clusterDeployment)()).Should(SatisfyAll(
HaveField("Finalizers", ContainElement(hmc.ClusterDeploymentFinalizer)),
))
}).Should(Succeed())
})

By("Reconciling resource with finalizer", func() {
By("reconciling resource with finalizer", func() {
Eventually(func(g Gomega) {
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: clusterDeploymentKey,
})
Expect(err).To(HaveOccurred())
g.Expect(err).To(HaveOccurred())
g.Expect(Object(&clusterDeployment)()).Should(SatisfyAll(
HaveField("Status.Conditions", ContainElement(SatisfyAll(
HaveField("Type", hmc.TemplateReadyCondition),
Expand All @@ -311,7 +314,7 @@ var _ = Describe("ClusterDeployment Controller", func() {
}).Should(Succeed())
})

By("Reconciling when dependencies are not in valid state", func() {
By("reconciling when dependencies are not in valid state", func() {
Eventually(func(g Gomega) {
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: clusterDeploymentKey,
Expand All @@ -321,7 +324,7 @@ var _ = Describe("ClusterDeployment Controller", func() {
}).Should(Succeed())
})

By("Patching ClusterTemplate and corresponding HelmChart statuses", func() {
By("patching ClusterTemplate and corresponding HelmChart statuses", func() {
Expect(Get(&clusterTemplate)()).To(Succeed())
clusterTemplate.Status.ChartRef = &hcv2.CrossNamespaceSourceReference{
Kind: "HelmChart",
Expand Down Expand Up @@ -361,5 +364,102 @@ var _ = Describe("ClusterDeployment Controller", func() {
}).Should(Succeed())
})
})

// todo: Cluster and MachineDeployment resources creation fails with "no matches for kind",
// need to install CRDs and add to scheme. Until then the test is disabled.
PIt("should successfully finish reconciliation", func() {
controllerReconciler := &ClusterDeploymentReconciler{
Client: mgrClient,
HelmActor: &fakeHelmActor{},
Config: &rest.Config{},
}

By("Ensuring related resources are in proper state", func() {
Expect(Get(&clusterTemplate)()).To(Succeed())
clusterTemplate.Status.ChartRef = &hcv2.CrossNamespaceSourceReference{
Kind: "HelmChart",
Name: clusterTemplateHelmChart.Name,
Namespace: namespace.Name,
}
Expect(k8sClient.Status().Update(ctx, &clusterTemplate)).To(Succeed())

Expect(Get(&clusterTemplateHelmChart)()).To(Succeed())
clusterTemplateHelmChart.Status.URL = helmChartURL
clusterTemplateHelmChart.Status.Artifact = &sourcev1.Artifact{
URL: helmChartURL,
LastUpdateTime: metav1.Now(),
}
Expect(k8sClient.Status().Update(ctx, &clusterTemplateHelmChart)).To(Succeed())

helmRelease := &hcv2.HelmRelease{
ObjectMeta: metav1.ObjectMeta{
Name: clusterDeployment.Name,
Namespace: namespace.Name,
},
}
Expect(Get(helmRelease)()).To(Succeed())
meta.SetStatusCondition(&helmRelease.Status.Conditions, metav1.Condition{
Type: meta2.ReadyCondition,
Status: metav1.ConditionTrue,
LastTransitionTime: metav1.Now(),
Reason: hcv2.InstallSucceededReason,
})
Expect(k8sClient.Status().Update(ctx, helmRelease)).To(Succeed())

cluster := v1beta1.Cluster{
ObjectMeta: metav1.ObjectMeta{
Name: clusterDeployment.Name,
Namespace: namespace.Name,
Labels: map[string]string{hmc.FluxHelmChartNameKey: clusterDeployment.Name},
},
}
Expect(k8sClient.Create(ctx, &cluster)).To(Succeed())

machineDeployment := v1beta1.MachineDeployment{
ObjectMeta: metav1.ObjectMeta{
Name: clusterDeployment.Name + "-md",
Namespace: namespace.Name,
Labels: map[string]string{hmc.FluxHelmChartNameKey: clusterDeployment.Name},
},
}
Expect(k8sClient.Create(ctx, &machineDeployment)).To(Succeed())
})

By("ensuring reconciliation finished", func() {
Eventually(func(g Gomega) {
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: clusterDeploymentKey,
})
g.Expect(err).NotTo(HaveOccurred())
g.Expect(Object(&clusterDeployment)()).Should(SatisfyAll(
HaveField("Finalizers", ContainElement(hmc.ClusterDeploymentFinalizer)),
HaveField("Status.Conditions", ContainElements(
SatisfyAll(
HaveField("Type", hmc.TemplateReadyCondition),
HaveField("Status", metav1.ConditionTrue),
HaveField("Reason", hmc.SucceededReason),
HaveField("Message", "Template is valid"),
),
SatisfyAll(
HaveField("Type", hmc.HelmChartReadyCondition),
HaveField("Status", metav1.ConditionTrue),
HaveField("Reason", hmc.SucceededReason),
HaveField("Message", "Helm chart is valid"),
),
SatisfyAll(
HaveField("Type", hmc.CredentialReadyCondition),
HaveField("Status", metav1.ConditionTrue),
HaveField("Reason", hmc.SucceededReason),
HaveField("Message", "Credential is Ready"),
),
SatisfyAll(
HaveField("Type", hmc.HelmReleaseReadyCondition),
HaveField("Status", metav1.ConditionTrue),
HaveField("Reason", hmc.SucceededReason),
),
))))
}).Should(Succeed())
})
})
})
})
21 changes: 18 additions & 3 deletions internal/helm/actor.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
// Copyright 2024
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package helm

import (
"context"

"github.com/Mirantis/hmc/api/v1alpha1"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart"
apimeta "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/client-go/rest"

"github.com/Mirantis/hmc/api/v1alpha1"
)

type Actor struct {
Expand All @@ -23,7 +38,7 @@ func NewActor(config *rest.Config, mapper apimeta.RESTMapper) *Actor {
}
}

func (a *Actor) DownloadChartFromArtifact(ctx context.Context, artifact *sourcev1.Artifact) (*chart.Chart, error) {
func (*Actor) DownloadChartFromArtifact(ctx context.Context, artifact *sourcev1.Artifact) (*chart.Chart, error) {
return DownloadChart(ctx, artifact.URL, artifact.Digest)
}

Expand All @@ -40,7 +55,7 @@ func (a *Actor) InitializeConfiguration(
return actionConfig, nil
}

func (a *Actor) EnsureReleaseWithValues(
func (*Actor) EnsureReleaseWithValues(
ctx context.Context,
actionConfig *action.Configuration,
hcChart *chart.Chart,
Expand Down

0 comments on commit 8010dd2

Please sign in to comment.