From 01099f858ebd52250dc96a442fc6edd5deeb7199 Mon Sep 17 00:00:00 2001 From: Jont828 Date: Fri, 21 Jul 2023 17:30:33 -0400 Subject: [PATCH] Add controller tests and mocks for HelmReleaseProxy --- Makefile | 16 +- .../helmreleaseproxy_controller.go | 22 +- .../helmreleaseproxy_controller_test.go | 337 ++++++++++++++++++ controllers/helmreleaseproxy/suite_test.go | 15 + go.mod | 1 + go.sum | 1 + .../{helm_operations.go => helm_client.go} | 28 +- .../{cluster_operations.go => kubeconfig.go} | 173 +++------ internal/mocks/doc.go | 23 ++ internal/mocks/helm_client_mock.go | 97 +++++ internal/mocks/kubeconfig_mock.go | 66 ++++ 11 files changed, 626 insertions(+), 153 deletions(-) create mode 100644 controllers/helmreleaseproxy/helmreleaseproxy_controller_test.go rename internal/{helm_operations.go => helm_client.go} (89%) rename internal/{cluster_operations.go => kubeconfig.go} (54%) create mode 100644 internal/mocks/doc.go create mode 100644 internal/mocks/helm_client_mock.go create mode 100644 internal/mocks/kubeconfig_mock.go diff --git a/Makefile b/Makefile index 0de81e2e..6c8dc88b 100644 --- a/Makefile +++ b/Makefile @@ -103,6 +103,10 @@ KUSTOMIZE_BIN := kustomize KUSTOMIZE := $(abspath $(TOOLS_BIN_DIR)/$(KUSTOMIZE_BIN)-$(KUSTOMIZE_VER)) KUSTOMIZE_PKG := sigs.k8s.io/kustomize/kustomize/v4 +MOCKGEN_VER := v1.6.0 +MOCKGEN_BIN := mockgen +MOCKGEN := $(TOOLS_BIN_DIR)/$(MOCKGEN_BIN)-$(MOCKGEN_VER) + SETUP_ENVTEST_VER := v0.0.0-20211110210527-619e6b92dab9 SETUP_ENVTEST_BIN := setup-envtest SETUP_ENVTEST := $(abspath $(TOOLS_BIN_DIR)/$(SETUP_ENVTEST_BIN)-$(SETUP_ENVTEST_VER)) @@ -234,14 +238,14 @@ generate-manifests: $(CONTROLLER_GEN) $(KUSTOMIZE) ## Generate manifests e.g. CR $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases .PHONY: generate-go -generate-go: $(CONTROLLER_GEN) $(KUSTOMIZE) ## Generate manifests e.g. CRD, RBAC etc. for core - $(CONTROLLER_GEN) object:headerFile="hack/boilerplate/boilerplate.generatego.txt" paths="./..." +generate-go: $(CONTROLLER_GEN) $(MOCKGEN) $(KUSTOMIZE) ## Generate manifests e.g. CRD, RBAC etc. for core + $(CONTROLLER_GEN) object:headerFile="hack/boilerplate/boilerplate.generatego.txt" paths="./api/..." + go generate ./... .PHONY: generate-modules generate-modules: ## Run go mod tidy to ensure modules are up to date go mod tidy cd $(TOOLS_DIR); go mod tidy - cd $(TEST_DIR); go mod tidy ## -------------------------------------- ## Lint / Verify @@ -632,6 +636,9 @@ $(ENVSUBST_BIN): $(ENVSUBST) ## Build a local copy of envsubst. .PHONY: $(KUSTOMIZE_BIN) $(KUSTOMIZE_BIN): $(KUSTOMIZE) ## Build a local copy of kustomize. +.PHONY: $(MOCKGEN_BIN) +$(MOCKGEN_BIN): $(MOCKGEN) ## Build a local copy of mockgen. + .PHONY: $(SETUP_ENVTEST_BIN) $(SETUP_ENVTEST_BIN): $(SETUP_ENVTEST) ## Build a local copy of setup-envtest. @@ -686,6 +693,9 @@ $(ENVSUBST): # Build gotestsum from tools folder. $(KUSTOMIZE): # Build kustomize from tools folder. CGO_ENABLED=0 GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(KUSTOMIZE_PKG) $(KUSTOMIZE_BIN) $(KUSTOMIZE_VER) +$(MOCKGEN): ## Build mockgen from tools folder. + GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) github.com/golang/mock/mockgen $(MOCKGEN_BIN) $(MOCKGEN_VER) + $(SETUP_ENVTEST): # Build setup-envtest from tools folder. GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(SETUP_ENVTEST_PKG) $(SETUP_ENVTEST_BIN) $(SETUP_ENVTEST_VER) diff --git a/controllers/helmreleaseproxy/helmreleaseproxy_controller.go b/controllers/helmreleaseproxy/helmreleaseproxy_controller.go index 8a4e5469..310aff7c 100644 --- a/controllers/helmreleaseproxy/helmreleaseproxy_controller.go +++ b/controllers/helmreleaseproxy/helmreleaseproxy_controller.go @@ -111,6 +111,9 @@ func (r *HelmReleaseProxyReconciler) Reconcile(ctx context.Context, req ctrl.Req Name: helmReleaseProxy.Spec.ClusterRef.Name, } + k := internal.KubeconfigGetter{} + client := &internal.HelmClient{} + // examine DeletionTimestamp to determine if object is under deletion if helmReleaseProxy.ObjectMeta.DeletionTimestamp.IsZero() { // The object is not being deleted, so if it does not have our finalizer, @@ -129,7 +132,7 @@ func (r *HelmReleaseProxyReconciler) Reconcile(ctx context.Context, req ctrl.Req // our finalizer is present, so lets handle any external dependency if err := r.Client.Get(ctx, clusterKey, cluster); err == nil { log.V(2).Info("Getting kubeconfig for cluster", "cluster", cluster.Name) - kubeconfig, err := internal.GetClusterKubeconfig(ctx, cluster) + kubeconfig, err := k.GetClusterKubeconfig(ctx, cluster) if err != nil { wrappedErr := errors.Wrapf(err, "failed to get kubeconfig for cluster") conditions.MarkFalse(helmReleaseProxy, addonsv1alpha1.ClusterAvailableCondition, addonsv1alpha1.GetKubeconfigFailedReason, clusterv1.ConditionSeverityError, wrappedErr.Error()) @@ -138,7 +141,7 @@ func (r *HelmReleaseProxyReconciler) Reconcile(ctx context.Context, req ctrl.Req } conditions.MarkTrue(helmReleaseProxy, addonsv1alpha1.ClusterAvailableCondition) - if err := r.reconcileDelete(ctx, helmReleaseProxy, kubeconfig); err != nil { + if err := r.reconcileDelete(ctx, helmReleaseProxy, client, kubeconfig); err != nil { // if fail to delete the external dependency here, return with error // so that it can be retried return ctrl.Result{}, err @@ -175,7 +178,7 @@ func (r *HelmReleaseProxyReconciler) Reconcile(ctx context.Context, req ctrl.Req } log.V(2).Info("Getting kubeconfig for cluster", "cluster", cluster.Name) - kubeconfig, err := internal.GetClusterKubeconfig(ctx, cluster) + kubeconfig, err := k.GetClusterKubeconfig(ctx, cluster) if err != nil { wrappedErr := errors.Wrapf(err, "failed to get kubeconfig for cluster") conditions.MarkFalse(helmReleaseProxy, addonsv1alpha1.ClusterAvailableCondition, addonsv1alpha1.GetKubeconfigFailedReason, clusterv1.ConditionSeverityError, wrappedErr.Error()) @@ -185,14 +188,14 @@ func (r *HelmReleaseProxyReconciler) Reconcile(ctx context.Context, req ctrl.Req conditions.MarkTrue(helmReleaseProxy, addonsv1alpha1.ClusterAvailableCondition) log.V(2).Info("Reconciling HelmReleaseProxy", "releaseProxyName", helmReleaseProxy.Name) - err = r.reconcileNormal(ctx, helmReleaseProxy, kubeconfig) + err = r.reconcileNormal(ctx, helmReleaseProxy, client, kubeconfig) return ctrl.Result{}, err } // reconcileNormal handles HelmReleaseProxy reconciliation when it is not being deleted. This will install or upgrade the HelmReleaseProxy on the Cluster. // It will set the ReleaseName on the HelmReleaseProxy if the name is generated and also set the release status and release revision. -func (r *HelmReleaseProxyReconciler) reconcileNormal(ctx context.Context, helmReleaseProxy *addonsv1alpha1.HelmReleaseProxy, kubeconfig string) error { +func (r *HelmReleaseProxyReconciler) reconcileNormal(ctx context.Context, helmReleaseProxy *addonsv1alpha1.HelmReleaseProxy, client internal.Client, kubeconfig string) error { log := ctrl.LoggerFrom(ctx) log.V(2).Info("Reconciling HelmReleaseProxy on cluster", "HelmReleaseProxy", helmReleaseProxy.Name, "cluster", helmReleaseProxy.Spec.ClusterRef.Name) @@ -204,8 +207,7 @@ func (r *HelmReleaseProxyReconciler) reconcileNormal(ctx context.Context, helmRe }) } - log.V(2).Info(fmt.Sprintf("Preparing to install or upgrade release '%s' on cluster %s", helmReleaseProxy.Spec.ReleaseName, helmReleaseProxy.Spec.ClusterRef.Name)) - release, err := internal.InstallOrUpgradeHelmRelease(ctx, kubeconfig, helmReleaseProxy.Spec) + release, err := client.InstallOrUpgradeHelmRelease(ctx, kubeconfig, helmReleaseProxy.Spec) if err != nil { log.Error(err, fmt.Sprintf("Failed to install or upgrade release '%s' on cluster %s", helmReleaseProxy.Spec.ReleaseName, helmReleaseProxy.Spec.ClusterRef.Name)) conditions.MarkFalse(helmReleaseProxy, addonsv1alpha1.HelmReleaseReadyCondition, addonsv1alpha1.HelmInstallOrUpgradeFailedReason, clusterv1.ConditionSeverityError, err.Error()) @@ -233,12 +235,12 @@ func (r *HelmReleaseProxyReconciler) reconcileNormal(ctx context.Context, helmRe } // reconcileDelete handles HelmReleaseProxy deletion. This will uninstall the HelmReleaseProxy on the Cluster or return nil if the HelmReleaseProxy is not found. -func (r *HelmReleaseProxyReconciler) reconcileDelete(ctx context.Context, helmReleaseProxy *addonsv1alpha1.HelmReleaseProxy, kubeconfig string) error { +func (r *HelmReleaseProxyReconciler) reconcileDelete(ctx context.Context, helmReleaseProxy *addonsv1alpha1.HelmReleaseProxy, client internal.Client, kubeconfig string) error { log := ctrl.LoggerFrom(ctx) log.V(2).Info("Deleting HelmReleaseProxy on cluster", "HelmReleaseProxy", helmReleaseProxy.Name, "cluster", helmReleaseProxy.Spec.ClusterRef.Name) - _, err := internal.GetHelmRelease(ctx, kubeconfig, helmReleaseProxy.Spec) + _, err := client.GetHelmRelease(ctx, kubeconfig, helmReleaseProxy.Spec) if err != nil { log.V(2).Error(err, "error getting release from cluster", "cluster", helmReleaseProxy.Spec.ClusterRef.Name) @@ -256,7 +258,7 @@ func (r *HelmReleaseProxyReconciler) reconcileDelete(ctx context.Context, helmRe log.V(2).Info("Preparing to uninstall release on cluster", "releaseName", helmReleaseProxy.Spec.ReleaseName, "clusterName", helmReleaseProxy.Spec.ClusterRef.Name) - response, err := internal.UninstallHelmRelease(ctx, kubeconfig, helmReleaseProxy.Spec) + response, err := client.UninstallHelmRelease(ctx, kubeconfig, helmReleaseProxy.Spec) if err != nil { log.V(2).Info("Error uninstalling chart with Helm:", err) conditions.MarkFalse(helmReleaseProxy, addonsv1alpha1.HelmReleaseReadyCondition, addonsv1alpha1.HelmReleaseDeletionFailedReason, clusterv1.ConditionSeverityError, err.Error()) diff --git a/controllers/helmreleaseproxy/helmreleaseproxy_controller_test.go b/controllers/helmreleaseproxy/helmreleaseproxy_controller_test.go new file mode 100644 index 00000000..66ccfcae --- /dev/null +++ b/controllers/helmreleaseproxy/helmreleaseproxy_controller_test.go @@ -0,0 +1,337 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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 helmreleaseproxy + +import ( + "fmt" + "testing" + + helmRelease "helm.sh/helm/v3/pkg/release" + helmDriver "helm.sh/helm/v3/pkg/storage/driver" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + addonsv1alpha1 "sigs.k8s.io/cluster-api-addon-provider-helm/api/v1alpha1" + "sigs.k8s.io/cluster-api-addon-provider-helm/internal/mocks" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "sigs.k8s.io/cluster-api/util/conditions" + + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/golang/mock/gomock" + . "github.com/onsi/gomega" +) + +var ( + kubeconfig = "test-kubeconfig" + + defaultProxy = &addonsv1alpha1.HelmReleaseProxy{ + TypeMeta: metav1.TypeMeta{ + Kind: "HelmReleaseProxy", + APIVersion: "addons.cluster.x-k8s.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-proxy", + Namespace: "default", + }, + Spec: addonsv1alpha1.HelmReleaseProxySpec{ + ClusterRef: corev1.ObjectReference{ + APIVersion: "cluster.x-k8s.io/v1beta1", + Kind: "Cluster", + Namespace: "default", + Name: "test-cluster", + }, + RepoURL: "https://test-repo", + ChartName: "test-chart", + Version: "test-version", + ReleaseName: "test-release", + ReleaseNamespace: "default", + Values: "test-values", + }, + } + + generateNameProxy = &addonsv1alpha1.HelmReleaseProxy{ + TypeMeta: metav1.TypeMeta{ + Kind: "HelmReleaseProxy", + APIVersion: "addons.cluster.x-k8s.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-proxy", + Namespace: "default", + }, + Spec: addonsv1alpha1.HelmReleaseProxySpec{ + ClusterRef: corev1.ObjectReference{ + APIVersion: "cluster.x-k8s.io/v1beta1", + Kind: "Cluster", + Namespace: "default", + Name: "test-cluster", + }, + RepoURL: "https://test-repo", + ChartName: "test-chart", + Version: "test-version", + ReleaseNamespace: "default", + Values: "test-values", + }, + } + + errInternal = fmt.Errorf("internal error") +) + +func TestReconcileNormal(t *testing.T) { + testcases := []struct { + name string + helmReleaseProxy *addonsv1alpha1.HelmReleaseProxy + clientExpect func(g *WithT, c *mocks.MockClientMockRecorder) + expect func(g *WithT, hrp *addonsv1alpha1.HelmReleaseProxy) + expectedError string + }{ + { + name: "succesfully install a Helm release", + helmReleaseProxy: defaultProxy.DeepCopy(), + clientExpect: func(g *WithT, c *mocks.MockClientMockRecorder) { + c.InstallOrUpgradeHelmRelease(ctx, kubeconfig, defaultProxy.DeepCopy().Spec).Return(&helmRelease.Release{ + Name: "test-release", + Version: 1, + Info: &helmRelease.Info{ + Status: helmRelease.StatusDeployed, + }, + }, nil).Times(1) + }, + expect: func(g *WithT, hrp *addonsv1alpha1.HelmReleaseProxy) { + _, ok := hrp.Annotations[addonsv1alpha1.IsReleaseNameGeneratedAnnotation] + g.Expect(ok).To(BeFalse()) + g.Expect(hrp.Spec.ReleaseName).To(Equal("test-release")) + g.Expect(hrp.Status.Revision).To(Equal(1)) + g.Expect(hrp.Status.Status).To(BeEquivalentTo(helmRelease.StatusDeployed)) + + g.Expect(conditions.Has(hrp, addonsv1alpha1.HelmReleaseReadyCondition)).To(BeTrue()) + g.Expect(conditions.IsTrue(hrp, addonsv1alpha1.HelmReleaseReadyCondition)).To(BeTrue()) + + }, + expectedError: "", + }, + { + name: "succesfully install a Helm release with a generated name", + helmReleaseProxy: generateNameProxy, + clientExpect: func(g *WithT, c *mocks.MockClientMockRecorder) { + c.InstallOrUpgradeHelmRelease(ctx, kubeconfig, generateNameProxy.Spec).Return(&helmRelease.Release{ + Name: "test-release", + Version: 1, + Info: &helmRelease.Info{ + Status: helmRelease.StatusDeployed, + }, + }, nil).Times(1) + }, + expect: func(g *WithT, hrp *addonsv1alpha1.HelmReleaseProxy) { + _, ok := hrp.Annotations[addonsv1alpha1.IsReleaseNameGeneratedAnnotation] + g.Expect(ok).To(BeTrue()) + g.Expect(hrp.Spec.ReleaseName).To(Equal("test-release")) + g.Expect(hrp.Status.Revision).To(Equal(1)) + g.Expect(hrp.Status.Status).To(BeEquivalentTo(helmRelease.StatusDeployed)) + + g.Expect(conditions.Has(hrp, addonsv1alpha1.HelmReleaseReadyCondition)).To(BeTrue()) + g.Expect(conditions.IsTrue(hrp, addonsv1alpha1.HelmReleaseReadyCondition)).To(BeTrue()) + }, + expectedError: "", + }, + { + name: "Helm release pending", + helmReleaseProxy: defaultProxy.DeepCopy(), + clientExpect: func(g *WithT, c *mocks.MockClientMockRecorder) { + c.InstallOrUpgradeHelmRelease(ctx, kubeconfig, defaultProxy.Spec).Return(&helmRelease.Release{ + Name: "test-release", + Version: 1, + Info: &helmRelease.Info{ + Status: helmRelease.StatusPendingInstall, + }, + }, nil).Times(1) + }, + expect: func(g *WithT, hrp *addonsv1alpha1.HelmReleaseProxy) { + t.Logf("HelmReleaseProxy: %+v", hrp) + _, ok := hrp.Annotations[addonsv1alpha1.IsReleaseNameGeneratedAnnotation] + g.Expect(ok).To(BeFalse()) + g.Expect(hrp.Spec.ReleaseName).To(Equal("test-release")) + g.Expect(hrp.Status.Revision).To(Equal(1)) + g.Expect(hrp.Status.Status).To(BeEquivalentTo(helmRelease.StatusPendingInstall)) + + releaseReady := conditions.Get(hrp, addonsv1alpha1.HelmReleaseReadyCondition) + g.Expect(releaseReady.Status).To(Equal(corev1.ConditionFalse)) + g.Expect(releaseReady.Reason).To(Equal(addonsv1alpha1.HelmReleasePendingReason)) + g.Expect(releaseReady.Severity).To(Equal(clusterv1.ConditionSeverityInfo)) + }, + expectedError: "", + }, + { + name: "Helm client returns error", + helmReleaseProxy: defaultProxy.DeepCopy(), + clientExpect: func(g *WithT, c *mocks.MockClientMockRecorder) { + c.InstallOrUpgradeHelmRelease(ctx, kubeconfig, defaultProxy.Spec).Return(nil, errInternal).Times(1) + }, + expect: func(g *WithT, hrp *addonsv1alpha1.HelmReleaseProxy) { + _, ok := hrp.Annotations[addonsv1alpha1.IsReleaseNameGeneratedAnnotation] + g.Expect(ok).To(BeFalse()) + + releaseReady := conditions.Get(hrp, addonsv1alpha1.HelmReleaseReadyCondition) + g.Expect(releaseReady.Status).To(Equal(corev1.ConditionFalse)) + g.Expect(releaseReady.Reason).To(Equal(addonsv1alpha1.HelmInstallOrUpgradeFailedReason)) + g.Expect(releaseReady.Severity).To(Equal(clusterv1.ConditionSeverityError)) + g.Expect(releaseReady.Message).To(Equal(errInternal.Error())) + + }, + expectedError: errInternal.Error(), + }, + { + name: "Helm release in a failed state, no client error", + helmReleaseProxy: defaultProxy.DeepCopy(), + clientExpect: func(g *WithT, c *mocks.MockClientMockRecorder) { + c.InstallOrUpgradeHelmRelease(ctx, kubeconfig, defaultProxy.Spec).Return(&helmRelease.Release{ + Name: "test-release", + Version: 1, + Info: &helmRelease.Info{ + Status: helmRelease.StatusFailed, + }, + }, nil).Times(1) + }, + expect: func(g *WithT, hrp *addonsv1alpha1.HelmReleaseProxy) { + _, ok := hrp.Annotations[addonsv1alpha1.IsReleaseNameGeneratedAnnotation] + g.Expect(ok).To(BeFalse()) + + releaseReady := conditions.Get(hrp, addonsv1alpha1.HelmReleaseReadyCondition) + g.Expect(releaseReady.Status).To(Equal(corev1.ConditionFalse)) + g.Expect(releaseReady.Reason).To(Equal(addonsv1alpha1.HelmInstallOrUpgradeFailedReason)) + g.Expect(releaseReady.Severity).To(Equal(clusterv1.ConditionSeverityError)) + g.Expect(releaseReady.Message).To(Equal(fmt.Sprintf("Helm release failed: %s", helmRelease.StatusFailed))) + + }, + expectedError: "", + }, + } + + for _, tc := range testcases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + t.Parallel() + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + clientMock := mocks.NewMockClient(mockCtrl) + tc.clientExpect(g, clientMock.EXPECT()) + + r := &HelmReleaseProxyReconciler{ + Client: fake.NewClientBuilder().WithScheme(fakeScheme).Build(), + } + + err := r.reconcileNormal(ctx, tc.helmReleaseProxy, clientMock, kubeconfig) + if tc.expectedError != "" { + g.Expect(err).To(HaveOccurred()) + g.Expect(err).To(MatchError(tc.expectedError), err.Error()) + } else { + g.Expect(err).NotTo(HaveOccurred()) + tc.expect(g, tc.helmReleaseProxy) + } + }) + } +} + +func TestReconcileDelete(t *testing.T) { + testcases := []struct { + name string + helmReleaseProxy *addonsv1alpha1.HelmReleaseProxy + clientExpect func(g *WithT, c *mocks.MockClientMockRecorder) + expect func(g *WithT, hrp *addonsv1alpha1.HelmReleaseProxy) + expectedError string + }{ + { + name: "succesfully uninstall a Helm release", + helmReleaseProxy: defaultProxy.DeepCopy(), + clientExpect: func(g *WithT, c *mocks.MockClientMockRecorder) { + c.GetHelmRelease(ctx, kubeconfig, defaultProxy.DeepCopy().Spec).Return(&helmRelease.Release{ + Name: "test-release", + Version: 1, + Info: &helmRelease.Info{ + Status: helmRelease.StatusDeployed, + }, + }, nil).Times(1) + c.UninstallHelmRelease(ctx, kubeconfig, defaultProxy.DeepCopy().Spec).Return(&helmRelease.UninstallReleaseResponse{}, nil).Times(1) + }, + expect: func(g *WithT, hrp *addonsv1alpha1.HelmReleaseProxy) { + g.Expect(conditions.Has(hrp, addonsv1alpha1.HelmReleaseReadyCondition)).To(BeTrue()) + releaseReady := conditions.Get(hrp, addonsv1alpha1.HelmReleaseReadyCondition) + g.Expect(releaseReady.Status).To(Equal(corev1.ConditionFalse)) + g.Expect(releaseReady.Reason).To(Equal(addonsv1alpha1.HelmReleaseDeletedReason)) + g.Expect(releaseReady.Severity).To(Equal(clusterv1.ConditionSeverityInfo)) + }, + expectedError: "", + }, + { + name: "Helm release already uninstalled", + helmReleaseProxy: defaultProxy.DeepCopy(), + clientExpect: func(g *WithT, c *mocks.MockClientMockRecorder) { + c.GetHelmRelease(ctx, kubeconfig, defaultProxy.DeepCopy().Spec).Return(nil, helmDriver.ErrReleaseNotFound).Times(1) + }, + expect: func(g *WithT, hrp *addonsv1alpha1.HelmReleaseProxy) { + g.Expect(conditions.Has(hrp, addonsv1alpha1.HelmReleaseReadyCondition)).To(BeTrue()) + releaseReady := conditions.Get(hrp, addonsv1alpha1.HelmReleaseReadyCondition) + g.Expect(releaseReady.Status).To(Equal(corev1.ConditionFalse)) + g.Expect(releaseReady.Reason).To(Equal(addonsv1alpha1.HelmReleaseDeletedReason)) + g.Expect(releaseReady.Severity).To(Equal(clusterv1.ConditionSeverityInfo)) + }, + expectedError: "", + }, + { + name: "error attempting to get Helm release", + helmReleaseProxy: defaultProxy.DeepCopy(), + clientExpect: func(g *WithT, c *mocks.MockClientMockRecorder) { + c.GetHelmRelease(ctx, kubeconfig, defaultProxy.DeepCopy().Spec).Return(nil, errInternal).Times(1) + }, + expect: func(g *WithT, hrp *addonsv1alpha1.HelmReleaseProxy) { + g.Expect(conditions.Has(hrp, addonsv1alpha1.HelmReleaseReadyCondition)).To(BeTrue()) + releaseReady := conditions.Get(hrp, addonsv1alpha1.HelmReleaseReadyCondition) + g.Expect(releaseReady.Status).To(Equal(corev1.ConditionFalse)) + g.Expect(releaseReady.Reason).To(Equal(addonsv1alpha1.HelmReleaseReadyCondition)) + g.Expect(releaseReady.Severity).To(Equal(clusterv1.ConditionSeverityError)) + }, + expectedError: errInternal.Error(), + }, + } + + for _, tc := range testcases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + t.Parallel() + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + clientMock := mocks.NewMockClient(mockCtrl) + tc.clientExpect(g, clientMock.EXPECT()) + + r := &HelmReleaseProxyReconciler{ + Client: fake.NewClientBuilder().WithScheme(fakeScheme).Build(), + } + + err := r.reconcileDelete(ctx, tc.helmReleaseProxy, clientMock, kubeconfig) + if tc.expectedError != "" { + g.Expect(err).To(HaveOccurred()) + g.Expect(err).To(MatchError(tc.expectedError), err.Error()) + } else { + g.Expect(err).NotTo(HaveOccurred()) + tc.expect(g, tc.helmReleaseProxy) + } + }) + } +} diff --git a/controllers/helmreleaseproxy/suite_test.go b/controllers/helmreleaseproxy/suite_test.go index 2b1111f0..dff6c825 100644 --- a/controllers/helmreleaseproxy/suite_test.go +++ b/controllers/helmreleaseproxy/suite_test.go @@ -22,8 +22,11 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/scheme" addonsv1alpha1 "sigs.k8s.io/cluster-api-addon-provider-helm/api/v1alpha1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -38,6 +41,18 @@ import ( var k8sClient client.Client var testEnv *envtest.Environment +var ( + _ *envtest.Environment + ctx = ctrl.SetupSignalHandler() + fakeScheme = runtime.NewScheme() +) + +func init() { + _ = scheme.AddToScheme(fakeScheme) + _ = clusterv1.AddToScheme(fakeScheme) + _ = addonsv1alpha1.AddToScheme(fakeScheme) +} + func TestAPIs(t *testing.T) { RegisterFailHandler(Fail) diff --git a/go.mod b/go.mod index 72c7adb3..cdf68118 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.19 require ( github.com/Masterminds/sprig v2.22.0+incompatible + github.com/golang/mock v1.5.0 github.com/google/go-cmp v0.5.9 github.com/onsi/ginkgo/v2 v2.9.2 github.com/onsi/gomega v1.27.5 diff --git a/go.sum b/go.sum index 60d057c5..fd24af41 100644 --- a/go.sum +++ b/go.sum @@ -256,6 +256,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= diff --git a/internal/helm_operations.go b/internal/helm_client.go similarity index 89% rename from internal/helm_operations.go rename to internal/helm_client.go index 91c00f50..f43e1847 100644 --- a/internal/helm_operations.go +++ b/internal/helm_client.go @@ -45,6 +45,14 @@ import ( addonsv1alpha1 "sigs.k8s.io/cluster-api-addon-provider-helm/api/v1alpha1" ) +type Client interface { + InstallOrUpgradeHelmRelease(ctx context.Context, kubeconfig string, spec addonsv1alpha1.HelmReleaseProxySpec) (*helmRelease.Release, error) + GetHelmRelease(ctx context.Context, kubeconfig string, spec addonsv1alpha1.HelmReleaseProxySpec) (*helmRelease.Release, error) + UninstallHelmRelease(ctx context.Context, kubeconfig string, spec addonsv1alpha1.HelmReleaseProxySpec) (*helmRelease.UninstallReleaseResponse, error) +} + +type HelmClient struct{} + // GetActionConfig returns a new Helm action configuration. func GetActionConfig(ctx context.Context, namespace string, config *rest.Config) (*helmAction.Configuration, error) { log := ctrl.LoggerFrom(ctx) @@ -107,7 +115,7 @@ func HelmInit(ctx context.Context, namespace string, kubeconfig string) (*helmCl // InstallOrUpgradeHelmRelease installs a Helm release if it does not exist, or upgrades it if it does and differs from the spec. // It returns a boolean indicating whether an install or upgrade was performed. -func InstallOrUpgradeHelmRelease(ctx context.Context, kubeconfig string, spec addonsv1alpha1.HelmReleaseProxySpec) (*helmRelease.Release, error) { +func (c *HelmClient) InstallOrUpgradeHelmRelease(ctx context.Context, kubeconfig string, spec addonsv1alpha1.HelmReleaseProxySpec) (*helmRelease.Release, error) { log := ctrl.LoggerFrom(ctx) log.V(2).Info("Installing or upgrading Helm release") @@ -115,16 +123,16 @@ func InstallOrUpgradeHelmRelease(ctx context.Context, kubeconfig string, spec ad // historyClient := helmAction.NewHistory(actionConfig) // historyClient.Max = 1 // if _, err := historyClient.Run(spec.ReleaseName); err == helmDriver.ErrReleaseNotFound { - existingRelease, err := GetHelmRelease(ctx, kubeconfig, spec) + existingRelease, err := c.GetHelmRelease(ctx, kubeconfig, spec) if err != nil { if err == helmDriver.ErrReleaseNotFound { - return InstallHelmRelease(ctx, kubeconfig, spec) + return c.InstallHelmRelease(ctx, kubeconfig, spec) } return nil, err } - return UpgradeHelmReleaseIfChanged(ctx, kubeconfig, spec, existingRelease) + return c.UpgradeHelmReleaseIfChanged(ctx, kubeconfig, spec, existingRelease) } // generateHelmInstallConfig generates default helm install config using helmOptions specified in HCP CR spec. @@ -187,7 +195,7 @@ func generateHelmUpgradeConfig(actionConfig *helmAction.Configuration, helmOptio } // InstallHelmRelease installs a Helm release. -func InstallHelmRelease(ctx context.Context, kubeconfig string, spec addonsv1alpha1.HelmReleaseProxySpec) (*helmRelease.Release, error) { +func (c *HelmClient) InstallHelmRelease(ctx context.Context, kubeconfig string, spec addonsv1alpha1.HelmReleaseProxySpec) (*helmRelease.Release, error) { log := ctrl.LoggerFrom(ctx) settings, actionConfig, err := HelmInit(ctx, spec.ReleaseNamespace, kubeconfig) @@ -293,7 +301,7 @@ func getHelmChartAndRepoName(chartName, repoURL string) (string, string, error) } // UpgradeHelmReleaseIfChanged upgrades a Helm release. The boolean refers to if an upgrade was attempted. -func UpgradeHelmReleaseIfChanged(ctx context.Context, kubeconfig string, spec addonsv1alpha1.HelmReleaseProxySpec, existing *helmRelease.Release) (*helmRelease.Release, error) { +func (c *HelmClient) UpgradeHelmReleaseIfChanged(ctx context.Context, kubeconfig string, spec addonsv1alpha1.HelmReleaseProxySpec, existing *helmRelease.Release) (*helmRelease.Release, error) { log := ctrl.LoggerFrom(ctx) settings, actionConfig, err := HelmInit(ctx, spec.ReleaseNamespace, kubeconfig) @@ -421,7 +429,7 @@ func shouldUpgradeHelmRelease(ctx context.Context, existing helmRelease.Release, } // GetHelmRelease returns a Helm release if it exists. -func GetHelmRelease(ctx context.Context, kubeconfig string, spec addonsv1alpha1.HelmReleaseProxySpec) (*helmRelease.Release, error) { +func (c *HelmClient) GetHelmRelease(ctx context.Context, kubeconfig string, spec addonsv1alpha1.HelmReleaseProxySpec) (*helmRelease.Release, error) { if spec.ReleaseName == "" { return nil, helmDriver.ErrReleaseNotFound } @@ -440,7 +448,7 @@ func GetHelmRelease(ctx context.Context, kubeconfig string, spec addonsv1alpha1. } // ListHelmReleases lists all Helm releases in a namespace. -func ListHelmReleases(ctx context.Context, kubeconfig string, spec addonsv1alpha1.HelmReleaseProxySpec) ([]*helmRelease.Release, error) { +func (c *HelmClient) ListHelmReleases(ctx context.Context, kubeconfig string, spec addonsv1alpha1.HelmReleaseProxySpec) ([]*helmRelease.Release, error) { _, actionConfig, err := HelmInit(ctx, spec.ReleaseNamespace, kubeconfig) if err != nil { return nil, err @@ -476,7 +484,7 @@ func generateHelmUninstallConfig(actionConfig *helmAction.Configuration, helmOpt } // UninstallHelmRelease uninstalls a Helm release. -func UninstallHelmRelease(ctx context.Context, kubeconfig string, spec addonsv1alpha1.HelmReleaseProxySpec) (*helmRelease.UninstallReleaseResponse, error) { +func (c *HelmClient) UninstallHelmRelease(ctx context.Context, kubeconfig string, spec addonsv1alpha1.HelmReleaseProxySpec) (*helmRelease.UninstallReleaseResponse, error) { _, actionConfig, err := HelmInit(ctx, spec.ReleaseNamespace, kubeconfig) if err != nil { return nil, err @@ -493,7 +501,7 @@ func UninstallHelmRelease(ctx context.Context, kubeconfig string, spec addonsv1a } // RollbackHelmRelease rolls back a Helm release. -func RollbackHelmRelease(ctx context.Context, kubeconfig string, spec addonsv1alpha1.HelmReleaseProxySpec) error { +func (c *HelmClient) RollbackHelmRelease(ctx context.Context, kubeconfig string, spec addonsv1alpha1.HelmReleaseProxySpec) error { _, actionConfig, err := HelmInit(ctx, spec.ReleaseNamespace, kubeconfig) if err != nil { return err diff --git a/internal/cluster_operations.go b/internal/kubeconfig.go similarity index 54% rename from internal/cluster_operations.go rename to internal/kubeconfig.go index 3958431b..4c91e283 100644 --- a/internal/cluster_operations.go +++ b/internal/kubeconfig.go @@ -22,36 +22,67 @@ import ( "path/filepath" "github.com/pkg/errors" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" "sigs.k8s.io/cluster-api/cmd/clusterctl/client" "sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster" - "sigs.k8s.io/cluster-api/controllers/external" ctrl "sigs.k8s.io/controller-runtime" - ctrlClient "sigs.k8s.io/controller-runtime/pkg/client" configclient "sigs.k8s.io/controller-runtime/pkg/client/config" - - corev1 "k8s.io/api/core/v1" ) -// InitInClusterKubeconfig generates a kubeconfig file for the management cluster. +type Getter interface { + GetClusterKubeconfig(ctx context.Context, cluster *clusterv1.Cluster) (string, error) +} + +type KubeconfigGetter struct{} + +// GetClusterKubeconfig returns the kubeconfig for a selected Cluster as a string. +func (k *KubeconfigGetter) GetClusterKubeconfig(ctx context.Context, cluster *clusterv1.Cluster) (string, error) { + log := ctrl.LoggerFrom(ctx) + + log.V(2).Info("Initializing management cluster kubeconfig") + managementKubeconfig, err := initInClusterKubeconfig(ctx) + if err != nil { + return "", errors.Wrapf(err, "failed to initialize management cluster kubeconfig") + } + + c, err := client.New("") + if err != nil { + return "", err + } + + options := client.GetKubeconfigOptions{ + Kubeconfig: client.Kubeconfig(*managementKubeconfig), + WorkloadClusterName: cluster.Name, + Namespace: cluster.Namespace, + } + + log.V(4).Info("Getting kubeconfig for cluster", "cluster", cluster.Name) + kubeconfig, err := c.GetKubeconfig(options) + if err != nil { + return "", err + } + + return kubeconfig, nil +} + +// initInClusterKubeconfig generates a kubeconfig file for the management cluster. // Note: The k8s.io/client-go/tools/clientcmd/api package and associated tools require a path to a kubeconfig file rather than the data stored in an object. -func InitInClusterKubeconfig(ctx context.Context) (*cluster.Kubeconfig, error) { +func initInClusterKubeconfig(ctx context.Context) (*cluster.Kubeconfig, error) { log := ctrl.LoggerFrom(ctx) log.V(2).Info("Generating kubeconfig file") restConfig := configclient.GetConfigOrDie() - apiConfig, err := ConstructInClusterKubeconfig(ctx, restConfig, "") + apiConfig, err := constructInClusterKubeconfig(ctx, restConfig, "") if err != nil { log.Error(err, "error constructing in-cluster kubeconfig") return nil, err } filePath := "tmp/management.kubeconfig" - if err = WriteInClusterKubeconfigToFile(ctx, filePath, *apiConfig); err != nil { + if err = writeInClusterKubeconfigToFile(ctx, filePath, *apiConfig); err != nil { log.Error(err, "error writing kubeconfig to file") return nil, err } @@ -64,7 +95,7 @@ func InitInClusterKubeconfig(ctx context.Context) (*cluster.Kubeconfig, error) { // GetClusterKubeconfig generates a kubeconfig file for the management cluster using a rest.Config. This is a bit of a workaround // since the k8s.io/client-go/tools/clientcmd/api expects to be run from a CLI context, but within a pod we don't have that. // As a result, we have to manually fill in the fields that would normally be present in ~/.kube/config. This seems to work for now. -func ConstructInClusterKubeconfig(ctx context.Context, restConfig *rest.Config, namespace string) (*clientcmdapi.Config, error) { +func constructInClusterKubeconfig(ctx context.Context, restConfig *rest.Config, namespace string) (*clientcmdapi.Config, error) { log := ctrl.LoggerFrom(ctx) log.V(2).Info("Constructing kubeconfig file from rest.Config") @@ -105,8 +136,8 @@ func ConstructInClusterKubeconfig(ctx context.Context, restConfig *rest.Config, }, nil } -// WriteInClusterKubeconfigToFile writes the clientcmdapi.Config to a kubeconfig file. -func WriteInClusterKubeconfigToFile(ctx context.Context, filePath string, clientConfig clientcmdapi.Config) error { +// writeInClusterKubeconfigToFile writes the clientcmdapi.Config to a kubeconfig file. +func writeInClusterKubeconfigToFile(ctx context.Context, filePath string, clientConfig clientcmdapi.Config) error { log := ctrl.LoggerFrom(ctx) dir := filepath.Dir(filePath) @@ -124,121 +155,3 @@ func WriteInClusterKubeconfigToFile(ctx context.Context, filePath string, client return nil } - -// GetClusterKubeconfig returns the kubeconfig for a selected Cluster as a string. -func GetClusterKubeconfig(ctx context.Context, cluster *clusterv1.Cluster) (string, error) { - log := ctrl.LoggerFrom(ctx) - - log.V(2).Info("Initializing management cluster kubeconfig") - managementKubeconfig, err := InitInClusterKubeconfig(ctx) - if err != nil { - return "", errors.Wrapf(err, "failed to initialize management cluster kubeconfig") - } - - c, err := client.New("") - if err != nil { - return "", err - } - - options := client.GetKubeconfigOptions{ - Kubeconfig: client.Kubeconfig(*managementKubeconfig), - // Kubeconfig: client.Kubeconfig{Path: gk.kubeconfig, Context: gk.kubeconfigContext}, - WorkloadClusterName: cluster.Name, - Namespace: cluster.Namespace, - } - - log.V(4).Info("Getting kubeconfig for cluster", "cluster", cluster.Name) - kubeconfig, err := c.GetKubeconfig(options) - if err != nil { - return "", err - } - - return kubeconfig, nil -} - -// WriteClusterKubeconfigToFile writes the kubeconfig for a selected Cluster to a file. -func WriteClusterKubeconfigToFile(ctx context.Context, cluster *clusterv1.Cluster) (string, error) { - log := ctrl.LoggerFrom(ctx) - c, err := client.New("") - if err != nil { - return "", err - } - - options := client.GetKubeconfigOptions{ - Kubeconfig: client.Kubeconfig{}, - // Kubeconfig: client.Kubeconfig{Path: gk.kubeconfig, Context: gk.kubeconfigContext}, - WorkloadClusterName: cluster.Name, - Namespace: cluster.Namespace, - } - - log.V(4).Info("Getting kubeconfig for cluster", "cluster", cluster.Name) - kubeconfig, err := c.GetKubeconfig(options) - if err != nil { - return "", err - } - log.V(4).Info("cluster", "cluster", cluster.Name, "kubeconfig is:", kubeconfig) - - path := "tmp" - filePath := path + "/" + cluster.Name - if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { - err := os.Mkdir(path, os.ModePerm) - if err != nil { - return "", errors.Wrapf(err, "failed to create directory %s", path) - } - } - f, err := os.Create(filePath) - if err != nil { - return "", errors.Wrapf(err, "failed to create file %s", filePath) - } - - log.V(4).Info("Writing kubeconfig to file", "cluster", cluster.Name) - _, err = f.WriteString(kubeconfig) - if err != nil { - f.Close() - return "", errors.Wrapf(err, "failed to close kubeconfig file") - } - err = f.Close() - if err != nil { - return "", errors.Wrapf(err, "failed to close kubeconfig file") - } - - log.V(4).Info("Path is", "path", path) - return filePath, nil -} - -// GetCustomResource returns the unstructured object for a selected Custom Resource. -func GetCustomResource(ctx context.Context, c ctrlClient.Client, kind string, apiVersion string, namespace string, name string) (*unstructured.Unstructured, error) { - objectRef := corev1.ObjectReference{ - Kind: kind, - Namespace: namespace, - Name: name, - APIVersion: apiVersion, - } - object, err := external.Get(context.TODO(), c, &objectRef, namespace) - if err != nil { - return nil, nil - } - - return object, nil -} - -// GetClusterField returns the value of a field in a selected Cluster. -func GetClusterField(ctx context.Context, c ctrlClient.Client, cluster *clusterv1.Cluster, fields []string) (string, error) { - log := ctrl.LoggerFrom(ctx) - - object, err := GetCustomResource(ctx, c, cluster.Kind, cluster.APIVersion, cluster.Namespace, cluster.Name) - if err != nil { - return "", err - } - objectMap := object.UnstructuredContent() - field, found, err := unstructured.NestedString(objectMap, fields...) - if err != nil { - return "", errors.Wrapf(err, "failed to get cluster name from cluster object") - } - if !found { - return "", errors.New("failed to get cluster name from cluster object") - } - log.V(2).Info("Resolved cluster field to", "field", fields, "value", field) - - return field, nil -} diff --git a/internal/mocks/doc.go b/internal/mocks/doc.go new file mode 100644 index 00000000..8f954721 --- /dev/null +++ b/internal/mocks/doc.go @@ -0,0 +1,23 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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. +*/ + +// Run go generate to regenerate this mock. +// +//go:generate ../../hack/tools/bin/mockgen -destination helm_client_mock.go -package mocks -source ../helm_client.go Client +//go:generate ../../hack/tools/bin/mockgen -destination kubeconfig_mock.go -package mocks -source ../kubeconfig.go Getter +//go:generate /usr/bin/env bash -c "cat ../../hack/boilerplate/boilerplate.generatego.txt helm_client_mock.go > _helm_client_mock.go && mv _helm_client_mock.go helm_client_mock.go" +//go:generate /usr/bin/env bash -c "cat ../../hack/boilerplate/boilerplate.generatego.txt kubeconfig_mock.go > _kubeconfig_mock.go && mv _kubeconfig_mock.go kubeconfig_mock.go" +package mocks diff --git a/internal/mocks/helm_client_mock.go b/internal/mocks/helm_client_mock.go new file mode 100644 index 00000000..9dd0d798 --- /dev/null +++ b/internal/mocks/helm_client_mock.go @@ -0,0 +1,97 @@ +/* +Copyright The Kubernetes Authors. + +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. +*/ +// Code generated by MockGen. DO NOT EDIT. +// Source: ../helm_client.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + release "helm.sh/helm/v3/pkg/release" + v1alpha1 "sigs.k8s.io/cluster-api-addon-provider-helm/api/v1alpha1" +) + +// MockClient is a mock of Client interface. +type MockClient struct { + ctrl *gomock.Controller + recorder *MockClientMockRecorder +} + +// MockClientMockRecorder is the mock recorder for MockClient. +type MockClientMockRecorder struct { + mock *MockClient +} + +// NewMockClient creates a new mock instance. +func NewMockClient(ctrl *gomock.Controller) *MockClient { + mock := &MockClient{ctrl: ctrl} + mock.recorder = &MockClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClient) EXPECT() *MockClientMockRecorder { + return m.recorder +} + +// GetHelmRelease mocks base method. +func (m *MockClient) GetHelmRelease(ctx context.Context, kubeconfig string, spec v1alpha1.HelmReleaseProxySpec) (*release.Release, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetHelmRelease", ctx, kubeconfig, spec) + ret0, _ := ret[0].(*release.Release) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetHelmRelease indicates an expected call of GetHelmRelease. +func (mr *MockClientMockRecorder) GetHelmRelease(ctx, kubeconfig, spec interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHelmRelease", reflect.TypeOf((*MockClient)(nil).GetHelmRelease), ctx, kubeconfig, spec) +} + +// InstallOrUpgradeHelmRelease mocks base method. +func (m *MockClient) InstallOrUpgradeHelmRelease(ctx context.Context, kubeconfig string, spec v1alpha1.HelmReleaseProxySpec) (*release.Release, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InstallOrUpgradeHelmRelease", ctx, kubeconfig, spec) + ret0, _ := ret[0].(*release.Release) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// InstallOrUpgradeHelmRelease indicates an expected call of InstallOrUpgradeHelmRelease. +func (mr *MockClientMockRecorder) InstallOrUpgradeHelmRelease(ctx, kubeconfig, spec interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstallOrUpgradeHelmRelease", reflect.TypeOf((*MockClient)(nil).InstallOrUpgradeHelmRelease), ctx, kubeconfig, spec) +} + +// UninstallHelmRelease mocks base method. +func (m *MockClient) UninstallHelmRelease(ctx context.Context, kubeconfig string, spec v1alpha1.HelmReleaseProxySpec) (*release.UninstallReleaseResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UninstallHelmRelease", ctx, kubeconfig, spec) + ret0, _ := ret[0].(*release.UninstallReleaseResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UninstallHelmRelease indicates an expected call of UninstallHelmRelease. +func (mr *MockClientMockRecorder) UninstallHelmRelease(ctx, kubeconfig, spec interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UninstallHelmRelease", reflect.TypeOf((*MockClient)(nil).UninstallHelmRelease), ctx, kubeconfig, spec) +} diff --git a/internal/mocks/kubeconfig_mock.go b/internal/mocks/kubeconfig_mock.go new file mode 100644 index 00000000..02c20783 --- /dev/null +++ b/internal/mocks/kubeconfig_mock.go @@ -0,0 +1,66 @@ +/* +Copyright The Kubernetes Authors. + +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. +*/ +// Code generated by MockGen. DO NOT EDIT. +// Source: ../kubeconfig.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + v1beta1 "sigs.k8s.io/cluster-api/api/v1beta1" +) + +// MockGetter is a mock of Getter interface. +type MockGetter struct { + ctrl *gomock.Controller + recorder *MockGetterMockRecorder +} + +// MockGetterMockRecorder is the mock recorder for MockGetter. +type MockGetterMockRecorder struct { + mock *MockGetter +} + +// NewMockGetter creates a new mock instance. +func NewMockGetter(ctrl *gomock.Controller) *MockGetter { + mock := &MockGetter{ctrl: ctrl} + mock.recorder = &MockGetterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockGetter) EXPECT() *MockGetterMockRecorder { + return m.recorder +} + +// GetClusterKubeconfig mocks base method. +func (m *MockGetter) GetClusterKubeconfig(ctx context.Context, cluster *v1beta1.Cluster) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetClusterKubeconfig", ctx, cluster) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetClusterKubeconfig indicates an expected call of GetClusterKubeconfig. +func (mr *MockGetterMockRecorder) GetClusterKubeconfig(ctx, cluster interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClusterKubeconfig", reflect.TypeOf((*MockGetter)(nil).GetClusterKubeconfig), ctx, cluster) +}