diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c723ef..3fb230d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [v0.5.1] - 2023-10-11 +### Changed +- [#33] Replace mittwald-go-helm-client with a reduced implementation fitted to our needs + ## [v0.5.0] - 2023-10-06 ### Added - [#29] Add permissions on all namespaces to install components in namespaces like monitoring or longhorn-system. Add a new property deployNamespace. It is used to determine where the helm deployment should go. If it is empty the namespace from the component-operator is used. diff --git a/Dockerfile b/Dockerfile index a06ef69..7a49340 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,7 +35,7 @@ RUN make compile-generic FROM gcr.io/distroless/static:nonroot LABEL maintainer="hello@cloudogu.com" \ NAME="k8s-component-operator" \ - VERSION="0.5.0" + VERSION="0.5.1" WORKDIR / COPY --from=builder /workspace/target/k8s-component-operator . diff --git a/Makefile b/Makefile index 2f0625e..3d4afb2 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # Set these to the desired values ARTIFACT_ID=k8s-component-operator -VERSION=0.5.0 +VERSION=0.5.1 ## Image URL to use all building/pushing image targets IMAGE_DEV=${K3CES_REGISTRY_URL_PREFIX}/${ARTIFACT_ID}:${VERSION} IMAGE=cloudogu/${ARTIFACT_ID}:${VERSION} diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index a9ad5cf..255600f 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -11,4 +11,4 @@ kind: Kustomization images: - name: controller newName: cloudogu/k8s-component-operator - newTag: 0.5.0 + newTag: 0.5.1 diff --git a/config/samples/dogu-operator-crd.yaml b/config/samples/dogu-operator-crd.yaml new file mode 100644 index 0000000..96c20fb --- /dev/null +++ b/config/samples/dogu-operator-crd.yaml @@ -0,0 +1,7 @@ +apiVersion: k8s.cloudogu.com/v1 +kind: Component +metadata: + name: k8s-dogu-operator-crd +spec: + name: k8s-dogu-operator-crd + namespace: k8s \ No newline at end of file diff --git a/go.mod b/go.mod index 43e3ec1..7792119 100644 --- a/go.mod +++ b/go.mod @@ -8,23 +8,21 @@ require ( github.com/cloudogu/cesapp-lib v0.12.1 github.com/cloudogu/k8s-apply-lib v0.4.2 github.com/go-logr/logr v1.2.4 - github.com/mittwald/go-helm-client v0.12.3 github.com/onsi/ginkgo v1.16.5 - github.com/onsi/gomega v1.27.10 + github.com/onsi/gomega v1.28.0 + github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.3 + github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.4 - gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.12.0-dev.1.0.20230817154107-a749b663101d + helm.sh/helm/v3 v3.13.0 k8s.io/api v0.28.2 k8s.io/apimachinery v0.28.2 + k8s.io/cli-runtime v0.28.2 k8s.io/client-go v0.28.2 sigs.k8s.io/controller-runtime v0.16.2 + sigs.k8s.io/yaml v1.3.0 ) -// replace mittwald client with our own until mittwald supports plain HTTP helm registries -// this should be released in helm v3.13 which is scheduled in September 23 -replace github.com/mittwald/go-helm-client v0.12.3 => github.com/cloudogu/go-helm-client v0.0.0-20230822080918-4b3b24282d0d - require ( github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect @@ -107,7 +105,6 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.16.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect @@ -119,7 +116,6 @@ require ( github.com/shopspring/decimal v1.3.1 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/cobra v1.7.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -146,9 +142,9 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.28.2 // indirect k8s.io/apiserver v0.28.2 // indirect - k8s.io/cli-runtime v0.28.2 // indirect k8s.io/component-base v0.28.2 // indirect k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20230905202853-d090da108d2f // indirect @@ -159,5 +155,4 @@ require ( sigs.k8s.io/kustomize/api v0.14.0 // indirect sigs.k8s.io/kustomize/kyaml v0.14.3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index b105a19..27b7d3c 100644 --- a/go.sum +++ b/go.sum @@ -56,8 +56,6 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudogu/cesapp-lib v0.12.1 h1:FBHviZwc3fZB3cMqhK7BqRQD5HZZJcDt6BiG7kAKrTc= github.com/cloudogu/cesapp-lib v0.12.1/go.mod h1:PTQqI3xs1ReJMXYE6BGTF33yAfmS4J7P8UiE4AwDMDY= -github.com/cloudogu/go-helm-client v0.0.0-20230822080918-4b3b24282d0d h1:wORDIdIYkMpsHrNUXbyhllPYpM8fbXIYqv0jvrcIj6I= -github.com/cloudogu/go-helm-client v0.0.0-20230822080918-4b3b24282d0d/go.mod h1:b1jCbr7z27zWDtVPp3IjXDMwe7GrHxg/L8jOC5jW9Ls= github.com/cloudogu/k8s-apply-lib v0.4.2 h1:D5hTYvIZya+tAyGCUGaZ1T83otvpQwzrZXz5JPHQQ5M= github.com/cloudogu/k8s-apply-lib v0.4.2/go.mod h1:jR/+7q47O5gb++4gVsmEElT8/EJoi+Msw2dVzArTPW0= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= @@ -322,12 +320,12 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= -github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI= +github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.28.0 h1:i2rg/p9n/UqIDAMFUJ6qIUUMcsqOuUHgbpbu235Vr1c= +github.com/onsi/gomega v1.28.0/go.mod h1:A1H2JE76sI14WIP57LMKj7FVfCHx3g3BcZVjJG8bjX8= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= @@ -595,8 +593,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= -helm.sh/helm/v3 v3.12.0-dev.1.0.20230817154107-a749b663101d h1:6J+y+NmyX17vulRO05myRL0BzQwmzZ3bNs+h0ZUiAE4= -helm.sh/helm/v3 v3.12.0-dev.1.0.20230817154107-a749b663101d/go.mod h1:bmvO+xB/gCRkjlpIFBHGEDI4tCwmTU+tcOGqd4C4RqU= +helm.sh/helm/v3 v3.13.0 h1:XPJKIU30K4JTQ6VX/6e0hFAmEIonYa8E7wx5aqv4xOc= +helm.sh/helm/v3 v3.13.0/go.mod h1:2PBEKsMWKLVZTojUOqMS3Eadv5mP43FBWrRgLNkNm9Y= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.28.2 h1:9mpl5mOb6vXZvqbQmankOfPIGiudghwCoLl1EYfUZbw= diff --git a/pkg/api/v1/ces_component_types.go b/pkg/api/v1/ces_component_types.go index 7dfc108..4a1f226 100644 --- a/pkg/api/v1/ces_component_types.go +++ b/pkg/api/v1/ces_component_types.go @@ -3,9 +3,11 @@ package v1 import ( "embed" "fmt" - helmclient "github.com/mittwald/go-helm-client" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/cloudogu/k8s-component-operator/pkg/helm/client" ) // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! @@ -73,7 +75,7 @@ func (c *Component) String() string { } // GetHelmChartSpec returns the helm chart for the component cr without custom values. -func (c *Component) GetHelmChartSpec() *helmclient.ChartSpec { +func (c *Component) GetHelmChartSpec() *client.ChartSpec { deployNamespace := "" if c.Spec.DeployNamespace != "" { @@ -82,7 +84,7 @@ func (c *Component) GetHelmChartSpec() *helmclient.ChartSpec { deployNamespace = c.Namespace } - return &helmclient.ChartSpec{ + return &client.ChartSpec{ ReleaseName: c.Spec.Name, ChartName: fmt.Sprintf("%s/%s", c.Spec.Namespace, c.Spec.Name), Namespace: deployNamespace, diff --git a/pkg/config/config.go b/pkg/config/config.go index c305814..7929a89 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -3,14 +3,16 @@ package config import ( "context" "fmt" + "os" + "strconv" + "strings" + "github.com/Masterminds/semver/v3" - "gopkg.in/yaml.v3" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" - "os" ctrl "sigs.k8s.io/controller-runtime" - "strconv" - "strings" + "sigs.k8s.io/yaml" ) const ( diff --git a/pkg/controllers/interfaces.go b/pkg/controllers/interfaces.go index c1290f0..75a97c9 100644 --- a/pkg/controllers/interfaces.go +++ b/pkg/controllers/interfaces.go @@ -4,13 +4,13 @@ import ( "context" "time" - helmclient "github.com/mittwald/go-helm-client" "helm.sh/helm/v3/pkg/release" "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" "github.com/cloudogu/k8s-component-operator/pkg/api/ecosystem" k8sv1 "github.com/cloudogu/k8s-component-operator/pkg/api/v1" + "github.com/cloudogu/k8s-component-operator/pkg/helm/client" ) // installManager includes functionality to install components in the cluster. @@ -34,7 +34,7 @@ type upgradeManager interface { // helmClient is an interface for managing components with helm. type helmClient interface { // InstallOrUpgrade takes a helmChart and applies it. - InstallOrUpgrade(ctx context.Context, chart *helmclient.ChartSpec) error + InstallOrUpgrade(ctx context.Context, chart *client.ChartSpec) error // Uninstall removes the helmRelease for the given name Uninstall(releaseName string) error // ListDeployedReleases returns all deployed helm releases @@ -42,7 +42,7 @@ type helmClient interface { // SatisfiesDependencies validates that all dependencies are installed in the required version. A nil error // indicates that all dependencies (if any) meet the requirements, so that the client may conduct an installation or // upgrade. - SatisfiesDependencies(ctx context.Context, chart *helmclient.ChartSpec) error + SatisfiesDependencies(ctx context.Context, chart *client.ChartSpec) error } // eventRecorder embeds the record.EventRecorder interface for usage in this package. diff --git a/pkg/controllers/mock_helmClient_test.go b/pkg/controllers/mock_helmClient_test.go index abd6254..b94f101 100644 --- a/pkg/controllers/mock_helmClient_test.go +++ b/pkg/controllers/mock_helmClient_test.go @@ -5,7 +5,8 @@ package controllers import ( context "context" - helmclient "github.com/mittwald/go-helm-client" + client "github.com/cloudogu/k8s-component-operator/pkg/helm/client" + mock "github.com/stretchr/testify/mock" release "helm.sh/helm/v3/pkg/release" @@ -25,11 +26,11 @@ func (_m *mockHelmClient) EXPECT() *mockHelmClient_Expecter { } // InstallOrUpgrade provides a mock function with given fields: ctx, chart -func (_m *mockHelmClient) InstallOrUpgrade(ctx context.Context, chart *helmclient.ChartSpec) error { +func (_m *mockHelmClient) InstallOrUpgrade(ctx context.Context, chart *client.ChartSpec) error { ret := _m.Called(ctx, chart) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *helmclient.ChartSpec) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, *client.ChartSpec) error); ok { r0 = rf(ctx, chart) } else { r0 = ret.Error(0) @@ -45,14 +46,14 @@ type mockHelmClient_InstallOrUpgrade_Call struct { // InstallOrUpgrade is a helper method to define mock.On call // - ctx context.Context -// - chart *helmclient.ChartSpec +// - chart *client.ChartSpec func (_e *mockHelmClient_Expecter) InstallOrUpgrade(ctx interface{}, chart interface{}) *mockHelmClient_InstallOrUpgrade_Call { return &mockHelmClient_InstallOrUpgrade_Call{Call: _e.mock.On("InstallOrUpgrade", ctx, chart)} } -func (_c *mockHelmClient_InstallOrUpgrade_Call) Run(run func(ctx context.Context, chart *helmclient.ChartSpec)) *mockHelmClient_InstallOrUpgrade_Call { +func (_c *mockHelmClient_InstallOrUpgrade_Call) Run(run func(ctx context.Context, chart *client.ChartSpec)) *mockHelmClient_InstallOrUpgrade_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*helmclient.ChartSpec)) + run(args[0].(context.Context), args[1].(*client.ChartSpec)) }) return _c } @@ -62,7 +63,7 @@ func (_c *mockHelmClient_InstallOrUpgrade_Call) Return(_a0 error) *mockHelmClien return _c } -func (_c *mockHelmClient_InstallOrUpgrade_Call) RunAndReturn(run func(context.Context, *helmclient.ChartSpec) error) *mockHelmClient_InstallOrUpgrade_Call { +func (_c *mockHelmClient_InstallOrUpgrade_Call) RunAndReturn(run func(context.Context, *client.ChartSpec) error) *mockHelmClient_InstallOrUpgrade_Call { _c.Call.Return(run) return _c } @@ -121,11 +122,11 @@ func (_c *mockHelmClient_ListDeployedReleases_Call) RunAndReturn(run func() ([]* } // SatisfiesDependencies provides a mock function with given fields: ctx, chart -func (_m *mockHelmClient) SatisfiesDependencies(ctx context.Context, chart *helmclient.ChartSpec) error { +func (_m *mockHelmClient) SatisfiesDependencies(ctx context.Context, chart *client.ChartSpec) error { ret := _m.Called(ctx, chart) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *helmclient.ChartSpec) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, *client.ChartSpec) error); ok { r0 = rf(ctx, chart) } else { r0 = ret.Error(0) @@ -141,14 +142,14 @@ type mockHelmClient_SatisfiesDependencies_Call struct { // SatisfiesDependencies is a helper method to define mock.On call // - ctx context.Context -// - chart *helmclient.ChartSpec +// - chart *client.ChartSpec func (_e *mockHelmClient_Expecter) SatisfiesDependencies(ctx interface{}, chart interface{}) *mockHelmClient_SatisfiesDependencies_Call { return &mockHelmClient_SatisfiesDependencies_Call{Call: _e.mock.On("SatisfiesDependencies", ctx, chart)} } -func (_c *mockHelmClient_SatisfiesDependencies_Call) Run(run func(ctx context.Context, chart *helmclient.ChartSpec)) *mockHelmClient_SatisfiesDependencies_Call { +func (_c *mockHelmClient_SatisfiesDependencies_Call) Run(run func(ctx context.Context, chart *client.ChartSpec)) *mockHelmClient_SatisfiesDependencies_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*helmclient.ChartSpec)) + run(args[0].(context.Context), args[1].(*client.ChartSpec)) }) return _c } @@ -158,7 +159,7 @@ func (_c *mockHelmClient_SatisfiesDependencies_Call) Return(_a0 error) *mockHelm return _c } -func (_c *mockHelmClient_SatisfiesDependencies_Call) RunAndReturn(run func(context.Context, *helmclient.ChartSpec) error) *mockHelmClient_SatisfiesDependencies_Call { +func (_c *mockHelmClient_SatisfiesDependencies_Call) RunAndReturn(run func(context.Context, *client.ChartSpec) error) *mockHelmClient_SatisfiesDependencies_Call { _c.Call.Return(run) return _c } diff --git a/pkg/helm/client.go b/pkg/helm/client.go index 767ac27..9e8f572 100644 --- a/pkg/helm/client.go +++ b/pkg/helm/client.go @@ -3,19 +3,19 @@ package helm import ( "context" "fmt" - "github.com/cloudogu/cesapp-lib/core" - "github.com/cloudogu/k8s-component-operator/pkg/config" "sort" "strings" - helmclient "github.com/mittwald/go-helm-client" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/registry" "helm.sh/helm/v3/pkg/release" - "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/cloudogu/cesapp-lib/core" + "github.com/cloudogu/k8s-component-operator/pkg/config" + "github.com/cloudogu/k8s-component-operator/pkg/helm/client" ) const ( @@ -25,100 +25,47 @@ const ( ociSchemePrefix = string(config.EndpointSchemaOCI + "://") ) -// HelmClient embeds the helmclient.Client interface for usage in this package. +// HelmClient embeds the client.Client interface for usage in this package. type HelmClient interface { - helmclient.Client -} - -type tagResolver interface { - Tags(ref string) ([]string, error) + client.Client } // Client wraps the HelmClient with config.HelmRepositoryData type Client struct { helmClient HelmClient helmRepoData *config.HelmRepositoryData - actionConfig *action.Configuration dependencyChecker dependencyChecker - tagResolver tagResolver } // NewClient create a new instance of the helm client. func NewClient(namespace string, helmRepoData *config.HelmRepositoryData, debug bool, debugLog action.DebugLog) (*Client, error) { - opt := &helmclient.RestConfClientOptions{ - Options: &helmclient.Options{ + opt := &client.RestConfClientOptions{ + Options: &client.Options{ Namespace: namespace, RepositoryCache: helmRepositoryCache, RepositoryConfig: helmRepositoryConfig, RegistryConfig: helmRegistryConfigFile, Debug: debug, DebugLog: debugLog, - Linting: true, PlainHttp: helmRepoData.PlainHttp, }, RestConfig: ctrl.GetConfigOrDie(), } - helmClient, err := helmclient.NewClientFromRestConf(opt) + helmClient, err := client.NewClientFromRestConf(opt) if err != nil { return nil, fmt.Errorf("failed to create helm client: %w", err) } - helmRegistryClient, err := createRegistryClient(debug, helmRepoData.PlainHttp) - if err != nil { - return nil, fmt.Errorf("failed to create helm registry client: %w", err) - } - - actionConfig, err := createActionConfig(namespace, helmRegistryClient, debugLog, opt.RestConfig) - if err != nil { - return nil, fmt.Errorf("failed to create action config: %w", err) - } - return &Client{ helmClient: helmClient, helmRepoData: helmRepoData, - tagResolver: helmRegistryClient, - actionConfig: actionConfig, dependencyChecker: &installedDependencyChecker{}, }, nil } -func createActionConfig(namespace string, helmRegistryClient *registry.Client, debugLog action.DebugLog, restConfig *rest.Config) (*action.Configuration, error) { - actionConfig := new(action.Configuration) - clientGetter := helmclient.NewRESTClientGetter(namespace, nil, restConfig) - err := actionConfig.Init( - clientGetter, - namespace, - "secret", - debugLog, - ) - if err != nil { - return nil, fmt.Errorf("failed to init actionConfig: %w", err) - } - - actionConfig.RegistryClient = helmRegistryClient - return actionConfig, nil -} - -func createRegistryClient(debug bool, plainHttp bool) (*registry.Client, error) { - clientOpts := []registry.ClientOption{ - registry.ClientOptDebug(debug), - registry.ClientOptCredentialsFile(helmRegistryConfigFile), - } - - if plainHttp { - clientOpts = append(clientOpts, registry.ClientOptPlainHTTP()) - } - - helmRegistryClient, err := registry.NewClient(clientOpts...) - if err != nil { - return nil, fmt.Errorf("failed to create helm registry client: %w", err) - } - return helmRegistryClient, nil -} - // InstallOrUpgrade takes a helmChart and applies it. -func (c *Client) InstallOrUpgrade(ctx context.Context, chart *helmclient.ChartSpec) error { +func (c *Client) InstallOrUpgrade(ctx context.Context, chart *client.ChartSpec) error { // This helm-client currently only works with OCI-Helm-Repositories. // Therefore, the chartName has to include the FQDN of the repository (e.g. "oci://my.repo/...") // If in the future non-oci-repositories need to be used, this should be done here... @@ -128,7 +75,7 @@ func (c *Client) InstallOrUpgrade(ctx context.Context, chart *helmclient.ChartSp return fmt.Errorf("error patching chart-version for chart %s: %w", chart.ChartName, err) } - _, err := c.helmClient.InstallOrUpgradeChart(ctx, chart, nil) + _, err := c.helmClient.InstallOrUpgradeChart(ctx, chart) if err != nil { return fmt.Errorf("error while installOrUpgrade chart %s: %w", chart.ChartName, err) } @@ -137,7 +84,7 @@ func (c *Client) InstallOrUpgrade(ctx context.Context, chart *helmclient.ChartSp } // SatisfiesDependencies checks if all dependencies are satisfied in terms of installation and version. -func (c *Client) SatisfiesDependencies(ctx context.Context, chart *helmclient.ChartSpec) error { +func (c *Client) SatisfiesDependencies(ctx context.Context, chart *client.ChartSpec) error { logger := log.FromContext(ctx) logger.Info("Checking if components dependencies are satisfied", "component", chart.ChartName) @@ -166,7 +113,7 @@ func (c *Client) SatisfiesDependencies(ctx context.Context, chart *helmclient.Ch return nil } -func (c *Client) getChart(ctx context.Context, chartSpec *helmclient.ChartSpec) (*chart.Chart, error) { +func (c *Client) getChart(ctx context.Context, chartSpec *client.ChartSpec) (*chart.Chart, error) { logger := log.FromContext(ctx) logger.Info("Trying to get chart with options", @@ -174,8 +121,7 @@ func (c *Client) getChart(ctx context.Context, chartSpec *helmclient.ChartSpec) "version", chartSpec.Version, "plain http", c.helmRepoData.PlainHttp) - pathOptions := createChartPathOptions(c.actionConfig, chartSpec, c.helmRepoData.PlainHttp) - componentChart, _, err := c.helmClient.GetChart(chartSpec.ChartName, pathOptions) + componentChart, _, err := c.helmClient.GetChart(chartSpec) if err != nil { return nil, fmt.Errorf("error while getting chart for %s:%s: %w", chartSpec.ChartName, chartSpec.Version, err) } @@ -183,15 +129,6 @@ func (c *Client) getChart(ctx context.Context, chartSpec *helmclient.ChartSpec) return componentChart, nil } -func createChartPathOptions(config *action.Configuration, chartSpec *helmclient.ChartSpec, plainHttp bool) *action.ChartPathOptions { - // We need this installAction because it sets the registryClient in ChartPathOptions which is a private field. - install := action.NewInstall(config) - install.Version = chartSpec.Version - install.PlainHTTP = plainHttp - - return &install.ChartPathOptions -} - // Uninstall removes the helmRelease for the given name func (c *Client) Uninstall(releaseName string) error { if err := c.helmClient.UninstallReleaseByName(releaseName); err != nil { @@ -205,7 +142,7 @@ func (c *Client) ListDeployedReleases() ([]*release.Release, error) { return c.helmClient.ListDeployedReleases() } -func (c *Client) patchOciEndpoint(chart *helmclient.ChartSpec) { +func (c *Client) patchOciEndpoint(chart *client.ChartSpec) { if strings.HasPrefix(chart.ChartName, ociSchemePrefix) { return } @@ -213,13 +150,13 @@ func (c *Client) patchOciEndpoint(chart *helmclient.ChartSpec) { chart.ChartName = fmt.Sprintf("%s/%s", c.helmRepoData.URL(), chart.ChartName) } -func (c *Client) patchChartVersion(chart *helmclient.ChartSpec) error { +func (c *Client) patchChartVersion(chart *client.ChartSpec) error { if chart.Version != "" { return nil } ref := strings.TrimPrefix(chart.ChartName, ociSchemePrefix) - tags, err := c.tagResolver.Tags(ref) + tags, err := c.helmClient.Tags(ref) if err != nil { return fmt.Errorf("error resolving tags for chart %s: %w", chart.ChartName, err) } diff --git a/pkg/helm/client/LICENSE b/pkg/helm/client/LICENSE new file mode 100644 index 0000000..461009a --- /dev/null +++ b/pkg/helm/client/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Mittwald CM Service + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/pkg/helm/client/Readme.md b/pkg/helm/client/Readme.md new file mode 100644 index 0000000..7be06a6 --- /dev/null +++ b/pkg/helm/client/Readme.md @@ -0,0 +1,15 @@ +The files in this directory were copied from https://github.com/mittwald/go-helm-client/tree/15ee7e014f3c79d7b48b24fcd29a34a2990d4450. + +Their original contents are [licensed MIT and copyrighted by Mittwald CM Service](./LICENSE) except stated otherwise. + +Modifications are licensed MIT under the [root license of this repository](../../../LICENSE). +Modifications include: +- support for plain http registries +- usage of the client's `action.Config` when getting charts +- simplification of `ChartSpec` by stripping unnecessary fields +- simplification of the installation and upgrade procedures by removing (for our use-case) unnecessary code +- removal of unnecessary functions from the client +- embedding the registry client into the helm client to act as a tag resolver +- Moving example code to `example_test.go` +- refactoring to improve testability +- adding tests \ No newline at end of file diff --git a/pkg/helm/client/action.go b/pkg/helm/client/action.go new file mode 100644 index 0000000..3d7dd20 --- /dev/null +++ b/pkg/helm/client/action.go @@ -0,0 +1,151 @@ +package client + +import ( + "context" + "helm.sh/helm/v3/pkg/cli" + + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/release" +) + +type provider struct { + *action.Configuration + plainHttp bool +} + +func (p *provider) newInstall() installAction { + installAction := action.NewInstall(p.Configuration) + installAction.PlainHTTP = p.plainHttp + return &install{Install: installAction} +} + +func (p *provider) newUpgrade() upgradeAction { + upgradeAction := action.NewUpgrade(p.Configuration) + upgradeAction.PlainHTTP = p.plainHttp + return &upgrade{Upgrade: upgradeAction} +} + +func (p *provider) newUninstall() uninstallAction { + uninstallAction := action.NewUninstall(p.Configuration) + return &uninstall{Uninstall: uninstallAction} +} + +func (p *provider) newLocateChart() locateChartAction { + dummyAction := action.NewInstall(p.Configuration) + dummyAction.PlainHTTP = p.plainHttp + return &locateChart{dummyAction: dummyAction} +} + +func (p *provider) newListReleases() listReleasesAction { + listAction := action.NewList(p.Configuration) + return &listReleases{List: listAction} +} + +func (p *provider) newGetReleaseValues() getReleaseValuesAction { + getValuesAction := action.NewGetValues(p.Configuration) + return &getReleaseValues{GetValues: getValuesAction} +} + +func (p *provider) newGetRelease() getReleaseAction { + getAction := action.NewGet(p.Configuration) + return &getRelease{Get: getAction} +} + +func (p *provider) newRollbackRelease() rollbackReleaseAction { + rollbackAction := action.NewRollback(p.Configuration) + return &rollbackRelease{Rollback: rollbackAction} +} + +type install struct { + *action.Install +} + +func (i *install) install(ctx context.Context, chart *chart.Chart, values map[string]interface{}) (*release.Release, error) { + return i.RunWithContext(ctx, chart, values) +} + +func (i *install) raw() *action.Install { + return i.Install +} + +type upgrade struct { + *action.Upgrade +} + +func (u *upgrade) upgrade(ctx context.Context, releaseName string, chart *chart.Chart, values map[string]interface{}) (*release.Release, error) { + return u.RunWithContext(ctx, releaseName, chart, values) +} + +func (u *upgrade) raw() *action.Upgrade { + return u.Upgrade +} + +type uninstall struct { + *action.Uninstall +} + +func (u *uninstall) uninstall(releaseName string) (*release.UninstallReleaseResponse, error) { + return u.Run(releaseName) +} + +func (u *uninstall) raw() *action.Uninstall { + return u.Uninstall +} + +type locateChart struct { + dummyAction *action.Install +} + +func (l *locateChart) locateChart(name, version string, settings *cli.EnvSettings) (chartPath string, err error) { + l.dummyAction.Version = version + return l.dummyAction.ChartPathOptions.LocateChart(name, settings) +} + +type listReleases struct { + *action.List +} + +func (l *listReleases) listReleases() ([]*release.Release, error) { + return l.Run() +} + +func (l *listReleases) raw() *action.List { + return l.List +} + +type getReleaseValues struct { + *action.GetValues +} + +func (v *getReleaseValues) getReleaseValues(releaseName string) (map[string]interface{}, error) { + return v.Run(releaseName) +} + +func (v *getReleaseValues) raw() *action.GetValues { + return v.GetValues +} + +type getRelease struct { + *action.Get +} + +func (g *getRelease) getRelease(releaseName string) (*release.Release, error) { + return g.Run(releaseName) +} + +func (g *getRelease) raw() *action.Get { + return g.Get +} + +type rollbackRelease struct { + *action.Rollback +} + +func (r *rollbackRelease) rollbackRelease(releaseName string) error { + return r.Run(releaseName) +} + +func (r *rollbackRelease) raw() *action.Rollback { + return r.Rollback +} diff --git a/pkg/helm/client/action_test.go b/pkg/helm/client/action_test.go new file mode 100644 index 0000000..a89ae45 --- /dev/null +++ b/pkg/helm/client/action_test.go @@ -0,0 +1,100 @@ +package client + +import ( + "github.com/stretchr/testify/assert" + "helm.sh/helm/v3/pkg/action" + "testing" +) + +func Test_provider_newInstall(t *testing.T) { + // given + sut := &provider{ + Configuration: &action.Configuration{}, + plainHttp: true, + } + + // when + result := sut.newInstall() + + // then + assert.NotEmpty(t, result.raw()) + assert.True(t, result.raw().PlainHTTP) +} + +func Test_provider_newUpgrade(t *testing.T) { + // given + sut := &provider{Configuration: &action.Configuration{}} + + // when + result := sut.newUpgrade() + + // then + assert.NotEmpty(t, result.raw()) + assert.False(t, result.raw().PlainHTTP) +} + +func Test_provider_newUninstall(t *testing.T) { + // given + sut := &provider{Configuration: &action.Configuration{}} + + // when + result := sut.newUninstall() + + // then + assert.NotEmpty(t, result.raw()) +} + +func Test_provider_newLocateChart(t *testing.T) { + // given + sut := &provider{Configuration: &action.Configuration{}} + + // when + result := sut.newLocateChart() + + // then + assert.NotEmpty(t, result) +} + +func Test_provider_newListReleases(t *testing.T) { + // given + sut := &provider{Configuration: &action.Configuration{}} + + // when + result := sut.newListReleases() + + // then + assert.NotEmpty(t, result.raw()) +} + +func Test_provider_newGetReleaseValues(t *testing.T) { + // given + sut := &provider{Configuration: &action.Configuration{}} + + // when + result := sut.newGetReleaseValues() + + // then + assert.NotEmpty(t, result.raw()) +} + +func Test_provider_newGetRelease(t *testing.T) { + // given + sut := &provider{Configuration: &action.Configuration{}} + + // when + result := sut.newGetRelease() + + // then + assert.NotEmpty(t, result.raw()) +} + +func Test_provider_newRollbackRelease(t *testing.T) { + // given + sut := &provider{Configuration: &action.Configuration{}} + + // when + result := sut.newRollbackRelease() + + // then + assert.NotEmpty(t, result.raw()) +} diff --git a/pkg/helm/client/client.go b/pkg/helm/client/client.go new file mode 100644 index 0000000..d93caff --- /dev/null +++ b/pkg/helm/client/client.go @@ -0,0 +1,447 @@ +package client + +import ( + "context" + "fmt" + "log" + "os" + + "github.com/spf13/pflag" + + "k8s.io/cli-runtime/pkg/genericclioptions" + + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/registry" + "helm.sh/helm/v3/pkg/release" +) + +const ( + defaultCachePath = "/tmp/.helmcache" + defaultRepositoryConfigPath = "/tmp/.helmrepo" +) + +const anyVersionConstraint = ">0.0.0-0" + +var defaultDebugLog = func(format string, v ...interface{}) { + log.Printf(format, v...) +} + +// NewClientFromRestConf returns a new Helm client constructed with the provided REST config options. +func NewClientFromRestConf(options *RestConfClientOptions) (Client, error) { + settings := cli.New() + + clientGetter := NewRESTClientGetter(options.Namespace, nil, options.RestConfig) + + return newClient(options.Options, clientGetter, settings) +} + +// newClient is used by both NewClientFromKubeConf and NewClientFromRestConf +// and returns a new Helm client via the provided options and REST config. +func newClient(options *Options, clientGetter genericclioptions.RESTClientGetter, settings *cli.EnvSettings) (Client, error) { + err := setEnvSettings(&options, settings) + if err != nil { + return nil, err + } + + debugLog := options.DebugLog + if debugLog == nil { + debugLog = defaultDebugLog + } + + if options.Output == nil { + options.Output = os.Stdout + } + + actionConfig := new(action.Configuration) + err = actionConfig.Init( + clientGetter, + settings.Namespace(), + "secret", + debugLog, + ) + if err != nil { + return nil, err + } + + clientOpts := []registry.ClientOption{ + registry.ClientOptDebug(settings.Debug), + registry.ClientOptCredentialsFile(settings.RegistryConfig), + } + + if options.PlainHttp { + clientOpts = append(clientOpts, registry.ClientOptPlainHTTP()) + } + + registryClient, err := registry.NewClient(clientOpts...) + if err != nil { + return nil, err + } + actionConfig.RegistryClient = registryClient + + actionProvider := &provider{ + Configuration: actionConfig, + plainHttp: options.PlainHttp, + } + + return &HelmClient{ + TagResolver: registryClient, + Settings: settings, + actions: actionProvider, + DebugLog: debugLog, + output: options.Output, + }, nil +} + +// setEnvSettings sets the client's environment settings based on the provided client configuration. +func setEnvSettings(ppOptions **Options, settings *cli.EnvSettings) error { + if *ppOptions == nil { + *ppOptions = &Options{ + RepositoryConfig: defaultRepositoryConfigPath, + RepositoryCache: defaultCachePath, + } + } + + options := *ppOptions + + // set the namespace with this ugly workaround because cli.EnvSettings.namespace is private + // thank you helm! + if options.Namespace != "" { + pflags := pflag.NewFlagSet("", pflag.ContinueOnError) + settings.AddFlags(pflags) + err := pflags.Parse([]string{"-n", options.Namespace}) + if err != nil { + return err + } + } + + if options.RepositoryConfig == "" { + options.RepositoryConfig = defaultRepositoryConfigPath + } + + if options.RepositoryCache == "" { + options.RepositoryCache = defaultCachePath + } + + settings.RepositoryCache = options.RepositoryCache + settings.RepositoryConfig = options.RepositoryConfig + settings.Debug = options.Debug + + if options.RegistryConfig != "" { + settings.RegistryConfig = options.RegistryConfig + } + + return nil +} + +// InstallOrUpgradeChart installs or upgrades the provided chart and returns the corresponding release. +// Namespace and other context is provided via the client.Options struct when instantiating a client. +func (c *HelmClient) InstallOrUpgradeChart(ctx context.Context, spec *ChartSpec) (*release.Release, error) { + exists, err := c.chartExists(spec) + if err != nil { + return nil, err + } + + if exists { + return c.upgrade(ctx, spec) + } + + return c.install(ctx, spec) +} + +// InstallChart installs the provided chart and returns the corresponding release. +// Namespace and other context is provided via the client.Options struct when instantiating a client. +func (c *HelmClient) InstallChart(ctx context.Context, spec *ChartSpec) (*release.Release, error) { + return c.install(ctx, spec) +} + +// UpgradeChart upgrades the provided chart and returns the corresponding release. +// Namespace and other context is provided via the client.Options struct when instantiating a client. +func (c *HelmClient) UpgradeChart(ctx context.Context, spec *ChartSpec) (*release.Release, error) { + return c.upgrade(ctx, spec) +} + +// ListDeployedReleases lists all deployed releases. +// Namespace and other context is provided via the client.Options struct when instantiating a client. +func (c *HelmClient) ListDeployedReleases() ([]*release.Release, error) { + return c.listReleases(action.ListDeployed) +} + +// ListReleasesByStateMask lists all releases filtered by stateMask. +// Namespace and other context is provided via the client.Options struct when instantiating a client. +func (c *HelmClient) ListReleasesByStateMask(states action.ListStates) ([]*release.Release, error) { + return c.listReleases(states) +} + +// GetReleaseValues returns the (optionally, all computed) values for the specified release. +func (c *HelmClient) GetReleaseValues(name string, allValues bool) (map[string]interface{}, error) { + return c.getReleaseValues(name, allValues) +} + +// GetRelease returns a release specified by name. +func (c *HelmClient) GetRelease(name string) (*release.Release, error) { + return c.getRelease(name) +} + +// RollbackRelease implicitly rolls back a release to the last revision. +func (c *HelmClient) RollbackRelease(spec *ChartSpec) error { + return c.rollbackRelease(spec) +} + +// UninstallRelease uninstalls the provided release +func (c *HelmClient) UninstallRelease(spec *ChartSpec) error { + return c.uninstallRelease(spec) +} + +// UninstallReleaseByName uninstalls a release identified by the provided 'name'. +func (c *HelmClient) UninstallReleaseByName(name string) error { + return c.uninstallReleaseByName(name) +} + +// install installs the provided chart. +func (c *HelmClient) install(ctx context.Context, spec *ChartSpec) (*release.Release, error) { + installAction := c.actions.newInstall() + client := installAction.raw() + mergeInstallOptions(spec, client) + + // NameAndChart returns either the TemplateName if set, + // the ReleaseName if set or the generatedName as the first return value. + releaseName, _, err := client.NameAndChart([]string{spec.ChartName}) + if err != nil { + return nil, fmt.Errorf("failed to determine release name for chart %q: %w", spec.ChartName, err) + } + client.ReleaseName = releaseName + + if client.Version == "" { + client.Version = anyVersionConstraint + } + + helmChart, _, err := c.GetChart(spec) + if err != nil { + return nil, fmt.Errorf("failed to get chart for release %q: %w", spec.ReleaseName, err) + } + + if helmChart.Metadata.Type != "" && helmChart.Metadata.Type != "application" { + return nil, fmt.Errorf( + "chart %q has an unsupported type and is not installable: %q", + helmChart.Metadata.Name, + helmChart.Metadata.Type, + ) + } + + p := getter.All(c.Settings) + values, err := spec.GetValuesMap(p) + if err != nil { + return nil, fmt.Errorf("failed to get values for release %q: %w", spec.ReleaseName, err) + } + + rel, err := installAction.install(ctx, helmChart, values) + if err != nil { + return nil, fmt.Errorf("failed to install release %q: %w", spec.ReleaseName, err) + } + + c.DebugLog("release installed successfully: %s/%s-%s", rel.Name, rel.Chart.Metadata.Name, rel.Chart.Metadata.Version) + + return rel, nil +} + +// upgrade upgrades a chart and CRDs. +func (c *HelmClient) upgrade(ctx context.Context, spec *ChartSpec) (*release.Release, error) { + upgradeAction := c.actions.newUpgrade() + client := upgradeAction.raw() + mergeUpgradeOptions(spec, client) + client.Install = true + + if client.Version == "" { + client.Version = anyVersionConstraint + } + + helmChart, _, err := c.GetChart(spec) + if err != nil { + return nil, fmt.Errorf("failed to get chart for release %q: %w", spec.ReleaseName, err) + } + + p := getter.All(c.Settings) + values, err := spec.GetValuesMap(p) + if err != nil { + return nil, fmt.Errorf("failed to get values for release %q: %w", spec.ReleaseName, err) + } + + upgradedRelease, upgradeErr := upgradeAction.upgrade(ctx, spec.ReleaseName, helmChart, values) + if upgradeErr != nil { + var resultErr error + + rollbackErr := c.rollbackRelease(spec) + if rollbackErr != nil { + resultErr = fmt.Errorf("release failed, rollback failed: release error: %w, rollback error: %v", upgradeErr, rollbackErr) + } else { + resultErr = fmt.Errorf("release failed, rollback succeeded: release error: %w", upgradeErr) + } + + c.DebugLog("release upgrade failed: %s", resultErr) + return nil, fmt.Errorf("failed to upgrade release %q: %w", spec.ReleaseName, resultErr) + } + + c.DebugLog("release upgraded successfully: %s/%s-%s", upgradedRelease.Name, upgradedRelease.Chart.Metadata.Name, upgradedRelease.Chart.Metadata.Version) + + return upgradedRelease, nil +} + +// uninstallRelease uninstalls the provided release. +func (c *HelmClient) uninstallRelease(spec *ChartSpec) error { + uninstallAction := c.actions.newUninstall() + mergeUninstallReleaseOptions(spec, uninstallAction.raw()) + + resp, err := uninstallAction.uninstall(spec.ReleaseName) + if err != nil { + return fmt.Errorf("failed to uninstall release %q: %w", spec.ReleaseName, err) + } + + c.DebugLog("release uninstalled, response: %v", resp) + + return nil +} + +// uninstallReleaseByName uninstalls a release identified by the provided 'name'. +func (c *HelmClient) uninstallReleaseByName(name string) error { + uninstallAction := c.actions.newUninstall() + + resp, err := uninstallAction.uninstall(name) + if err != nil { + return fmt.Errorf("failed to uninstall release %q: %w", name, err) + } + + c.DebugLog("release uninstalled, response: %v", resp) + + return nil +} + +// GetChart returns a chart matching the provided chart name and options. +func (c *HelmClient) GetChart(spec *ChartSpec) (*chart.Chart, string, error) { + locateAction := c.actions.newLocateChart() + + if spec.Version == "" { + spec.Version = anyVersionConstraint + } + + chartPath, err := locateAction.locateChart(spec.ChartName, spec.Version, c.Settings) + if err != nil { + return nil, "", fmt.Errorf("failed to locate chart %q with version %q: %w", spec.ChartName, spec.Version, err) + } + + helmChart, err := loader.Load(chartPath) + if err != nil { + return nil, "", fmt.Errorf("failed to load chart %q with version %q from path %q: %w", spec.ChartName, spec.Version, chartPath, err) + } + + if helmChart.Metadata.Deprecated { + c.DebugLog("WARNING: This chart (%q) is deprecated", helmChart.Metadata.Name) + } + + return helmChart, chartPath, err +} + +// chartExists checks whether a chart is already installed +// in a namespace or not based on the provided chart spec. +// Note that this function only considers the contained chart name and namespace. +func (c *HelmClient) chartExists(spec *ChartSpec) (bool, error) { + releases, err := c.listReleases(action.ListAll) + if err != nil { + return false, fmt.Errorf("could not check if release %q is already installed: %w", spec.ReleaseName, err) + } + + for _, r := range releases { + if r.Name == spec.ReleaseName && r.Namespace == spec.Namespace { + return true, nil + } + } + + return false, nil +} + +// listReleases lists all releases that match the given state. +func (c *HelmClient) listReleases(state action.ListStates) ([]*release.Release, error) { + listAction := c.actions.newListReleases() + listAction.raw().StateMask = state + + releases, err := listAction.listReleases() + if err != nil { + return nil, fmt.Errorf("failed to list releases: %w", err) + } + + return releases, nil +} + +// getReleaseValues returns the values for the provided release 'name'. +// If allValues = true is specified, all computed values are returned. +func (c *HelmClient) getReleaseValues(name string, allValues bool) (map[string]interface{}, error) { + getReleaseValuesAction := c.actions.newGetReleaseValues() + getReleaseValuesAction.raw().AllValues = allValues + + values, err := getReleaseValuesAction.getReleaseValues(name) + if err != nil { + return nil, fmt.Errorf("failed to get values of release %q: %w", name, err) + } + + return values, nil +} + +// getRelease returns a release matching the provided 'name'. +func (c *HelmClient) getRelease(name string) (*release.Release, error) { + getReleaseAction := c.actions.newGetRelease() + + rel, err := getReleaseAction.getRelease(name) + if err != nil { + return nil, fmt.Errorf("failed to get release %q: %w", name, err) + } + + return rel, nil +} + +// rollbackRelease implicitly rolls back a release to the last revision. +func (c *HelmClient) rollbackRelease(spec *ChartSpec) error { + rollbackAction := c.actions.newRollbackRelease() + mergeRollbackOptions(spec, rollbackAction.raw()) + + err := rollbackAction.rollbackRelease(spec.ReleaseName) + if err != nil { + return fmt.Errorf("failed to rollback release %q: %w", spec.ReleaseName, err) + } + + return nil +} + +// mergeRollbackOptions merges values of the provided chart to helm rollback options used by the client. +func mergeRollbackOptions(chartSpec *ChartSpec, rollbackOptions *action.Rollback) { + rollbackOptions.Timeout = chartSpec.Timeout + rollbackOptions.CleanupOnFail = chartSpec.CleanupOnFail +} + +// mergeInstallOptions merges values of the provided chart to helm install options used by the client. +func mergeInstallOptions(chartSpec *ChartSpec, installOptions *action.Install) { + installOptions.CreateNamespace = chartSpec.CreateNamespace + installOptions.Timeout = chartSpec.Timeout + installOptions.Namespace = chartSpec.Namespace + installOptions.ReleaseName = chartSpec.ReleaseName + installOptions.Version = chartSpec.Version + installOptions.Atomic = chartSpec.Atomic +} + +// mergeUpgradeOptions merges values of the provided chart to helm upgrade options used by the client. +func mergeUpgradeOptions(chartSpec *ChartSpec, upgradeOptions *action.Upgrade) { + upgradeOptions.Version = chartSpec.Version + upgradeOptions.Namespace = chartSpec.Namespace + upgradeOptions.Timeout = chartSpec.Timeout + upgradeOptions.ResetValues = chartSpec.ResetValues + upgradeOptions.ReuseValues = chartSpec.ReuseValues + upgradeOptions.Atomic = chartSpec.Atomic + upgradeOptions.CleanupOnFail = chartSpec.CleanupOnFail +} + +// mergeUninstallReleaseOptions merges values of the provided chart to helm uninstall options used by the client. +func mergeUninstallReleaseOptions(chartSpec *ChartSpec, uninstallReleaseOptions *action.Uninstall) { + uninstallReleaseOptions.Timeout = chartSpec.Timeout +} diff --git a/pkg/helm/client/client_getter.go b/pkg/helm/client/client_getter.go new file mode 100644 index 0000000..be87486 --- /dev/null +++ b/pkg/helm/client/client_getter.go @@ -0,0 +1,74 @@ +package client + +import ( + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/client-go/discovery" + "k8s.io/client-go/discovery/cached/memory" + "k8s.io/client-go/rest" + "k8s.io/client-go/restmapper" + "k8s.io/client-go/tools/clientcmd" +) + +// NewRESTClientGetter returns a RESTClientGetter using the provided 'namespace', 'kubeConfig' and 'restConfig'. +// +// source: https://github.com/helm/helm/issues/6910#issuecomment-601277026 +func NewRESTClientGetter(namespace string, kubeConfig []byte, restConfig *rest.Config, opts ...RESTClientOption) *RESTClientGetter { + return &RESTClientGetter{ + namespace: namespace, + kubeConfig: kubeConfig, + restConfig: restConfig, + opts: opts, + } +} + +// ToRESTConfig returns a REST config build from a given kubeconfig +func (c *RESTClientGetter) ToRESTConfig() (*rest.Config, error) { + if c.restConfig != nil { + return c.restConfig, nil + } + + return clientcmd.RESTConfigFromKubeConfig(c.kubeConfig) +} + +// ToDiscoveryClient returns a CachedDiscoveryInterface that can be used as a discovery client. +func (c *RESTClientGetter) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) { + config, err := c.ToRESTConfig() + if err != nil { + return nil, err + } + + // The more API groups exist, the more discovery requests need to be made. + // Given 25 API groups with about one version each, discovery needs to make 50 requests. + // This setting is only used for discovery. + config.Burst = 100 + + for _, fn := range c.opts { + fn(config) + } + + discoveryClient, _ := discovery.NewDiscoveryClientForConfig(config) + return memory.NewMemCacheClient(discoveryClient), nil +} + +func (c *RESTClientGetter) ToRESTMapper() (meta.RESTMapper, error) { + discoveryClient, err := c.ToDiscoveryClient() + if err != nil { + return nil, err + } + + mapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient) + expander := restmapper.NewShortcutExpander(mapper, discoveryClient) + return expander, nil +} + +func (c *RESTClientGetter) ToRawKubeConfigLoader() clientcmd.ClientConfig { + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + // use the standard defaults for this client command + // DEPRECATED: remove and replace with something more accurate + loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig + + overrides := &clientcmd.ConfigOverrides{ClusterDefaults: clientcmd.ClusterDefaults} + overrides.Context.Namespace = c.namespace + + return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides) +} diff --git a/pkg/helm/client/client_getter_test.go b/pkg/helm/client/client_getter_test.go new file mode 100644 index 0000000..7198bab --- /dev/null +++ b/pkg/helm/client/client_getter_test.go @@ -0,0 +1,109 @@ +package client + +import ( + _ "embed" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "k8s.io/client-go/rest" +) + +//go:embed testdata/kubeconfig.yaml +var kubeconfigBytes []byte + +//go:embed testdata/invalid-kubeconfig.yaml +var invalidKubeconfigBytes []byte + +func TestRESTClientGetter_ToRESTConfig(t *testing.T) { + t.Run("should return rest config if not nil", func(t *testing.T) { + // given + restConfig := &rest.Config{} + sut := &RESTClientGetter{restConfig: restConfig} + + // when + actual, err := sut.ToRESTConfig() + + // then + require.NoError(t, err) + assert.Same(t, restConfig, actual) + }) + t.Run("should return rest config for kubeconfig", func(t *testing.T) { + // given + sut := &RESTClientGetter{kubeConfig: kubeconfigBytes} + + // when + actual, err := sut.ToRESTConfig() + + // then + require.NoError(t, err) + assert.NotNil(t, actual) + }) +} + +func TestRESTClientGetter_ToDiscoveryClient(t *testing.T) { + t.Run("should fail to create rest config", func(t *testing.T) { + // given + sut := &RESTClientGetter{kubeConfig: invalidKubeconfigBytes} + + // when + _, err := sut.ToDiscoveryClient() + + // then + require.Error(t, err) + }) + t.Run("should create discovery client", func(t *testing.T) { + // given + sut := &RESTClientGetter{ + kubeConfig: kubeconfigBytes, + opts: []RESTClientOption{func(config *rest.Config) { + config.UserAgent = "my-user-agent" + }}, + } + + // when + actual, err := sut.ToDiscoveryClient() + + // then + require.NoError(t, err) + assert.NotNil(t, actual) + }) +} + +func TestRESTClientGetter_ToRESTMapper(t *testing.T) { + t.Run("should fail to create discovery client", func(t *testing.T) { + // given + sut := &RESTClientGetter{kubeConfig: invalidKubeconfigBytes} + + // when + _, err := sut.ToRESTMapper() + + // then + require.Error(t, err) + }) + t.Run("should succeed to create rest mapper", func(t *testing.T) { + // given + sut := &RESTClientGetter{kubeConfig: kubeconfigBytes} + + // when + actual, err := sut.ToRESTMapper() + + // then + require.NoError(t, err) + assert.NotNil(t, actual) + }) +} + +func TestRESTClientGetter_ToRawKubeConfigLoader(t *testing.T) { + t.Run("should return config loader", func(t *testing.T) { + // given + sut := &RESTClientGetter{namespace: "test-namespace"} + + // when + actual := sut.ToRawKubeConfigLoader() + + // then + assert.NotNil(t, actual) + }) +} diff --git a/pkg/helm/client/client_test.go b/pkg/helm/client/client_test.go new file mode 100644 index 0000000..ded8bfe --- /dev/null +++ b/pkg/helm/client/client_test.go @@ -0,0 +1,1025 @@ +package client + +import ( + "context" + "github.com/stretchr/testify/mock" + "helm.sh/helm/v3/pkg/chart" + "log" + "os" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/release" + + "k8s.io/client-go/rest" +) + +var testCtx = context.TODO() + +func Test_defaultDebugLog(t *testing.T) { + // given + defer func() { log.Default().SetOutput(os.Stderr) }() + buf := new(strings.Builder) + log.Default().SetOutput(buf) + + // when + defaultDebugLog("test %s %d %v", "1", 2, 3) + + // then + assert.Contains(t, buf.String(), "test 1 2 3") +} + +func TestNewClientFromRestConf(t *testing.T) { + // given + opt := &RestConfClientOptions{ + Options: &Options{ + RegistryConfig: "/tmp/.registry.conf", + Namespace: "default", + Debug: true, + PlainHttp: true, + }, + RestConfig: &rest.Config{}, + } + + // when + actual, err := NewClientFromRestConf(opt) + + // then + require.NoError(t, err) + assert.NotEmpty(t, actual) +} + +func Test_setEnvSettings(t *testing.T) { + // given + settings := &cli.EnvSettings{} + options := new(*Options) + + // when + err := setEnvSettings(options, settings) + + // then + require.NoError(t, err) + assert.Equal(t, defaultRepositoryConfigPath, settings.RepositoryConfig) + assert.Equal(t, defaultCachePath, settings.RepositoryCache) + assert.Equal(t, defaultRepositoryConfigPath, (*options).RepositoryConfig) + assert.Equal(t, defaultCachePath, (*options).RepositoryCache) +} + +func TestHelmClient_UninstallReleaseByName(t *testing.T) { + t.Run("should fail to uninstall release", func(t *testing.T) { + // given + uninstallMock := newMockUninstallAction(t) + uninstallMock.EXPECT().uninstall("test-release").Return(nil, assert.AnError) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newUninstall().Return(uninstallMock) + + sut := &HelmClient{ + actions: providerMock, + } + + // when + err := sut.UninstallReleaseByName("test-release") + + // then + require.Error(t, err) + assert.ErrorIs(t, err, assert.AnError) + assert.ErrorContains(t, err, "failed to uninstall release \"test-release\"") + }) + t.Run("should succeed to uninstall release", func(t *testing.T) { + // given + uninstallResponse := &release.UninstallReleaseResponse{ + Release: &release.Release{Name: "test-release"}, + Info: "uninstall successful", + } + + uninstallMock := newMockUninstallAction(t) + uninstallMock.EXPECT().uninstall("test-release").Return(uninstallResponse, nil) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newUninstall().Return(uninstallMock) + + sut := &HelmClient{ + actions: providerMock, + DebugLog: func(format string, v ...interface{}) { + t.Helper() + assert.Equal(t, "release uninstalled, response: %v", format) + require.NotEmpty(t, v) + assert.Same(t, uninstallResponse, v[0]) + }, + } + + // when + err := sut.UninstallReleaseByName("test-release") + + // then + require.NoError(t, err) + }) +} + +func TestHelmClient_UninstallRelease(t *testing.T) { + t.Run("should fail to uninstall release", func(t *testing.T) { + // given + spec := &ChartSpec{ + ReleaseName: "test-release", + Timeout: 42, + } + uninstallAction := &action.Uninstall{} + + uninstallMock := newMockUninstallAction(t) + uninstallMock.EXPECT().raw().Return(uninstallAction) + uninstallMock.EXPECT().uninstall("test-release").Return(nil, assert.AnError) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newUninstall().Return(uninstallMock) + + sut := &HelmClient{ + actions: providerMock, + } + + // when + err := sut.UninstallRelease(spec) + + // then + require.Error(t, err) + assert.ErrorIs(t, err, assert.AnError) + assert.ErrorContains(t, err, "failed to uninstall release \"test-release\"") + assert.Equal(t, time.Duration(42), uninstallAction.Timeout) + }) + t.Run("should succeed to uninstall release", func(t *testing.T) { + // given + spec := &ChartSpec{ + ReleaseName: "test-release", + Timeout: 69, + } + uninstallAction := &action.Uninstall{} + uninstallResponse := &release.UninstallReleaseResponse{ + Release: &release.Release{Name: "test-release"}, + Info: "uninstall successful", + } + + uninstallMock := newMockUninstallAction(t) + uninstallMock.EXPECT().raw().Return(uninstallAction) + uninstallMock.EXPECT().uninstall("test-release").Return(uninstallResponse, nil) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newUninstall().Return(uninstallMock) + + sut := &HelmClient{ + actions: providerMock, + DebugLog: func(format string, v ...interface{}) { + t.Helper() + assert.Equal(t, "release uninstalled, response: %v", format) + require.NotEmpty(t, v) + assert.Same(t, uninstallResponse, v[0]) + }, + } + + // when + err := sut.UninstallRelease(spec) + + // then + require.NoError(t, err) + assert.Equal(t, time.Duration(69), uninstallAction.Timeout) + }) +} + +func TestHelmClient_RollbackRelease(t *testing.T) { + t.Run("should fail to rollback release", func(t *testing.T) { + // given + spec := &ChartSpec{ + ReleaseName: "test-release", + Timeout: 42, + CleanupOnFail: true, + } + rollbackAction := &action.Rollback{} + + rollbackMock := newMockRollbackReleaseAction(t) + rollbackMock.EXPECT().raw().Return(rollbackAction) + rollbackMock.EXPECT().rollbackRelease("test-release").Return(assert.AnError) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newRollbackRelease().Return(rollbackMock) + + sut := &HelmClient{ + actions: providerMock, + } + + // when + err := sut.RollbackRelease(spec) + + // then + require.Error(t, err) + assert.ErrorIs(t, err, assert.AnError) + assert.ErrorContains(t, err, "failed to rollback release \"test-release\"") + + assert.Equal(t, time.Duration(42), rollbackAction.Timeout) + assert.True(t, rollbackAction.CleanupOnFail) + }) + t.Run("should succeed to rollback release", func(t *testing.T) { + // given + spec := &ChartSpec{ + ReleaseName: "test-release", + Timeout: 69, + } + rollbackAction := &action.Rollback{} + + rollbackMock := newMockRollbackReleaseAction(t) + rollbackMock.EXPECT().raw().Return(rollbackAction) + rollbackMock.EXPECT().rollbackRelease("test-release").Return(nil) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newRollbackRelease().Return(rollbackMock) + + sut := &HelmClient{ + actions: providerMock, + } + + // when + err := sut.RollbackRelease(spec) + + // then + require.NoError(t, err) + assert.Equal(t, time.Duration(69), rollbackAction.Timeout) + assert.False(t, rollbackAction.CleanupOnFail) + }) +} + +func TestHelmClient_GetRelease(t *testing.T) { + t.Run("should fail to get release", func(t *testing.T) { + // given + getReleaseMock := newMockGetReleaseAction(t) + getReleaseMock.EXPECT().getRelease("test-release").Return(nil, assert.AnError) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newGetRelease().Return(getReleaseMock) + + sut := &HelmClient{ + actions: providerMock, + } + + // when + actual, err := sut.GetRelease("test-release") + + // then + require.Error(t, err) + assert.Nil(t, actual) + assert.ErrorIs(t, err, assert.AnError) + assert.ErrorContains(t, err, "failed to get release \"test-release\"") + }) + t.Run("should succeed to get release", func(t *testing.T) { + // given + expectedRelease := release.Release{Name: "test-release"} + getReleaseMock := newMockGetReleaseAction(t) + getReleaseMock.EXPECT().getRelease("test-release").Return(&expectedRelease, nil) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newGetRelease().Return(getReleaseMock) + + sut := &HelmClient{ + actions: providerMock, + } + + // when + actual, err := sut.GetRelease("test-release") + + // then + require.NoError(t, err) + assert.Equal(t, expectedRelease, *actual) + }) +} + +func TestHelmClient_GetReleaseValues(t *testing.T) { + t.Run("should fail to get release values", func(t *testing.T) { + // given + getValuesAction := &action.GetValues{} + getValuesMock := newMockGetReleaseValuesAction(t) + getValuesMock.EXPECT().raw().Return(getValuesAction) + getValuesMock.EXPECT().getReleaseValues("test-release").Return(nil, assert.AnError) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newGetReleaseValues().Return(getValuesMock) + + sut := &HelmClient{ + actions: providerMock, + } + + // when + actual, err := sut.GetReleaseValues("test-release", true) + + // then + require.Error(t, err) + + assert.ErrorIs(t, err, assert.AnError) + assert.ErrorContains(t, err, "failed to get values of release \"test-release\"") + + assert.Nil(t, actual) + assert.True(t, getValuesAction.AllValues) + }) + t.Run("should succeed to get release values", func(t *testing.T) { + // given + getValuesAction := &action.GetValues{} + expectedValues := map[string]interface{}{"myKey": "myValue"} + getValuesMock := newMockGetReleaseValuesAction(t) + getValuesMock.EXPECT().raw().Return(getValuesAction) + getValuesMock.EXPECT().getReleaseValues("test-release").Return(expectedValues, nil) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newGetReleaseValues().Return(getValuesMock) + + sut := &HelmClient{ + actions: providerMock, + } + + // when + actual, err := sut.GetReleaseValues("test-release", false) + + // then + require.NoError(t, err) + + assert.Equal(t, expectedValues, actual) + assert.False(t, getValuesAction.AllValues) + }) +} + +func TestHelmClient_ListDeployedReleases(t *testing.T) { + t.Run("should fail to list deployed releases", func(t *testing.T) { + // given + listAction := &action.List{} + listMock := newMockListReleasesAction(t) + listMock.EXPECT().raw().Return(listAction) + listMock.EXPECT().listReleases().Return(nil, assert.AnError) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newListReleases().Return(listMock) + + sut := &HelmClient{ + actions: providerMock, + } + + // when + actual, err := sut.ListDeployedReleases() + + // then + require.Error(t, err) + assert.ErrorIs(t, err, assert.AnError) + assert.ErrorContains(t, err, "failed to list releases") + + assert.Nil(t, actual) + assert.Equal(t, action.ListDeployed, listAction.StateMask) + }) + t.Run("should succeed to list deployed releases", func(t *testing.T) { + // given + listAction := &action.List{} + expectedReleases := []*release.Release{{Name: "test-release"}} + listMock := newMockListReleasesAction(t) + listMock.EXPECT().raw().Return(listAction) + listMock.EXPECT().listReleases().Return(expectedReleases, nil) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newListReleases().Return(listMock) + + sut := &HelmClient{ + actions: providerMock, + } + + // when + actual, err := sut.ListDeployedReleases() + + // then + require.NoError(t, err) + assert.Equal(t, expectedReleases, actual) + assert.Equal(t, action.ListDeployed, listAction.StateMask) + }) +} + +func TestHelmClient_ListReleasesByStateMask(t *testing.T) { + t.Run("should succeed to list failed releases", func(t *testing.T) { + // given + listAction := &action.List{} + expectedReleases := []*release.Release{{Name: "test-release"}} + listMock := newMockListReleasesAction(t) + listMock.EXPECT().raw().Return(listAction) + listMock.EXPECT().listReleases().Return(expectedReleases, nil) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newListReleases().Return(listMock) + + sut := &HelmClient{ + actions: providerMock, + } + + // when + actual, err := sut.ListReleasesByStateMask(action.ListFailed) + + // then + require.NoError(t, err) + assert.Equal(t, expectedReleases, actual) + assert.Equal(t, action.ListFailed, listAction.StateMask) + }) +} + +func TestHelmClient_InstallChart(t *testing.T) { + t.Run("should fail on empty release name", func(t *testing.T) { + // given + spec := &ChartSpec{ChartName: "test-chart", ReleaseName: ""} + installAction := &action.Install{} + + installMock := newMockInstallAction(t) + installMock.EXPECT().raw().Return(installAction) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newInstall().Return(installMock) + + sut := &HelmClient{ + actions: providerMock, + } + + // when + actual, err := sut.InstallChart(testCtx, spec) + + // then + require.Error(t, err) + assert.ErrorContains(t, err, "failed to determine release name for chart \"test-chart\"") + assert.Nil(t, actual) + }) + t.Run("should fail to get chart", func(t *testing.T) { + // given + spec := &ChartSpec{ + ChartName: "test-chart", + ReleaseName: "test-release", + } + installAction := &action.Install{} + + installMock := newMockInstallAction(t) + installMock.EXPECT().raw().Return(installAction) + locateMock := newMockLocateChartAction(t) + locateMock.EXPECT().locateChart("test-chart", ">0.0.0-0", (*cli.EnvSettings)(nil)).Return("", assert.AnError) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newInstall().Return(installMock) + providerMock.EXPECT().newLocateChart().Return(locateMock) + + sut := &HelmClient{ + actions: providerMock, + } + + // when + actual, err := sut.InstallChart(testCtx, spec) + + // then + require.Error(t, err) + assert.ErrorIs(t, err, assert.AnError) + assert.ErrorContains(t, err, "failed to get chart for release \"test-release\"") + assert.ErrorContains(t, err, "failed to locate chart \"test-chart\" with version \">0.0.0-0\"") + assert.Nil(t, actual) + }) + t.Run("should fail because chart has unsupported type", func(t *testing.T) { + // given + spec := &ChartSpec{ + ChartName: "test-chart", + ReleaseName: "test-release", + } + installAction := &action.Install{} + + installMock := newMockInstallAction(t) + installMock.EXPECT().raw().Return(installAction) + locateMock := newMockLocateChartAction(t) + locateMock.EXPECT().locateChart("test-chart", ">0.0.0-0", (*cli.EnvSettings)(nil)).Return("testdata/invalid-type-test-chart", nil) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newInstall().Return(installMock) + providerMock.EXPECT().newLocateChart().Return(locateMock) + + sut := &HelmClient{ + actions: providerMock, + } + + // when + actual, err := sut.InstallChart(testCtx, spec) + + // then + require.Error(t, err) + assert.ErrorContains(t, err, "chart \"test-chart\" has an unsupported type and is not installable: \"library\"") + assert.Nil(t, actual) + }) + t.Run("should fail to get values", func(t *testing.T) { + // given + spec := &ChartSpec{ + ChartName: "test-chart", + ReleaseName: "test-release", + ValuesYaml: "invalid YAML", + } + installAction := &action.Install{} + envSettings := &cli.EnvSettings{ + RepositoryConfig: defaultRepositoryConfigPath, + RepositoryCache: defaultCachePath, + } + + installMock := newMockInstallAction(t) + installMock.EXPECT().raw().Return(installAction) + locateMock := newMockLocateChartAction(t) + locateMock.EXPECT().locateChart("test-chart", ">0.0.0-0", envSettings).Return("testdata/test-chart", nil) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newInstall().Return(installMock) + providerMock.EXPECT().newLocateChart().Return(locateMock) + + sut := &HelmClient{ + Settings: envSettings, + actions: providerMock, + } + + // when + actual, err := sut.InstallChart(testCtx, spec) + + // then + require.Error(t, err) + assert.ErrorContains(t, err, "failed to get values for release \"test-release\"") + assert.Nil(t, actual) + }) + t.Run("should fail to install release", func(t *testing.T) { + // given + spec := &ChartSpec{ + ChartName: "test-chart", + ReleaseName: "test-release", + } + installAction := &action.Install{} + envSettings := &cli.EnvSettings{ + RepositoryConfig: defaultRepositoryConfigPath, + RepositoryCache: defaultCachePath, + } + + installMock := newMockInstallAction(t) + installMock.EXPECT().raw().Return(installAction) + installMock.EXPECT().install(testCtx, mock.Anything, mock.Anything).Return(nil, assert.AnError) + locateMock := newMockLocateChartAction(t) + locateMock.EXPECT().locateChart("test-chart", ">0.0.0-0", envSettings).Return("testdata/test-chart", nil) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newInstall().Return(installMock) + providerMock.EXPECT().newLocateChart().Return(locateMock) + + sut := &HelmClient{ + Settings: envSettings, + actions: providerMock, + } + + // when + actual, err := sut.InstallChart(testCtx, spec) + + // then + require.Error(t, err) + assert.ErrorIs(t, err, assert.AnError) + assert.ErrorContains(t, err, "failed to install release \"test-release\"") + assert.Nil(t, actual) + }) + t.Run("should succeed to install release", func(t *testing.T) { + // given + spec := &ChartSpec{ + ChartName: "test-chart", + ReleaseName: "test-release", + } + installAction := &action.Install{} + envSettings := &cli.EnvSettings{ + RepositoryConfig: defaultRepositoryConfigPath, + RepositoryCache: defaultCachePath, + } + expectedRelease := &release.Release{ + Name: "test-release", + Chart: &chart.Chart{Metadata: &chart.Metadata{ + Name: "test-chart", + Version: "1.0.0", + }}, + } + + installMock := newMockInstallAction(t) + installMock.EXPECT().raw().Return(installAction) + installMock.EXPECT().install(testCtx, mock.Anything, mock.Anything).Return(expectedRelease, nil) + locateMock := newMockLocateChartAction(t) + locateMock.EXPECT().locateChart("test-chart", ">0.0.0-0", envSettings).Return("testdata/test-chart", nil) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newInstall().Return(installMock) + providerMock.EXPECT().newLocateChart().Return(locateMock) + + sut := &HelmClient{ + Settings: envSettings, + actions: providerMock, + DebugLog: func(format string, v ...interface{}) { + t.Helper() + assert.Equal(t, "release installed successfully: %s/%s-%s", format) + }, + } + + // when + actual, err := sut.InstallChart(testCtx, spec) + + // then + require.NoError(t, err) + assert.Same(t, expectedRelease, actual) + }) +} + +func TestHelmClient_UpgradeChart(t *testing.T) { + t.Run("should fail to get chart", func(t *testing.T) { + // given + spec := &ChartSpec{ + ChartName: "test-chart", + ReleaseName: "test-release", + } + upgradeAction := &action.Upgrade{} + + upgradeMock := newMockUpgradeAction(t) + upgradeMock.EXPECT().raw().Return(upgradeAction) + locateMock := newMockLocateChartAction(t) + locateMock.EXPECT().locateChart("test-chart", ">0.0.0-0", (*cli.EnvSettings)(nil)).Return("", assert.AnError) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newUpgrade().Return(upgradeMock) + providerMock.EXPECT().newLocateChart().Return(locateMock) + + sut := &HelmClient{ + actions: providerMock, + } + + // when + actual, err := sut.UpgradeChart(testCtx, spec) + + // then + require.Error(t, err) + assert.ErrorIs(t, err, assert.AnError) + assert.ErrorContains(t, err, "failed to get chart for release \"test-release\"") + + assert.Nil(t, actual) + }) + t.Run("should fail to get values for release", func(t *testing.T) { + // given + spec := &ChartSpec{ + ChartName: "test-chart", + ReleaseName: "test-release", + ValuesYaml: "invalid YAML", + } + upgradeAction := &action.Upgrade{} + envSettings := &cli.EnvSettings{ + RepositoryConfig: defaultRepositoryConfigPath, + RepositoryCache: defaultCachePath, + } + + upgradeMock := newMockUpgradeAction(t) + upgradeMock.EXPECT().raw().Return(upgradeAction) + locateMock := newMockLocateChartAction(t) + locateMock.EXPECT().locateChart("test-chart", ">0.0.0-0", envSettings).Return("testdata/test-chart", nil) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newUpgrade().Return(upgradeMock) + providerMock.EXPECT().newLocateChart().Return(locateMock) + + sut := &HelmClient{ + Settings: envSettings, + actions: providerMock, + } + + // when + actual, err := sut.UpgradeChart(testCtx, spec) + + // then + require.Error(t, err) + assert.ErrorContains(t, err, "failed to get values for release \"test-release\"") + + assert.Nil(t, actual) + }) + t.Run("should fail to upgrade and fail to rollback", func(t *testing.T) { + // given + spec := &ChartSpec{ + ChartName: "test-chart", + ReleaseName: "test-release", + } + upgradeAction := &action.Upgrade{} + envSettings := &cli.EnvSettings{ + RepositoryConfig: defaultRepositoryConfigPath, + RepositoryCache: defaultCachePath, + } + + upgradeMock := newMockUpgradeAction(t) + upgradeMock.EXPECT().raw().Return(upgradeAction) + upgradeMock.EXPECT().upgrade(testCtx, "test-release", mock.Anything, mock.Anything).Return(nil, assert.AnError) + locateMock := newMockLocateChartAction(t) + locateMock.EXPECT().locateChart("test-chart", ">0.0.0-0", envSettings).Return("testdata/test-chart", nil) + rollbackMock := newMockRollbackReleaseAction(t) + rollbackMock.EXPECT().raw().Return(&action.Rollback{}) + rollbackMock.EXPECT().rollbackRelease("test-release").Return(assert.AnError) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newUpgrade().Return(upgradeMock) + providerMock.EXPECT().newLocateChart().Return(locateMock) + providerMock.EXPECT().newRollbackRelease().Return(rollbackMock) + + sut := &HelmClient{ + Settings: envSettings, + actions: providerMock, + DebugLog: func(format string, v ...interface{}) { + t.Helper() + assert.Equal(t, "release upgrade failed: %s", format) + }, + } + + // when + actual, err := sut.UpgradeChart(testCtx, spec) + + // then + require.Error(t, err) + assert.ErrorIs(t, err, assert.AnError) + assert.ErrorContains(t, err, "release failed, rollback failed") + assert.ErrorContains(t, err, "failed to upgrade release \"test-release\"") + assert.ErrorContains(t, err, "failed to rollback release \"test-release\"") + + assert.Nil(t, actual) + }) + t.Run("should fail to upgrade and succeed to rollback", func(t *testing.T) { + // given + spec := &ChartSpec{ + ChartName: "test-chart", + ReleaseName: "test-release", + } + upgradeAction := &action.Upgrade{} + envSettings := &cli.EnvSettings{ + RepositoryConfig: defaultRepositoryConfigPath, + RepositoryCache: defaultCachePath, + } + + upgradeMock := newMockUpgradeAction(t) + upgradeMock.EXPECT().raw().Return(upgradeAction) + upgradeMock.EXPECT().upgrade(testCtx, "test-release", mock.Anything, mock.Anything).Return(nil, assert.AnError) + locateMock := newMockLocateChartAction(t) + locateMock.EXPECT().locateChart("test-chart", ">0.0.0-0", envSettings).Return("testdata/test-chart", nil) + rollbackMock := newMockRollbackReleaseAction(t) + rollbackMock.EXPECT().raw().Return(&action.Rollback{}) + rollbackMock.EXPECT().rollbackRelease("test-release").Return(nil) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newUpgrade().Return(upgradeMock) + providerMock.EXPECT().newLocateChart().Return(locateMock) + providerMock.EXPECT().newRollbackRelease().Return(rollbackMock) + + sut := &HelmClient{ + Settings: envSettings, + actions: providerMock, + DebugLog: func(format string, v ...interface{}) { + t.Helper() + assert.Equal(t, "release upgrade failed: %s", format) + }, + } + + // when + actual, err := sut.UpgradeChart(testCtx, spec) + + // then + require.Error(t, err) + assert.ErrorIs(t, err, assert.AnError) + assert.ErrorContains(t, err, "release failed, rollback succeeded") + assert.ErrorContains(t, err, "failed to upgrade release \"test-release\"") + + assert.Nil(t, actual) + }) + t.Run("should succeed to upgrade", func(t *testing.T) { + // given + spec := &ChartSpec{ + ChartName: "test-chart", + ReleaseName: "test-release", + } + upgradeAction := &action.Upgrade{} + envSettings := &cli.EnvSettings{ + RepositoryConfig: defaultRepositoryConfigPath, + RepositoryCache: defaultCachePath, + } + expectedRelease := &release.Release{ + Name: "test-release", + Chart: &chart.Chart{Metadata: &chart.Metadata{ + Name: "test-chart", + Version: "1.0.0", + }}, + } + + upgradeMock := newMockUpgradeAction(t) + upgradeMock.EXPECT().raw().Return(upgradeAction) + upgradeMock.EXPECT().upgrade(testCtx, "test-release", mock.Anything, mock.Anything).Return(expectedRelease, nil) + locateMock := newMockLocateChartAction(t) + locateMock.EXPECT().locateChart("test-chart", ">0.0.0-0", envSettings).Return("testdata/test-chart", nil) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newUpgrade().Return(upgradeMock) + providerMock.EXPECT().newLocateChart().Return(locateMock) + + sut := &HelmClient{ + Settings: envSettings, + actions: providerMock, + DebugLog: func(format string, v ...interface{}) { + t.Helper() + assert.Equal(t, "release upgraded successfully: %s/%s-%s", format) + }, + } + + // when + actual, err := sut.UpgradeChart(testCtx, spec) + + // then + require.NoError(t, err) + assert.Same(t, expectedRelease, actual) + }) +} + +func TestHelmClient_InstallOrUpgradeChart(t *testing.T) { + t.Run("should fail to check if chart exists", func(t *testing.T) { + // given + spec := &ChartSpec{ + ChartName: "test-chart", + ReleaseName: "test-release", + } + + listMock := newMockListReleasesAction(t) + listMock.EXPECT().raw().Return(&action.List{}) + listMock.EXPECT().listReleases().Return(nil, assert.AnError) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newListReleases().Return(listMock) + + sut := &HelmClient{ + actions: providerMock, + } + + // when + actual, err := sut.InstallOrUpgradeChart(testCtx, spec) + + // then + require.Error(t, err) + assert.ErrorIs(t, err, assert.AnError) + assert.ErrorContains(t, err, "failed to list releases") + assert.ErrorContains(t, err, "could not check if release \"test-release\" is already installed") + assert.Nil(t, actual) + }) + t.Run("should find release to upgrade", func(t *testing.T) { + // given + spec := &ChartSpec{ + ChartName: "test-chart", + ReleaseName: "test-release", + Namespace: "test-namespace", + } + upgradeAction := &action.Upgrade{} + envSettings := &cli.EnvSettings{ + RepositoryConfig: defaultRepositoryConfigPath, + RepositoryCache: defaultCachePath, + } + releaseToUpgrade := &release.Release{ + Name: "test-release", + Namespace: "test-namespace", + } + expectedRelease := &release.Release{ + Name: "test-release", + Chart: &chart.Chart{Metadata: &chart.Metadata{ + Name: "test-chart", + Version: "1.1.0", + }}, + } + + upgradeMock := newMockUpgradeAction(t) + upgradeMock.EXPECT().raw().Return(upgradeAction) + upgradeMock.EXPECT().upgrade(testCtx, "test-release", mock.Anything, mock.Anything).Return(expectedRelease, nil) + locateMock := newMockLocateChartAction(t) + locateMock.EXPECT().locateChart("test-chart", ">0.0.0-0", envSettings).Return("testdata/test-chart", nil) + listMock := newMockListReleasesAction(t) + listMock.EXPECT().raw().Return(&action.List{}) + listMock.EXPECT().listReleases().Return([]*release.Release{releaseToUpgrade}, nil) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newListReleases().Return(listMock) + providerMock.EXPECT().newUpgrade().Return(upgradeMock) + providerMock.EXPECT().newLocateChart().Return(locateMock) + + sut := &HelmClient{ + Settings: envSettings, + actions: providerMock, + DebugLog: func(format string, v ...interface{}) { + t.Helper() + assert.Equal(t, "release upgraded successfully: %s/%s-%s", format) + }, + } + + // when + actual, err := sut.InstallOrUpgradeChart(testCtx, spec) + + // then + require.NoError(t, err) + assert.Same(t, expectedRelease, actual) + }) + t.Run("should install if no release found", func(t *testing.T) { + // given + spec := &ChartSpec{ + ChartName: "test-chart", + ReleaseName: "test-release", + } + installAction := &action.Install{} + envSettings := &cli.EnvSettings{ + RepositoryConfig: defaultRepositoryConfigPath, + RepositoryCache: defaultCachePath, + } + expectedRelease := &release.Release{ + Name: "test-release", + Chart: &chart.Chart{Metadata: &chart.Metadata{ + Name: "test-chart", + Version: "1.0.0", + }}, + } + + installMock := newMockInstallAction(t) + installMock.EXPECT().raw().Return(installAction) + installMock.EXPECT().install(testCtx, mock.Anything, mock.Anything).Return(expectedRelease, nil) + locateMock := newMockLocateChartAction(t) + locateMock.EXPECT().locateChart("test-chart", ">0.0.0-0", envSettings).Return("testdata/test-chart", nil) + listMock := newMockListReleasesAction(t) + listMock.EXPECT().raw().Return(&action.List{}) + listMock.EXPECT().listReleases().Return([]*release.Release{}, nil) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newListReleases().Return(listMock) + providerMock.EXPECT().newInstall().Return(installMock) + providerMock.EXPECT().newLocateChart().Return(locateMock) + + sut := &HelmClient{ + Settings: envSettings, + actions: providerMock, + DebugLog: func(format string, v ...interface{}) { + t.Helper() + assert.Equal(t, "release installed successfully: %s/%s-%s", format) + }, + } + + // when + actual, err := sut.InstallOrUpgradeChart(testCtx, spec) + + // then + require.NoError(t, err) + assert.Same(t, expectedRelease, actual) + }) +} + +func TestHelmClient_GetChart(t *testing.T) { + t.Run("should fail to locate chart", func(t *testing.T) { + // given + spec := &ChartSpec{ + ChartName: "test-chart", + ReleaseName: "test-release", + } + + locateMock := newMockLocateChartAction(t) + locateMock.EXPECT().locateChart("test-chart", ">0.0.0-0", (*cli.EnvSettings)(nil)).Return("", assert.AnError) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newLocateChart().Return(locateMock) + + sut := &HelmClient{ + actions: providerMock, + } + + // when + actualChart, chartPath, err := sut.GetChart(spec) + + // then + require.Error(t, err) + assert.ErrorIs(t, err, assert.AnError) + assert.ErrorContains(t, err, "failed to locate chart \"test-chart\" with version \">0.0.0-0\"") + assert.Nil(t, actualChart) + assert.Empty(t, chartPath) + }) + t.Run("should fail to load chart", func(t *testing.T) { + // given + spec := &ChartSpec{ + ChartName: "test-chart", + ReleaseName: "test-release", + } + + locateMock := newMockLocateChartAction(t) + locateMock.EXPECT().locateChart("test-chart", ">0.0.0-0", (*cli.EnvSettings)(nil)).Return("invalid-path", nil) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newLocateChart().Return(locateMock) + + sut := &HelmClient{ + actions: providerMock, + } + + // when + actualChart, chartPath, err := sut.GetChart(spec) + + // then + require.Error(t, err) + assert.ErrorContains(t, err, "failed to load chart \"test-chart\" with version \">0.0.0-0\" from path \"invalid-path\"") + assert.Nil(t, actualChart) + assert.Empty(t, chartPath) + }) + t.Run("should succeed to get chart with deprecation warning", func(t *testing.T) { + // given + spec := &ChartSpec{ + ChartName: "test-chart", + ReleaseName: "test-release", + } + + locateMock := newMockLocateChartAction(t) + locateMock.EXPECT().locateChart("test-chart", ">0.0.0-0", (*cli.EnvSettings)(nil)).Return("testdata/deprecated-chart", nil) + providerMock := newMockActionProvider(t) + providerMock.EXPECT().newLocateChart().Return(locateMock) + + sut := &HelmClient{ + actions: providerMock, + DebugLog: func(format string, v ...interface{}) { + t.Helper() + assert.Equal(t, "WARNING: This chart (%q) is deprecated", format) + }, + } + + // when + actualChart, chartPath, err := sut.GetChart(spec) + + // then + require.NoError(t, err) + assert.NotEmpty(t, actualChart) + assert.Equal(t, "testdata/deprecated-chart", chartPath) + }) +} diff --git a/pkg/helm/client/example_test.go b/pkg/helm/client/example_test.go new file mode 100644 index 0000000..5ff04fa --- /dev/null +++ b/pkg/helm/client/example_test.go @@ -0,0 +1,141 @@ +package client + +import ( + "context" + + "k8s.io/client-go/rest" +) + +var helmClient *HelmClient + +func ExampleNewClientFromRestConf() { + opt := &RestConfClientOptions{ + Options: &Options{ + Namespace: "default", // Change this to the namespace you wish the client to operate in. + RepositoryCache: "/tmp/.helmcache", + RepositoryConfig: "/tmp/.helmrepo", + Debug: true, + DebugLog: func(format string, v ...interface{}) { + // Change this to your own logger. Default is 'log.Printf(format, v...)'. + }, + }, + RestConfig: &rest.Config{}, + } + + helmClient, err := NewClientFromRestConf(opt) + if err != nil { + panic(err) + } + _ = helmClient +} + +func ExampleHelmClient_InstallOrUpgradeChart() { + // Define the chart to be installed + chartSpec := ChartSpec{ + ReleaseName: "etcd-operator", + ChartName: "stable/etcd-operator", + Namespace: "default", + } + + // Install a chart release. + // Note that helmclient.Options.Namespace should ideally match the namespace in chartSpec.Namespace. + if _, err := helmClient.InstallOrUpgradeChart(context.Background(), &chartSpec); err != nil { + panic(err) + } +} + +func ExampleHelmClient_InstallOrUpgradeChart_useChartDirectory() { + // Use an unpacked chart directory. + chartSpec := ChartSpec{ + ReleaseName: "etcd-operator", + ChartName: "/path/to/stable/etcd-operator", + Namespace: "default", + } + + if _, err := helmClient.InstallOrUpgradeChart(context.Background(), &chartSpec); err != nil { + panic(err) + } +} + +func ExampleHelmClient_InstallOrUpgradeChart_useLocalChartArchive() { + // Use an archived chart directory. + chartSpec := ChartSpec{ + ReleaseName: "etcd-operator", + ChartName: "/path/to/stable/etcd-operator.tar.gz", + Namespace: "default", + } + + if _, err := helmClient.InstallOrUpgradeChart(context.Background(), &chartSpec); err != nil { + panic(err) + } +} + +func ExampleHelmClient_InstallOrUpgradeChart_useURL() { + // Use an archived chart directory via URL. + chartSpec := ChartSpec{ + ReleaseName: "etcd-operator", + ChartName: "http://helm.whatever.com/repo/etcd-operator.tar.gz", + Namespace: "default", + } + + if _, err := helmClient.InstallOrUpgradeChart(context.Background(), &chartSpec); err != nil { + panic(err) + } +} + +func ExampleHelmClient_UninstallRelease() { + // Define the released chart to be installed. + chartSpec := ChartSpec{ + ReleaseName: "etcd-operator", + ChartName: "stable/etcd-operator", + Namespace: "default", + } + + // Uninstall the chart release. + // Note that helmclient.Options.Namespace should ideally match the namespace in chartSpec.Namespace. + if err := helmClient.UninstallRelease(&chartSpec); err != nil { + panic(err) + } +} + +func ExampleHelmClient_UninstallReleaseByName() { + // Uninstall a release by name. + if err := helmClient.UninstallReleaseByName("etcd-operator"); err != nil { + panic(err) + } +} + +func ExampleHelmClient_ListDeployedReleases() { + // List all deployed releases. + if _, err := helmClient.ListDeployedReleases(); err != nil { + panic(err) + } +} + +func ExampleHelmClient_GetReleaseValues() { + // Get the values of a deployed release. + if _, err := helmClient.GetReleaseValues("etcd-operator", true); err != nil { + panic(err) + } +} + +func ExampleHelmClient_GetRelease() { + // Get specific details of a deployed release. + if _, err := helmClient.GetRelease("etcd-operator"); err != nil { + panic(err) + } +} + +func ExampleHelmClient_RollbackRelease() { + // Define the released chart to be installed + chartSpec := ChartSpec{ + ReleaseName: "etcd-operator", + ChartName: "stable/etcd-operator", + Namespace: "default", + } + + // Rollback to the previous version of the release. + if err := helmClient.RollbackRelease(&chartSpec); err != nil { + return + } +} diff --git a/pkg/helm/client/interface.go b/pkg/helm/client/interface.go new file mode 100644 index 0000000..662a301 --- /dev/null +++ b/pkg/helm/client/interface.go @@ -0,0 +1,86 @@ +package client + +import ( + "context" + + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/release" +) + +// Client holds the method signatures for a Helm client. +// NOTE: This is an interface to allow for mocking in tests. +type Client interface { + InstallOrUpgradeChart(ctx context.Context, spec *ChartSpec) (*release.Release, error) + InstallChart(ctx context.Context, spec *ChartSpec) (*release.Release, error) + UpgradeChart(ctx context.Context, spec *ChartSpec) (*release.Release, error) + ListDeployedReleases() ([]*release.Release, error) + ListReleasesByStateMask(action.ListStates) ([]*release.Release, error) + GetRelease(name string) (*release.Release, error) + // RollBack is an interface to abstract a rollback action. + RollBack + GetReleaseValues(name string, allValues bool) (map[string]interface{}, error) + UninstallRelease(spec *ChartSpec) error + UninstallReleaseByName(name string) error + GetChart(spec *ChartSpec) (*chart.Chart, string, error) + TagResolver +} + +type TagResolver interface { + Tags(ref string) ([]string, error) +} + +type RollBack interface { + RollbackRelease(spec *ChartSpec) error +} + +type actionProvider interface { + newInstall() installAction + newUpgrade() upgradeAction + newLocateChart() locateChartAction + newUninstall() uninstallAction + newListReleases() listReleasesAction + newGetReleaseValues() getReleaseValuesAction + newGetRelease() getReleaseAction + newRollbackRelease() rollbackReleaseAction +} + +type installAction interface { + install(ctx context.Context, chart *chart.Chart, values map[string]interface{}) (*release.Release, error) + raw() *action.Install +} + +type upgradeAction interface { + upgrade(ctx context.Context, releaseName string, chart *chart.Chart, values map[string]interface{}) (*release.Release, error) + raw() *action.Upgrade +} + +type locateChartAction interface { + locateChart(name, version string, settings *cli.EnvSettings) (chartPath string, err error) +} + +type uninstallAction interface { + uninstall(releaseName string) (*release.UninstallReleaseResponse, error) + raw() *action.Uninstall +} + +type listReleasesAction interface { + listReleases() ([]*release.Release, error) + raw() *action.List +} + +type getReleaseValuesAction interface { + getReleaseValues(releaseName string) (map[string]interface{}, error) + raw() *action.GetValues +} + +type getReleaseAction interface { + getRelease(releaseName string) (*release.Release, error) + raw() *action.Get +} + +type rollbackReleaseAction interface { + rollbackRelease(releaseName string) error + raw() *action.Rollback +} diff --git a/pkg/helm/client/mock_actionProvider_test.go b/pkg/helm/client/mock_actionProvider_test.go new file mode 100644 index 0000000..0e616ec --- /dev/null +++ b/pkg/helm/client/mock_actionProvider_test.go @@ -0,0 +1,377 @@ +// Code generated by mockery v2.20.0. DO NOT EDIT. + +package client + +import mock "github.com/stretchr/testify/mock" + +// mockActionProvider is an autogenerated mock type for the actionProvider type +type mockActionProvider struct { + mock.Mock +} + +type mockActionProvider_Expecter struct { + mock *mock.Mock +} + +func (_m *mockActionProvider) EXPECT() *mockActionProvider_Expecter { + return &mockActionProvider_Expecter{mock: &_m.Mock} +} + +// newGetRelease provides a mock function with given fields: +func (_m *mockActionProvider) newGetRelease() getReleaseAction { + ret := _m.Called() + + var r0 getReleaseAction + if rf, ok := ret.Get(0).(func() getReleaseAction); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(getReleaseAction) + } + } + + return r0 +} + +// mockActionProvider_newGetRelease_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'newGetRelease' +type mockActionProvider_newGetRelease_Call struct { + *mock.Call +} + +// newGetRelease is a helper method to define mock.On call +func (_e *mockActionProvider_Expecter) newGetRelease() *mockActionProvider_newGetRelease_Call { + return &mockActionProvider_newGetRelease_Call{Call: _e.mock.On("newGetRelease")} +} + +func (_c *mockActionProvider_newGetRelease_Call) Run(run func()) *mockActionProvider_newGetRelease_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockActionProvider_newGetRelease_Call) Return(_a0 getReleaseAction) *mockActionProvider_newGetRelease_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockActionProvider_newGetRelease_Call) RunAndReturn(run func() getReleaseAction) *mockActionProvider_newGetRelease_Call { + _c.Call.Return(run) + return _c +} + +// newGetReleaseValues provides a mock function with given fields: +func (_m *mockActionProvider) newGetReleaseValues() getReleaseValuesAction { + ret := _m.Called() + + var r0 getReleaseValuesAction + if rf, ok := ret.Get(0).(func() getReleaseValuesAction); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(getReleaseValuesAction) + } + } + + return r0 +} + +// mockActionProvider_newGetReleaseValues_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'newGetReleaseValues' +type mockActionProvider_newGetReleaseValues_Call struct { + *mock.Call +} + +// newGetReleaseValues is a helper method to define mock.On call +func (_e *mockActionProvider_Expecter) newGetReleaseValues() *mockActionProvider_newGetReleaseValues_Call { + return &mockActionProvider_newGetReleaseValues_Call{Call: _e.mock.On("newGetReleaseValues")} +} + +func (_c *mockActionProvider_newGetReleaseValues_Call) Run(run func()) *mockActionProvider_newGetReleaseValues_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockActionProvider_newGetReleaseValues_Call) Return(_a0 getReleaseValuesAction) *mockActionProvider_newGetReleaseValues_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockActionProvider_newGetReleaseValues_Call) RunAndReturn(run func() getReleaseValuesAction) *mockActionProvider_newGetReleaseValues_Call { + _c.Call.Return(run) + return _c +} + +// newInstall provides a mock function with given fields: +func (_m *mockActionProvider) newInstall() installAction { + ret := _m.Called() + + var r0 installAction + if rf, ok := ret.Get(0).(func() installAction); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(installAction) + } + } + + return r0 +} + +// mockActionProvider_newInstall_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'newInstall' +type mockActionProvider_newInstall_Call struct { + *mock.Call +} + +// newInstall is a helper method to define mock.On call +func (_e *mockActionProvider_Expecter) newInstall() *mockActionProvider_newInstall_Call { + return &mockActionProvider_newInstall_Call{Call: _e.mock.On("newInstall")} +} + +func (_c *mockActionProvider_newInstall_Call) Run(run func()) *mockActionProvider_newInstall_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockActionProvider_newInstall_Call) Return(_a0 installAction) *mockActionProvider_newInstall_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockActionProvider_newInstall_Call) RunAndReturn(run func() installAction) *mockActionProvider_newInstall_Call { + _c.Call.Return(run) + return _c +} + +// newListReleases provides a mock function with given fields: +func (_m *mockActionProvider) newListReleases() listReleasesAction { + ret := _m.Called() + + var r0 listReleasesAction + if rf, ok := ret.Get(0).(func() listReleasesAction); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(listReleasesAction) + } + } + + return r0 +} + +// mockActionProvider_newListReleases_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'newListReleases' +type mockActionProvider_newListReleases_Call struct { + *mock.Call +} + +// newListReleases is a helper method to define mock.On call +func (_e *mockActionProvider_Expecter) newListReleases() *mockActionProvider_newListReleases_Call { + return &mockActionProvider_newListReleases_Call{Call: _e.mock.On("newListReleases")} +} + +func (_c *mockActionProvider_newListReleases_Call) Run(run func()) *mockActionProvider_newListReleases_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockActionProvider_newListReleases_Call) Return(_a0 listReleasesAction) *mockActionProvider_newListReleases_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockActionProvider_newListReleases_Call) RunAndReturn(run func() listReleasesAction) *mockActionProvider_newListReleases_Call { + _c.Call.Return(run) + return _c +} + +// newLocateChart provides a mock function with given fields: +func (_m *mockActionProvider) newLocateChart() locateChartAction { + ret := _m.Called() + + var r0 locateChartAction + if rf, ok := ret.Get(0).(func() locateChartAction); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(locateChartAction) + } + } + + return r0 +} + +// mockActionProvider_newLocateChart_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'newLocateChart' +type mockActionProvider_newLocateChart_Call struct { + *mock.Call +} + +// newLocateChart is a helper method to define mock.On call +func (_e *mockActionProvider_Expecter) newLocateChart() *mockActionProvider_newLocateChart_Call { + return &mockActionProvider_newLocateChart_Call{Call: _e.mock.On("newLocateChart")} +} + +func (_c *mockActionProvider_newLocateChart_Call) Run(run func()) *mockActionProvider_newLocateChart_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockActionProvider_newLocateChart_Call) Return(_a0 locateChartAction) *mockActionProvider_newLocateChart_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockActionProvider_newLocateChart_Call) RunAndReturn(run func() locateChartAction) *mockActionProvider_newLocateChart_Call { + _c.Call.Return(run) + return _c +} + +// newRollbackRelease provides a mock function with given fields: +func (_m *mockActionProvider) newRollbackRelease() rollbackReleaseAction { + ret := _m.Called() + + var r0 rollbackReleaseAction + if rf, ok := ret.Get(0).(func() rollbackReleaseAction); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(rollbackReleaseAction) + } + } + + return r0 +} + +// mockActionProvider_newRollbackRelease_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'newRollbackRelease' +type mockActionProvider_newRollbackRelease_Call struct { + *mock.Call +} + +// newRollbackRelease is a helper method to define mock.On call +func (_e *mockActionProvider_Expecter) newRollbackRelease() *mockActionProvider_newRollbackRelease_Call { + return &mockActionProvider_newRollbackRelease_Call{Call: _e.mock.On("newRollbackRelease")} +} + +func (_c *mockActionProvider_newRollbackRelease_Call) Run(run func()) *mockActionProvider_newRollbackRelease_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockActionProvider_newRollbackRelease_Call) Return(_a0 rollbackReleaseAction) *mockActionProvider_newRollbackRelease_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockActionProvider_newRollbackRelease_Call) RunAndReturn(run func() rollbackReleaseAction) *mockActionProvider_newRollbackRelease_Call { + _c.Call.Return(run) + return _c +} + +// newUninstall provides a mock function with given fields: +func (_m *mockActionProvider) newUninstall() uninstallAction { + ret := _m.Called() + + var r0 uninstallAction + if rf, ok := ret.Get(0).(func() uninstallAction); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(uninstallAction) + } + } + + return r0 +} + +// mockActionProvider_newUninstall_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'newUninstall' +type mockActionProvider_newUninstall_Call struct { + *mock.Call +} + +// newUninstall is a helper method to define mock.On call +func (_e *mockActionProvider_Expecter) newUninstall() *mockActionProvider_newUninstall_Call { + return &mockActionProvider_newUninstall_Call{Call: _e.mock.On("newUninstall")} +} + +func (_c *mockActionProvider_newUninstall_Call) Run(run func()) *mockActionProvider_newUninstall_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockActionProvider_newUninstall_Call) Return(_a0 uninstallAction) *mockActionProvider_newUninstall_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockActionProvider_newUninstall_Call) RunAndReturn(run func() uninstallAction) *mockActionProvider_newUninstall_Call { + _c.Call.Return(run) + return _c +} + +// newUpgrade provides a mock function with given fields: +func (_m *mockActionProvider) newUpgrade() upgradeAction { + ret := _m.Called() + + var r0 upgradeAction + if rf, ok := ret.Get(0).(func() upgradeAction); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(upgradeAction) + } + } + + return r0 +} + +// mockActionProvider_newUpgrade_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'newUpgrade' +type mockActionProvider_newUpgrade_Call struct { + *mock.Call +} + +// newUpgrade is a helper method to define mock.On call +func (_e *mockActionProvider_Expecter) newUpgrade() *mockActionProvider_newUpgrade_Call { + return &mockActionProvider_newUpgrade_Call{Call: _e.mock.On("newUpgrade")} +} + +func (_c *mockActionProvider_newUpgrade_Call) Run(run func()) *mockActionProvider_newUpgrade_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockActionProvider_newUpgrade_Call) Return(_a0 upgradeAction) *mockActionProvider_newUpgrade_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockActionProvider_newUpgrade_Call) RunAndReturn(run func() upgradeAction) *mockActionProvider_newUpgrade_Call { + _c.Call.Return(run) + return _c +} + +type mockConstructorTestingTnewMockActionProvider interface { + mock.TestingT + Cleanup(func()) +} + +// newMockActionProvider creates a new instance of mockActionProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func newMockActionProvider(t mockConstructorTestingTnewMockActionProvider) *mockActionProvider { + mock := &mockActionProvider{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/helm/client/mock_getReleaseAction_test.go b/pkg/helm/client/mock_getReleaseAction_test.go new file mode 100644 index 0000000..55b8ed5 --- /dev/null +++ b/pkg/helm/client/mock_getReleaseAction_test.go @@ -0,0 +1,135 @@ +// Code generated by mockery v2.20.0. DO NOT EDIT. + +package client + +import ( + mock "github.com/stretchr/testify/mock" + action "helm.sh/helm/v3/pkg/action" + + release "helm.sh/helm/v3/pkg/release" +) + +// mockGetReleaseAction is an autogenerated mock type for the getReleaseAction type +type mockGetReleaseAction struct { + mock.Mock +} + +type mockGetReleaseAction_Expecter struct { + mock *mock.Mock +} + +func (_m *mockGetReleaseAction) EXPECT() *mockGetReleaseAction_Expecter { + return &mockGetReleaseAction_Expecter{mock: &_m.Mock} +} + +// getRelease provides a mock function with given fields: releaseName +func (_m *mockGetReleaseAction) getRelease(releaseName string) (*release.Release, error) { + ret := _m.Called(releaseName) + + var r0 *release.Release + var r1 error + if rf, ok := ret.Get(0).(func(string) (*release.Release, error)); ok { + return rf(releaseName) + } + if rf, ok := ret.Get(0).(func(string) *release.Release); ok { + r0 = rf(releaseName) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*release.Release) + } + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(releaseName) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockGetReleaseAction_getRelease_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'getRelease' +type mockGetReleaseAction_getRelease_Call struct { + *mock.Call +} + +// getRelease is a helper method to define mock.On call +// - releaseName string +func (_e *mockGetReleaseAction_Expecter) getRelease(releaseName interface{}) *mockGetReleaseAction_getRelease_Call { + return &mockGetReleaseAction_getRelease_Call{Call: _e.mock.On("getRelease", releaseName)} +} + +func (_c *mockGetReleaseAction_getRelease_Call) Run(run func(releaseName string)) *mockGetReleaseAction_getRelease_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *mockGetReleaseAction_getRelease_Call) Return(_a0 *release.Release, _a1 error) *mockGetReleaseAction_getRelease_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockGetReleaseAction_getRelease_Call) RunAndReturn(run func(string) (*release.Release, error)) *mockGetReleaseAction_getRelease_Call { + _c.Call.Return(run) + return _c +} + +// raw provides a mock function with given fields: +func (_m *mockGetReleaseAction) raw() *action.Get { + ret := _m.Called() + + var r0 *action.Get + if rf, ok := ret.Get(0).(func() *action.Get); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*action.Get) + } + } + + return r0 +} + +// mockGetReleaseAction_raw_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'raw' +type mockGetReleaseAction_raw_Call struct { + *mock.Call +} + +// raw is a helper method to define mock.On call +func (_e *mockGetReleaseAction_Expecter) raw() *mockGetReleaseAction_raw_Call { + return &mockGetReleaseAction_raw_Call{Call: _e.mock.On("raw")} +} + +func (_c *mockGetReleaseAction_raw_Call) Run(run func()) *mockGetReleaseAction_raw_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockGetReleaseAction_raw_Call) Return(_a0 *action.Get) *mockGetReleaseAction_raw_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockGetReleaseAction_raw_Call) RunAndReturn(run func() *action.Get) *mockGetReleaseAction_raw_Call { + _c.Call.Return(run) + return _c +} + +type mockConstructorTestingTnewMockGetReleaseAction interface { + mock.TestingT + Cleanup(func()) +} + +// newMockGetReleaseAction creates a new instance of mockGetReleaseAction. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func newMockGetReleaseAction(t mockConstructorTestingTnewMockGetReleaseAction) *mockGetReleaseAction { + mock := &mockGetReleaseAction{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/helm/client/mock_getReleaseValuesAction_test.go b/pkg/helm/client/mock_getReleaseValuesAction_test.go new file mode 100644 index 0000000..a301d5d --- /dev/null +++ b/pkg/helm/client/mock_getReleaseValuesAction_test.go @@ -0,0 +1,133 @@ +// Code generated by mockery v2.20.0. DO NOT EDIT. + +package client + +import ( + mock "github.com/stretchr/testify/mock" + action "helm.sh/helm/v3/pkg/action" +) + +// mockGetReleaseValuesAction is an autogenerated mock type for the getReleaseValuesAction type +type mockGetReleaseValuesAction struct { + mock.Mock +} + +type mockGetReleaseValuesAction_Expecter struct { + mock *mock.Mock +} + +func (_m *mockGetReleaseValuesAction) EXPECT() *mockGetReleaseValuesAction_Expecter { + return &mockGetReleaseValuesAction_Expecter{mock: &_m.Mock} +} + +// getReleaseValues provides a mock function with given fields: releaseName +func (_m *mockGetReleaseValuesAction) getReleaseValues(releaseName string) (map[string]interface{}, error) { + ret := _m.Called(releaseName) + + var r0 map[string]interface{} + var r1 error + if rf, ok := ret.Get(0).(func(string) (map[string]interface{}, error)); ok { + return rf(releaseName) + } + if rf, ok := ret.Get(0).(func(string) map[string]interface{}); ok { + r0 = rf(releaseName) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[string]interface{}) + } + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(releaseName) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockGetReleaseValuesAction_getReleaseValues_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'getReleaseValues' +type mockGetReleaseValuesAction_getReleaseValues_Call struct { + *mock.Call +} + +// getReleaseValues is a helper method to define mock.On call +// - releaseName string +func (_e *mockGetReleaseValuesAction_Expecter) getReleaseValues(releaseName interface{}) *mockGetReleaseValuesAction_getReleaseValues_Call { + return &mockGetReleaseValuesAction_getReleaseValues_Call{Call: _e.mock.On("getReleaseValues", releaseName)} +} + +func (_c *mockGetReleaseValuesAction_getReleaseValues_Call) Run(run func(releaseName string)) *mockGetReleaseValuesAction_getReleaseValues_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *mockGetReleaseValuesAction_getReleaseValues_Call) Return(_a0 map[string]interface{}, _a1 error) *mockGetReleaseValuesAction_getReleaseValues_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockGetReleaseValuesAction_getReleaseValues_Call) RunAndReturn(run func(string) (map[string]interface{}, error)) *mockGetReleaseValuesAction_getReleaseValues_Call { + _c.Call.Return(run) + return _c +} + +// raw provides a mock function with given fields: +func (_m *mockGetReleaseValuesAction) raw() *action.GetValues { + ret := _m.Called() + + var r0 *action.GetValues + if rf, ok := ret.Get(0).(func() *action.GetValues); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*action.GetValues) + } + } + + return r0 +} + +// mockGetReleaseValuesAction_raw_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'raw' +type mockGetReleaseValuesAction_raw_Call struct { + *mock.Call +} + +// raw is a helper method to define mock.On call +func (_e *mockGetReleaseValuesAction_Expecter) raw() *mockGetReleaseValuesAction_raw_Call { + return &mockGetReleaseValuesAction_raw_Call{Call: _e.mock.On("raw")} +} + +func (_c *mockGetReleaseValuesAction_raw_Call) Run(run func()) *mockGetReleaseValuesAction_raw_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockGetReleaseValuesAction_raw_Call) Return(_a0 *action.GetValues) *mockGetReleaseValuesAction_raw_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockGetReleaseValuesAction_raw_Call) RunAndReturn(run func() *action.GetValues) *mockGetReleaseValuesAction_raw_Call { + _c.Call.Return(run) + return _c +} + +type mockConstructorTestingTnewMockGetReleaseValuesAction interface { + mock.TestingT + Cleanup(func()) +} + +// newMockGetReleaseValuesAction creates a new instance of mockGetReleaseValuesAction. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func newMockGetReleaseValuesAction(t mockConstructorTestingTnewMockGetReleaseValuesAction) *mockGetReleaseValuesAction { + mock := &mockGetReleaseValuesAction{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/helm/client/mock_installAction_test.go b/pkg/helm/client/mock_installAction_test.go new file mode 100644 index 0000000..c27fc96 --- /dev/null +++ b/pkg/helm/client/mock_installAction_test.go @@ -0,0 +1,141 @@ +// Code generated by mockery v2.20.0. DO NOT EDIT. + +package client + +import ( + action "helm.sh/helm/v3/pkg/action" + chart "helm.sh/helm/v3/pkg/chart" + + context "context" + + mock "github.com/stretchr/testify/mock" + + release "helm.sh/helm/v3/pkg/release" +) + +// mockInstallAction is an autogenerated mock type for the installAction type +type mockInstallAction struct { + mock.Mock +} + +type mockInstallAction_Expecter struct { + mock *mock.Mock +} + +func (_m *mockInstallAction) EXPECT() *mockInstallAction_Expecter { + return &mockInstallAction_Expecter{mock: &_m.Mock} +} + +// install provides a mock function with given fields: ctx, _a1, values +func (_m *mockInstallAction) install(ctx context.Context, _a1 *chart.Chart, values map[string]interface{}) (*release.Release, error) { + ret := _m.Called(ctx, _a1, values) + + var r0 *release.Release + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *chart.Chart, map[string]interface{}) (*release.Release, error)); ok { + return rf(ctx, _a1, values) + } + if rf, ok := ret.Get(0).(func(context.Context, *chart.Chart, map[string]interface{}) *release.Release); ok { + r0 = rf(ctx, _a1, values) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*release.Release) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *chart.Chart, map[string]interface{}) error); ok { + r1 = rf(ctx, _a1, values) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockInstallAction_install_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'install' +type mockInstallAction_install_Call struct { + *mock.Call +} + +// install is a helper method to define mock.On call +// - ctx context.Context +// - _a1 *chart.Chart +// - values map[string]interface{} +func (_e *mockInstallAction_Expecter) install(ctx interface{}, _a1 interface{}, values interface{}) *mockInstallAction_install_Call { + return &mockInstallAction_install_Call{Call: _e.mock.On("install", ctx, _a1, values)} +} + +func (_c *mockInstallAction_install_Call) Run(run func(ctx context.Context, _a1 *chart.Chart, values map[string]interface{})) *mockInstallAction_install_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*chart.Chart), args[2].(map[string]interface{})) + }) + return _c +} + +func (_c *mockInstallAction_install_Call) Return(_a0 *release.Release, _a1 error) *mockInstallAction_install_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockInstallAction_install_Call) RunAndReturn(run func(context.Context, *chart.Chart, map[string]interface{}) (*release.Release, error)) *mockInstallAction_install_Call { + _c.Call.Return(run) + return _c +} + +// raw provides a mock function with given fields: +func (_m *mockInstallAction) raw() *action.Install { + ret := _m.Called() + + var r0 *action.Install + if rf, ok := ret.Get(0).(func() *action.Install); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*action.Install) + } + } + + return r0 +} + +// mockInstallAction_raw_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'raw' +type mockInstallAction_raw_Call struct { + *mock.Call +} + +// raw is a helper method to define mock.On call +func (_e *mockInstallAction_Expecter) raw() *mockInstallAction_raw_Call { + return &mockInstallAction_raw_Call{Call: _e.mock.On("raw")} +} + +func (_c *mockInstallAction_raw_Call) Run(run func()) *mockInstallAction_raw_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockInstallAction_raw_Call) Return(_a0 *action.Install) *mockInstallAction_raw_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockInstallAction_raw_Call) RunAndReturn(run func() *action.Install) *mockInstallAction_raw_Call { + _c.Call.Return(run) + return _c +} + +type mockConstructorTestingTnewMockInstallAction interface { + mock.TestingT + Cleanup(func()) +} + +// newMockInstallAction creates a new instance of mockInstallAction. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func newMockInstallAction(t mockConstructorTestingTnewMockInstallAction) *mockInstallAction { + mock := &mockInstallAction{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/helm/client/mock_listReleasesAction_test.go b/pkg/helm/client/mock_listReleasesAction_test.go new file mode 100644 index 0000000..7f746f7 --- /dev/null +++ b/pkg/helm/client/mock_listReleasesAction_test.go @@ -0,0 +1,134 @@ +// Code generated by mockery v2.20.0. DO NOT EDIT. + +package client + +import ( + mock "github.com/stretchr/testify/mock" + action "helm.sh/helm/v3/pkg/action" + + release "helm.sh/helm/v3/pkg/release" +) + +// mockListReleasesAction is an autogenerated mock type for the listReleasesAction type +type mockListReleasesAction struct { + mock.Mock +} + +type mockListReleasesAction_Expecter struct { + mock *mock.Mock +} + +func (_m *mockListReleasesAction) EXPECT() *mockListReleasesAction_Expecter { + return &mockListReleasesAction_Expecter{mock: &_m.Mock} +} + +// listReleases provides a mock function with given fields: +func (_m *mockListReleasesAction) listReleases() ([]*release.Release, error) { + ret := _m.Called() + + var r0 []*release.Release + var r1 error + if rf, ok := ret.Get(0).(func() ([]*release.Release, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []*release.Release); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*release.Release) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockListReleasesAction_listReleases_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'listReleases' +type mockListReleasesAction_listReleases_Call struct { + *mock.Call +} + +// listReleases is a helper method to define mock.On call +func (_e *mockListReleasesAction_Expecter) listReleases() *mockListReleasesAction_listReleases_Call { + return &mockListReleasesAction_listReleases_Call{Call: _e.mock.On("listReleases")} +} + +func (_c *mockListReleasesAction_listReleases_Call) Run(run func()) *mockListReleasesAction_listReleases_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockListReleasesAction_listReleases_Call) Return(_a0 []*release.Release, _a1 error) *mockListReleasesAction_listReleases_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockListReleasesAction_listReleases_Call) RunAndReturn(run func() ([]*release.Release, error)) *mockListReleasesAction_listReleases_Call { + _c.Call.Return(run) + return _c +} + +// raw provides a mock function with given fields: +func (_m *mockListReleasesAction) raw() *action.List { + ret := _m.Called() + + var r0 *action.List + if rf, ok := ret.Get(0).(func() *action.List); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*action.List) + } + } + + return r0 +} + +// mockListReleasesAction_raw_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'raw' +type mockListReleasesAction_raw_Call struct { + *mock.Call +} + +// raw is a helper method to define mock.On call +func (_e *mockListReleasesAction_Expecter) raw() *mockListReleasesAction_raw_Call { + return &mockListReleasesAction_raw_Call{Call: _e.mock.On("raw")} +} + +func (_c *mockListReleasesAction_raw_Call) Run(run func()) *mockListReleasesAction_raw_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockListReleasesAction_raw_Call) Return(_a0 *action.List) *mockListReleasesAction_raw_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockListReleasesAction_raw_Call) RunAndReturn(run func() *action.List) *mockListReleasesAction_raw_Call { + _c.Call.Return(run) + return _c +} + +type mockConstructorTestingTnewMockListReleasesAction interface { + mock.TestingT + Cleanup(func()) +} + +// newMockListReleasesAction creates a new instance of mockListReleasesAction. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func newMockListReleasesAction(t mockConstructorTestingTnewMockListReleasesAction) *mockListReleasesAction { + mock := &mockListReleasesAction{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/helm/client/mock_locateChartAction_test.go b/pkg/helm/client/mock_locateChartAction_test.go new file mode 100644 index 0000000..6d1e804 --- /dev/null +++ b/pkg/helm/client/mock_locateChartAction_test.go @@ -0,0 +1,90 @@ +// Code generated by mockery v2.20.0. DO NOT EDIT. + +package client + +import ( + mock "github.com/stretchr/testify/mock" + cli "helm.sh/helm/v3/pkg/cli" +) + +// mockLocateChartAction is an autogenerated mock type for the locateChartAction type +type mockLocateChartAction struct { + mock.Mock +} + +type mockLocateChartAction_Expecter struct { + mock *mock.Mock +} + +func (_m *mockLocateChartAction) EXPECT() *mockLocateChartAction_Expecter { + return &mockLocateChartAction_Expecter{mock: &_m.Mock} +} + +// locateChart provides a mock function with given fields: name, version, settings +func (_m *mockLocateChartAction) locateChart(name string, version string, settings *cli.EnvSettings) (string, error) { + ret := _m.Called(name, version, settings) + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(string, string, *cli.EnvSettings) (string, error)); ok { + return rf(name, version, settings) + } + if rf, ok := ret.Get(0).(func(string, string, *cli.EnvSettings) string); ok { + r0 = rf(name, version, settings) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(string, string, *cli.EnvSettings) error); ok { + r1 = rf(name, version, settings) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockLocateChartAction_locateChart_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'locateChart' +type mockLocateChartAction_locateChart_Call struct { + *mock.Call +} + +// locateChart is a helper method to define mock.On call +// - name string +// - version string +// - settings *cli.EnvSettings +func (_e *mockLocateChartAction_Expecter) locateChart(name interface{}, version interface{}, settings interface{}) *mockLocateChartAction_locateChart_Call { + return &mockLocateChartAction_locateChart_Call{Call: _e.mock.On("locateChart", name, version, settings)} +} + +func (_c *mockLocateChartAction_locateChart_Call) Run(run func(name string, version string, settings *cli.EnvSettings)) *mockLocateChartAction_locateChart_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string), args[2].(*cli.EnvSettings)) + }) + return _c +} + +func (_c *mockLocateChartAction_locateChart_Call) Return(chartPath string, err error) *mockLocateChartAction_locateChart_Call { + _c.Call.Return(chartPath, err) + return _c +} + +func (_c *mockLocateChartAction_locateChart_Call) RunAndReturn(run func(string, string, *cli.EnvSettings) (string, error)) *mockLocateChartAction_locateChart_Call { + _c.Call.Return(run) + return _c +} + +type mockConstructorTestingTnewMockLocateChartAction interface { + mock.TestingT + Cleanup(func()) +} + +// newMockLocateChartAction creates a new instance of mockLocateChartAction. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func newMockLocateChartAction(t mockConstructorTestingTnewMockLocateChartAction) *mockLocateChartAction { + mock := &mockLocateChartAction{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/helm/client/mock_rollbackReleaseAction_test.go b/pkg/helm/client/mock_rollbackReleaseAction_test.go new file mode 100644 index 0000000..61345ae --- /dev/null +++ b/pkg/helm/client/mock_rollbackReleaseAction_test.go @@ -0,0 +1,121 @@ +// Code generated by mockery v2.20.0. DO NOT EDIT. + +package client + +import ( + mock "github.com/stretchr/testify/mock" + action "helm.sh/helm/v3/pkg/action" +) + +// mockRollbackReleaseAction is an autogenerated mock type for the rollbackReleaseAction type +type mockRollbackReleaseAction struct { + mock.Mock +} + +type mockRollbackReleaseAction_Expecter struct { + mock *mock.Mock +} + +func (_m *mockRollbackReleaseAction) EXPECT() *mockRollbackReleaseAction_Expecter { + return &mockRollbackReleaseAction_Expecter{mock: &_m.Mock} +} + +// raw provides a mock function with given fields: +func (_m *mockRollbackReleaseAction) raw() *action.Rollback { + ret := _m.Called() + + var r0 *action.Rollback + if rf, ok := ret.Get(0).(func() *action.Rollback); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*action.Rollback) + } + } + + return r0 +} + +// mockRollbackReleaseAction_raw_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'raw' +type mockRollbackReleaseAction_raw_Call struct { + *mock.Call +} + +// raw is a helper method to define mock.On call +func (_e *mockRollbackReleaseAction_Expecter) raw() *mockRollbackReleaseAction_raw_Call { + return &mockRollbackReleaseAction_raw_Call{Call: _e.mock.On("raw")} +} + +func (_c *mockRollbackReleaseAction_raw_Call) Run(run func()) *mockRollbackReleaseAction_raw_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockRollbackReleaseAction_raw_Call) Return(_a0 *action.Rollback) *mockRollbackReleaseAction_raw_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockRollbackReleaseAction_raw_Call) RunAndReturn(run func() *action.Rollback) *mockRollbackReleaseAction_raw_Call { + _c.Call.Return(run) + return _c +} + +// rollbackRelease provides a mock function with given fields: releaseName +func (_m *mockRollbackReleaseAction) rollbackRelease(releaseName string) error { + ret := _m.Called(releaseName) + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(releaseName) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockRollbackReleaseAction_rollbackRelease_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'rollbackRelease' +type mockRollbackReleaseAction_rollbackRelease_Call struct { + *mock.Call +} + +// rollbackRelease is a helper method to define mock.On call +// - releaseName string +func (_e *mockRollbackReleaseAction_Expecter) rollbackRelease(releaseName interface{}) *mockRollbackReleaseAction_rollbackRelease_Call { + return &mockRollbackReleaseAction_rollbackRelease_Call{Call: _e.mock.On("rollbackRelease", releaseName)} +} + +func (_c *mockRollbackReleaseAction_rollbackRelease_Call) Run(run func(releaseName string)) *mockRollbackReleaseAction_rollbackRelease_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *mockRollbackReleaseAction_rollbackRelease_Call) Return(_a0 error) *mockRollbackReleaseAction_rollbackRelease_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockRollbackReleaseAction_rollbackRelease_Call) RunAndReturn(run func(string) error) *mockRollbackReleaseAction_rollbackRelease_Call { + _c.Call.Return(run) + return _c +} + +type mockConstructorTestingTnewMockRollbackReleaseAction interface { + mock.TestingT + Cleanup(func()) +} + +// newMockRollbackReleaseAction creates a new instance of mockRollbackReleaseAction. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func newMockRollbackReleaseAction(t mockConstructorTestingTnewMockRollbackReleaseAction) *mockRollbackReleaseAction { + mock := &mockRollbackReleaseAction{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/helm/client/mock_uninstallAction_test.go b/pkg/helm/client/mock_uninstallAction_test.go new file mode 100644 index 0000000..db627e6 --- /dev/null +++ b/pkg/helm/client/mock_uninstallAction_test.go @@ -0,0 +1,135 @@ +// Code generated by mockery v2.20.0. DO NOT EDIT. + +package client + +import ( + mock "github.com/stretchr/testify/mock" + action "helm.sh/helm/v3/pkg/action" + + release "helm.sh/helm/v3/pkg/release" +) + +// mockUninstallAction is an autogenerated mock type for the uninstallAction type +type mockUninstallAction struct { + mock.Mock +} + +type mockUninstallAction_Expecter struct { + mock *mock.Mock +} + +func (_m *mockUninstallAction) EXPECT() *mockUninstallAction_Expecter { + return &mockUninstallAction_Expecter{mock: &_m.Mock} +} + +// raw provides a mock function with given fields: +func (_m *mockUninstallAction) raw() *action.Uninstall { + ret := _m.Called() + + var r0 *action.Uninstall + if rf, ok := ret.Get(0).(func() *action.Uninstall); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*action.Uninstall) + } + } + + return r0 +} + +// mockUninstallAction_raw_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'raw' +type mockUninstallAction_raw_Call struct { + *mock.Call +} + +// raw is a helper method to define mock.On call +func (_e *mockUninstallAction_Expecter) raw() *mockUninstallAction_raw_Call { + return &mockUninstallAction_raw_Call{Call: _e.mock.On("raw")} +} + +func (_c *mockUninstallAction_raw_Call) Run(run func()) *mockUninstallAction_raw_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockUninstallAction_raw_Call) Return(_a0 *action.Uninstall) *mockUninstallAction_raw_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockUninstallAction_raw_Call) RunAndReturn(run func() *action.Uninstall) *mockUninstallAction_raw_Call { + _c.Call.Return(run) + return _c +} + +// uninstall provides a mock function with given fields: releaseName +func (_m *mockUninstallAction) uninstall(releaseName string) (*release.UninstallReleaseResponse, error) { + ret := _m.Called(releaseName) + + var r0 *release.UninstallReleaseResponse + var r1 error + if rf, ok := ret.Get(0).(func(string) (*release.UninstallReleaseResponse, error)); ok { + return rf(releaseName) + } + if rf, ok := ret.Get(0).(func(string) *release.UninstallReleaseResponse); ok { + r0 = rf(releaseName) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*release.UninstallReleaseResponse) + } + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(releaseName) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockUninstallAction_uninstall_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'uninstall' +type mockUninstallAction_uninstall_Call struct { + *mock.Call +} + +// uninstall is a helper method to define mock.On call +// - releaseName string +func (_e *mockUninstallAction_Expecter) uninstall(releaseName interface{}) *mockUninstallAction_uninstall_Call { + return &mockUninstallAction_uninstall_Call{Call: _e.mock.On("uninstall", releaseName)} +} + +func (_c *mockUninstallAction_uninstall_Call) Run(run func(releaseName string)) *mockUninstallAction_uninstall_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *mockUninstallAction_uninstall_Call) Return(_a0 *release.UninstallReleaseResponse, _a1 error) *mockUninstallAction_uninstall_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockUninstallAction_uninstall_Call) RunAndReturn(run func(string) (*release.UninstallReleaseResponse, error)) *mockUninstallAction_uninstall_Call { + _c.Call.Return(run) + return _c +} + +type mockConstructorTestingTnewMockUninstallAction interface { + mock.TestingT + Cleanup(func()) +} + +// newMockUninstallAction creates a new instance of mockUninstallAction. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func newMockUninstallAction(t mockConstructorTestingTnewMockUninstallAction) *mockUninstallAction { + mock := &mockUninstallAction{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/helm/client/mock_upgradeAction_test.go b/pkg/helm/client/mock_upgradeAction_test.go new file mode 100644 index 0000000..8624970 --- /dev/null +++ b/pkg/helm/client/mock_upgradeAction_test.go @@ -0,0 +1,142 @@ +// Code generated by mockery v2.20.0. DO NOT EDIT. + +package client + +import ( + action "helm.sh/helm/v3/pkg/action" + chart "helm.sh/helm/v3/pkg/chart" + + context "context" + + mock "github.com/stretchr/testify/mock" + + release "helm.sh/helm/v3/pkg/release" +) + +// mockUpgradeAction is an autogenerated mock type for the upgradeAction type +type mockUpgradeAction struct { + mock.Mock +} + +type mockUpgradeAction_Expecter struct { + mock *mock.Mock +} + +func (_m *mockUpgradeAction) EXPECT() *mockUpgradeAction_Expecter { + return &mockUpgradeAction_Expecter{mock: &_m.Mock} +} + +// raw provides a mock function with given fields: +func (_m *mockUpgradeAction) raw() *action.Upgrade { + ret := _m.Called() + + var r0 *action.Upgrade + if rf, ok := ret.Get(0).(func() *action.Upgrade); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*action.Upgrade) + } + } + + return r0 +} + +// mockUpgradeAction_raw_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'raw' +type mockUpgradeAction_raw_Call struct { + *mock.Call +} + +// raw is a helper method to define mock.On call +func (_e *mockUpgradeAction_Expecter) raw() *mockUpgradeAction_raw_Call { + return &mockUpgradeAction_raw_Call{Call: _e.mock.On("raw")} +} + +func (_c *mockUpgradeAction_raw_Call) Run(run func()) *mockUpgradeAction_raw_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockUpgradeAction_raw_Call) Return(_a0 *action.Upgrade) *mockUpgradeAction_raw_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockUpgradeAction_raw_Call) RunAndReturn(run func() *action.Upgrade) *mockUpgradeAction_raw_Call { + _c.Call.Return(run) + return _c +} + +// upgrade provides a mock function with given fields: ctx, releaseName, _a2, values +func (_m *mockUpgradeAction) upgrade(ctx context.Context, releaseName string, _a2 *chart.Chart, values map[string]interface{}) (*release.Release, error) { + ret := _m.Called(ctx, releaseName, _a2, values) + + var r0 *release.Release + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, *chart.Chart, map[string]interface{}) (*release.Release, error)); ok { + return rf(ctx, releaseName, _a2, values) + } + if rf, ok := ret.Get(0).(func(context.Context, string, *chart.Chart, map[string]interface{}) *release.Release); ok { + r0 = rf(ctx, releaseName, _a2, values) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*release.Release) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, *chart.Chart, map[string]interface{}) error); ok { + r1 = rf(ctx, releaseName, _a2, values) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockUpgradeAction_upgrade_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'upgrade' +type mockUpgradeAction_upgrade_Call struct { + *mock.Call +} + +// upgrade is a helper method to define mock.On call +// - ctx context.Context +// - releaseName string +// - _a2 *chart.Chart +// - values map[string]interface{} +func (_e *mockUpgradeAction_Expecter) upgrade(ctx interface{}, releaseName interface{}, _a2 interface{}, values interface{}) *mockUpgradeAction_upgrade_Call { + return &mockUpgradeAction_upgrade_Call{Call: _e.mock.On("upgrade", ctx, releaseName, _a2, values)} +} + +func (_c *mockUpgradeAction_upgrade_Call) Run(run func(ctx context.Context, releaseName string, _a2 *chart.Chart, values map[string]interface{})) *mockUpgradeAction_upgrade_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(*chart.Chart), args[3].(map[string]interface{})) + }) + return _c +} + +func (_c *mockUpgradeAction_upgrade_Call) Return(_a0 *release.Release, _a1 error) *mockUpgradeAction_upgrade_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockUpgradeAction_upgrade_Call) RunAndReturn(run func(context.Context, string, *chart.Chart, map[string]interface{}) (*release.Release, error)) *mockUpgradeAction_upgrade_Call { + _c.Call.Return(run) + return _c +} + +type mockConstructorTestingTnewMockUpgradeAction interface { + mock.TestingT + Cleanup(func()) +} + +// newMockUpgradeAction creates a new instance of mockUpgradeAction. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func newMockUpgradeAction(t mockConstructorTestingTnewMockUpgradeAction) *mockUpgradeAction { + mock := &mockUpgradeAction{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/helm/client/spec.go b/pkg/helm/client/spec.go new file mode 100644 index 0000000..52f8632 --- /dev/null +++ b/pkg/helm/client/spec.go @@ -0,0 +1,28 @@ +package client + +import ( + "github.com/cloudogu/k8s-component-operator/pkg/helm/client/values" + "github.com/pkg/errors" + + "sigs.k8s.io/yaml" + + "helm.sh/helm/v3/pkg/getter" +) + +// GetValuesMap returns the merged mapped out values of a chart, +// using both ValuesYaml and ValuesOptions +func (spec *ChartSpec) GetValuesMap(p getter.Providers) (map[string]interface{}, error) { + valuesYaml := map[string]interface{}{} + + err := yaml.Unmarshal([]byte(spec.ValuesYaml), &valuesYaml) + if err != nil { + return nil, errors.Wrap(err, "Failed to Parse ValuesYaml") + } + + valuesOptions, err := spec.ValuesOptions.MergeValues(p) + if err != nil { + return nil, errors.Wrap(err, "Failed to Parse ValuesOptions") + } + + return values.MergeMaps(valuesYaml, valuesOptions), nil +} diff --git a/pkg/helm/client/testdata/deprecated-chart/Chart.yaml b/pkg/helm/client/testdata/deprecated-chart/Chart.yaml new file mode 100644 index 0000000..c5a3275 --- /dev/null +++ b/pkg/helm/client/testdata/deprecated-chart/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: test-chart +description: Test Chart +type: application +version: 1.0.0 +appVersion: "1.0.0" +deprecated: true diff --git a/pkg/helm/client/testdata/deprecated-chart/templates/cm.yaml b/pkg/helm/client/testdata/deprecated-chart/templates/cm.yaml new file mode 100644 index 0000000..fe1ad99 --- /dev/null +++ b/pkg/helm/client/testdata/deprecated-chart/templates/cm.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-cm +data: + myKey: "my-value" \ No newline at end of file diff --git a/pkg/helm/client/testdata/invalid-kubeconfig.yaml b/pkg/helm/client/testdata/invalid-kubeconfig.yaml new file mode 100644 index 0000000..f69d6dc --- /dev/null +++ b/pkg/helm/client/testdata/invalid-kubeconfig.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Config +# current-context: example-cluster # fails if no current context +clusters: + - cluster: + server: cluster.example.com + name: example-cluster +contexts: + - context: + cluster: example-cluster + namespace: default + user: example-user + name: example-cluster +users: + - name: example-user + user: + username: example-user + password: example-password \ No newline at end of file diff --git a/pkg/helm/client/testdata/invalid-type-test-chart/Chart.yaml b/pkg/helm/client/testdata/invalid-type-test-chart/Chart.yaml new file mode 100644 index 0000000..eb53207 --- /dev/null +++ b/pkg/helm/client/testdata/invalid-type-test-chart/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: test-chart +description: Test Chart +type: library +version: 1.0.0 +appVersion: "1.0.0" \ No newline at end of file diff --git a/pkg/helm/client/testdata/invalid-type-test-chart/templates/cm.yaml b/pkg/helm/client/testdata/invalid-type-test-chart/templates/cm.yaml new file mode 100644 index 0000000..fe1ad99 --- /dev/null +++ b/pkg/helm/client/testdata/invalid-type-test-chart/templates/cm.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-cm +data: + myKey: "my-value" \ No newline at end of file diff --git a/pkg/helm/client/testdata/kubeconfig.yaml b/pkg/helm/client/testdata/kubeconfig.yaml new file mode 100644 index 0000000..bfc9a67 --- /dev/null +++ b/pkg/helm/client/testdata/kubeconfig.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Config +current-context: example-cluster +clusters: + - cluster: + server: cluster.example.com + name: example-cluster +contexts: + - context: + cluster: example-cluster + namespace: default + user: example-user + name: example-cluster +users: + - name: example-user + user: + username: example-user + password: example-password \ No newline at end of file diff --git a/pkg/helm/client/testdata/test-chart/Chart.yaml b/pkg/helm/client/testdata/test-chart/Chart.yaml new file mode 100644 index 0000000..9905842 --- /dev/null +++ b/pkg/helm/client/testdata/test-chart/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: test-chart +description: Test Chart +type: application +version: 1.0.0 +appVersion: "1.0.0" diff --git a/pkg/helm/client/testdata/test-chart/templates/cm.yaml b/pkg/helm/client/testdata/test-chart/templates/cm.yaml new file mode 100644 index 0000000..fe1ad99 --- /dev/null +++ b/pkg/helm/client/testdata/test-chart/templates/cm.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-cm +data: + myKey: "my-value" \ No newline at end of file diff --git a/pkg/helm/client/types.go b/pkg/helm/client/types.go new file mode 100644 index 0000000..29e3776 --- /dev/null +++ b/pkg/helm/client/types.go @@ -0,0 +1,111 @@ +package client + +import ( + "github.com/cloudogu/k8s-component-operator/pkg/helm/client/values" + "io" + "time" + + "k8s.io/client-go/rest" + + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chartutil" + "helm.sh/helm/v3/pkg/cli" +) + +// Type Guard asserting that HelmClient satisfies the HelmClient interface. +var _ Client = &HelmClient{} + +// KubeConfClientOptions defines the options used for constructing a client via kubeconfig. +type KubeConfClientOptions struct { + *Options + KubeContext string + KubeConfig []byte +} + +// RestConfClientOptions defines the options used for constructing a client via REST config. +type RestConfClientOptions struct { + *Options + RestConfig *rest.Config +} + +// Options defines the options of a client. If Output is not set, os.Stdout will be used. +type Options struct { + Namespace string + RepositoryConfig string + RepositoryCache string + Debug bool + DebugLog action.DebugLog + RegistryConfig string + Output io.Writer + // PlainHttp forces the registry client to establish plain http connections. + PlainHttp bool +} + +// RESTClientOption is a function that can be used to set the RESTClientOptions of a HelmClient. +type RESTClientOption func(*rest.Config) + +// RESTClientGetter defines the values of a helm REST client. +type RESTClientGetter struct { + namespace string + kubeConfig []byte + restConfig *rest.Config + + opts []RESTClientOption +} + +// HelmClient Client defines the values of a helm client. +type HelmClient struct { + TagResolver + // Settings defines the environment settings of a client. + Settings *cli.EnvSettings + actions actionProvider + output io.Writer + DebugLog action.DebugLog +} + +type HelmTemplateOptions struct { + KubeVersion *chartutil.KubeVersion + // APIVersions defined here will be appended to the default list helm provides + APIVersions chartutil.VersionSet +} + +// ChartSpec defines the values of a helm chart +// +kubebuilder:object:generate:=true +type ChartSpec struct { + ReleaseName string `json:"release"` + ChartName string `json:"chart"` + // Namespace where the chart release is deployed. + // Note that client.Options.Namespace should ideally match the namespace configured here. + Namespace string `json:"namespace"` + // ValuesYaml is the values.yaml content. + // use string instead of map[string]interface{} + // https://github.com/kubernetes-sigs/kubebuilder/issues/528#issuecomment-466449483 + // and https://github.com/kubernetes-sigs/controller-tools/pull/317 + // +optional + ValuesYaml string `json:"valuesYaml,omitempty"` + // Specify values similar to the cli + // +optional + ValuesOptions values.Options `json:"valuesOptions,omitempty"` + // Version of the chart release. + // +optional + Version string `json:"version,omitempty"` + // CreateNamespace indicates whether to create the namespace if it does not exist. + // +optional + CreateNamespace bool `json:"createNamespace,omitempty"` + // Timeout configures the time to wait for any individual Kubernetes operation (like Jobs for hooks). + // +optional + Timeout time.Duration `json:"timeout,omitempty"` + // Atomic indicates whether to install resources atomically. + // 'Wait' will automatically be set to true when using Atomic. + // +optional + Atomic bool `json:"atomic,omitempty"` + // ResetValues indicates whether to reset the values.yaml file during installation. + // +optional + ResetValues bool `json:"resetValues,omitempty"` + // ReuseValues indicates whether to reuse the values.yaml file during installation. + // +optional + ReuseValues bool `json:"reuseValues,omitempty"` + // CleanupOnFail indicates whether to cleanup the release on failure. + // +optional + CleanupOnFail bool `json:"cleanupOnFail,omitempty"` +} diff --git a/pkg/helm/client/values/options.go b/pkg/helm/client/values/options.go new file mode 100644 index 0000000..6fb8823 --- /dev/null +++ b/pkg/helm/client/values/options.go @@ -0,0 +1,147 @@ +/* +Copyright The Helm 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. +*/ +/* +Copied from https://github.com/helm/helm/blob/eea2f27babb0fddd9fb1907f4d8531c8f5c73c66/pkg/cli/values/options.go +Changes: +- Add generator comments +- Export MergeMaps +*/ + +package values + +import ( + "io" + "net/url" + "os" + "strings" + + "github.com/pkg/errors" + + "sigs.k8s.io/yaml" + + "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/strvals" +) + +// Options captures the different ways to specify values +// +kubebuilder:object:generate:=true +type Options struct { + ValueFiles []string // -f/--values + StringValues []string // --set-string + Values []string // --set + FileValues []string // --set-file + JSONValues []string // --set-json +} + +// MergeValues merges values from files specified via -f/--values and directly +// via --set-json, --set, --set-string, or --set-file, marshaling them to YAML +func (opts *Options) MergeValues(p getter.Providers) (map[string]interface{}, error) { + base := map[string]interface{}{} + + // User specified a values files via -f/--values + for _, filePath := range opts.ValueFiles { + currentMap := map[string]interface{}{} + + bytes, err := readFile(filePath, p) + if err != nil { + return nil, err + } + + if err := yaml.Unmarshal(bytes, ¤tMap); err != nil { + return nil, errors.Wrapf(err, "failed to parse %s", filePath) + } + // Merge with the previous map + base = MergeMaps(base, currentMap) + } + + // User specified a value via --set-json + for _, value := range opts.JSONValues { + if err := strvals.ParseJSON(value, base); err != nil { + return nil, errors.Errorf("failed parsing --set-json data %s", value) + } + } + + // User specified a value via --set + for _, value := range opts.Values { + if err := strvals.ParseInto(value, base); err != nil { + return nil, errors.Wrap(err, "failed parsing --set data") + } + } + + // User specified a value via --set-string + for _, value := range opts.StringValues { + if err := strvals.ParseIntoString(value, base); err != nil { + return nil, errors.Wrap(err, "failed parsing --set-string data") + } + } + + // User specified a value via --set-file + for _, value := range opts.FileValues { + reader := func(rs []rune) (interface{}, error) { + bytes, err := readFile(string(rs), p) + if err != nil { + return nil, err + } + return string(bytes), err + } + if err := strvals.ParseIntoFile(value, base, reader); err != nil { + return nil, errors.Wrap(err, "failed parsing --set-file data") + } + } + + return base, nil +} + +func MergeMaps(a, b map[string]interface{}) map[string]interface{} { + out := make(map[string]interface{}, len(a)) + for k, v := range a { + out[k] = v + } + for k, v := range b { + if v, ok := v.(map[string]interface{}); ok { + if bv, ok := out[k]; ok { + if bv, ok := bv.(map[string]interface{}); ok { + out[k] = MergeMaps(bv, v) + continue + } + } + } + out[k] = v + } + return out +} + +// readFile load a file from stdin, the local directory, or a remote file with a url. +func readFile(filePath string, p getter.Providers) ([]byte, error) { + if strings.TrimSpace(filePath) == "-" { + return io.ReadAll(os.Stdin) + } + u, err := url.Parse(filePath) + if err != nil { + return nil, err + } + + // FIXME: maybe someone handle other protocols like ftp. + g, err := p.ByScheme(u.Scheme) + if err != nil { + return os.ReadFile(filePath) + } + data, err := g.Get(filePath, getter.WithURL(filePath)) + if err != nil { + return nil, err + } + return data.Bytes(), err +} diff --git a/pkg/helm/client/values/options_test.go b/pkg/helm/client/values/options_test.go new file mode 100644 index 0000000..deed95d --- /dev/null +++ b/pkg/helm/client/values/options_test.go @@ -0,0 +1,92 @@ +/* +Copyright The Helm 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. +*/ +/* +Copied from https://github.com/helm/helm/blob/eea2f27babb0fddd9fb1907f4d8531c8f5c73c66/pkg/cli/values/options_test.go +No changes. +*/ + +package values + +import ( + "reflect" + "testing" + + "helm.sh/helm/v3/pkg/getter" +) + +func TestMergeValues(t *testing.T) { + nestedMap := map[string]interface{}{ + "foo": "bar", + "baz": map[string]string{ + "cool": "stuff", + }, + } + anotherNestedMap := map[string]interface{}{ + "foo": "bar", + "baz": map[string]string{ + "cool": "things", + "awesome": "stuff", + }, + } + flatMap := map[string]interface{}{ + "foo": "bar", + "baz": "stuff", + } + anotherFlatMap := map[string]interface{}{ + "testing": "fun", + } + + testMap := MergeMaps(flatMap, nestedMap) + equal := reflect.DeepEqual(testMap, nestedMap) + if !equal { + t.Errorf("Expected a nested map to overwrite a flat value. Expected: %v, got %v", nestedMap, testMap) + } + + testMap = MergeMaps(nestedMap, flatMap) + equal = reflect.DeepEqual(testMap, flatMap) + if !equal { + t.Errorf("Expected a flat value to overwrite a map. Expected: %v, got %v", flatMap, testMap) + } + + testMap = MergeMaps(nestedMap, anotherNestedMap) + equal = reflect.DeepEqual(testMap, anotherNestedMap) + if !equal { + t.Errorf("Expected a nested map to overwrite another nested map. Expected: %v, got %v", anotherNestedMap, testMap) + } + + testMap = MergeMaps(anotherFlatMap, anotherNestedMap) + expectedMap := map[string]interface{}{ + "testing": "fun", + "foo": "bar", + "baz": map[string]string{ + "cool": "things", + "awesome": "stuff", + }, + } + equal = reflect.DeepEqual(testMap, expectedMap) + if !equal { + t.Errorf("Expected a map with different keys to merge properly with another map. Expected: %v, got %v", expectedMap, testMap) + } +} + +func TestReadFile(t *testing.T) { + var p getter.Providers + filePath := "%a.txt" + _, err := readFile(filePath, p) + if err == nil { + t.Errorf("Expected error when has special strings") + } +} diff --git a/pkg/helm/client_test.go b/pkg/helm/client_test.go index 63cdcf5..1bcb259 100644 --- a/pkg/helm/client_test.go +++ b/pkg/helm/client_test.go @@ -8,15 +8,14 @@ import ( "testing" "github.com/cloudogu/k8s-component-operator/pkg/config" + "github.com/cloudogu/k8s-component-operator/pkg/helm/client" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - helmclient "github.com/mittwald/go-helm-client" - "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/release" + "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" ) @@ -34,16 +33,16 @@ func TestNew(t *testing.T) { return &rest.Config{} } - client, err := NewClient(namespace, &config.HelmRepositoryData{PlainHttp: true}, false, nil) + helmClient, err := NewClient(namespace, &config.HelmRepositoryData{PlainHttp: true}, false, nil) require.NoError(t, err) - assert.NotNil(t, client) + assert.NotNil(t, helmClient) }) } func TestClient_InstallOrUpgrade(t *testing.T) { t.Run("should install or upgrade chart", func(t *testing.T) { - chartSpec := &helmclient.ChartSpec{ + chartSpec := &client.ChartSpec{ ReleaseName: "testComponent", ChartName: "testing/testComponent", Namespace: "testNS", @@ -52,17 +51,17 @@ func TestClient_InstallOrUpgrade(t *testing.T) { helmRepoData := &config.HelmRepositoryData{Endpoint: "https://staging.cloudogu.com"} mockHelmClient := NewMockHelmClient(t) - mockHelmClient.EXPECT().InstallOrUpgradeChart(testCtx, chartSpec, mock.Anything).Return(nil, nil) + mockHelmClient.EXPECT().InstallOrUpgradeChart(testCtx, chartSpec).Return(nil, nil) - client := &Client{helmClient: mockHelmClient, helmRepoData: helmRepoData} + helmClient := &Client{helmClient: mockHelmClient, helmRepoData: helmRepoData} - err := client.InstallOrUpgrade(testCtx, chartSpec) + err := helmClient.InstallOrUpgrade(testCtx, chartSpec) require.NoError(t, err) }) t.Run("should install or upgrade chart with oci-endpoint in chart-name", func(t *testing.T) { - chartSpec := &helmclient.ChartSpec{ + chartSpec := &client.ChartSpec{ ReleaseName: "testComponent", ChartName: "oci://some.where/testing/testComponent", Namespace: "testNS", @@ -71,17 +70,17 @@ func TestClient_InstallOrUpgrade(t *testing.T) { helmRepoData := &config.HelmRepositoryData{Endpoint: "staging.cloudogu.com", Schema: config.EndpointSchemaOCI} mockHelmClient := NewMockHelmClient(t) - mockHelmClient.EXPECT().InstallOrUpgradeChart(testCtx, chartSpec, mock.Anything).Return(nil, nil) + mockHelmClient.EXPECT().InstallOrUpgradeChart(testCtx, chartSpec).Return(nil, nil) - client := &Client{helmClient: mockHelmClient, helmRepoData: helmRepoData} + helmClient := &Client{helmClient: mockHelmClient, helmRepoData: helmRepoData} - err := client.InstallOrUpgrade(testCtx, chartSpec) + err := helmClient.InstallOrUpgrade(testCtx, chartSpec) require.NoError(t, err) }) t.Run("should patch version in install or upgrade chart when given version is empty", func(t *testing.T) { - chartSpec := &helmclient.ChartSpec{ + chartSpec := &client.ChartSpec{ ReleaseName: "testComponent", ChartName: "testing/testComponent", Namespace: "testNS", @@ -89,21 +88,19 @@ func TestClient_InstallOrUpgrade(t *testing.T) { helmRepoData := &config.HelmRepositoryData{Endpoint: "staging.cloudogu.com", Schema: config.EndpointSchemaOCI} mockHelmClient := NewMockHelmClient(t) - mockHelmClient.EXPECT().InstallOrUpgradeChart(testCtx, chartSpec, mock.Anything).Return(nil, nil) - - mockedTagResolver := newMockTagResolver(t) - mockedTagResolver.EXPECT().Tags(fmt.Sprintf("%s/%s", helmRepoData.Endpoint, chartSpec.ChartName)).Return([]string{"1.2.3", "1.0.5"}, nil) + mockHelmClient.EXPECT().InstallOrUpgradeChart(testCtx, chartSpec).Return(nil, nil) + mockHelmClient.EXPECT().Tags(fmt.Sprintf("%s/%s", helmRepoData.Endpoint, chartSpec.ChartName)).Return([]string{"1.2.3", "1.0.5"}, nil) - client := &Client{helmClient: mockHelmClient, helmRepoData: helmRepoData, tagResolver: mockedTagResolver} + helmClient := &Client{helmClient: mockHelmClient, helmRepoData: helmRepoData} - err := client.InstallOrUpgrade(testCtx, chartSpec) + err := helmClient.InstallOrUpgrade(testCtx, chartSpec) require.NoError(t, err) assert.Equal(t, "1.2.3", chartSpec.Version) }) t.Run("should fail to install or upgrade chart for error while patching version", func(t *testing.T) { - chartSpec := &helmclient.ChartSpec{ + chartSpec := &client.ChartSpec{ ReleaseName: "testComponent", ChartName: "testing/testComponent", Namespace: "testNS", @@ -111,13 +108,11 @@ func TestClient_InstallOrUpgrade(t *testing.T) { helmRepoData := &config.HelmRepositoryData{Endpoint: "staging.cloudogu.com", Schema: config.EndpointSchemaOCI} mockHelmClient := NewMockHelmClient(t) + mockHelmClient.EXPECT().Tags(fmt.Sprintf("%s/%s", helmRepoData.Endpoint, chartSpec.ChartName)).Return(nil, assert.AnError) - mockedTagResolver := newMockTagResolver(t) - mockedTagResolver.EXPECT().Tags(fmt.Sprintf("%s/%s", helmRepoData.Endpoint, chartSpec.ChartName)).Return(nil, assert.AnError) + helmClient := &Client{helmClient: mockHelmClient, helmRepoData: helmRepoData} - client := &Client{helmClient: mockHelmClient, helmRepoData: helmRepoData, tagResolver: mockedTagResolver} - - err := client.InstallOrUpgrade(testCtx, chartSpec) + err := helmClient.InstallOrUpgrade(testCtx, chartSpec) require.Error(t, err) assert.ErrorIs(t, err, assert.AnError) @@ -125,7 +120,7 @@ func TestClient_InstallOrUpgrade(t *testing.T) { }) t.Run("should fail to install or upgrade chart for error in helmClient", func(t *testing.T) { - chartSpec := &helmclient.ChartSpec{ + chartSpec := &client.ChartSpec{ ReleaseName: "testComponent", ChartName: "testing/testComponent", Namespace: "testNS", @@ -134,11 +129,11 @@ func TestClient_InstallOrUpgrade(t *testing.T) { helmRepoData := &config.HelmRepositoryData{Endpoint: "staging.cloudogu.com", Schema: config.EndpointSchemaOCI} mockHelmClient := NewMockHelmClient(t) - mockHelmClient.EXPECT().InstallOrUpgradeChart(testCtx, chartSpec, mock.Anything).Return(nil, assert.AnError) + mockHelmClient.EXPECT().InstallOrUpgradeChart(testCtx, chartSpec).Return(nil, assert.AnError) - client := &Client{helmClient: mockHelmClient, helmRepoData: helmRepoData} + helmClient := &Client{helmClient: mockHelmClient, helmRepoData: helmRepoData} - err := client.InstallOrUpgrade(testCtx, chartSpec) + err := helmClient.InstallOrUpgrade(testCtx, chartSpec) require.Error(t, err) assert.ErrorIs(t, err, assert.AnError) @@ -152,9 +147,9 @@ func TestClient_Uninstall(t *testing.T) { mockHelmClient := NewMockHelmClient(t) mockHelmClient.EXPECT().UninstallReleaseByName(releaseName).Return(nil) - client := &Client{helmClient: mockHelmClient, helmRepoData: nil} + helmClient := &Client{helmClient: mockHelmClient, helmRepoData: nil} - err := client.Uninstall(releaseName) + err := helmClient.Uninstall(releaseName) require.NoError(t, err) }) @@ -165,9 +160,9 @@ func TestClient_Uninstall(t *testing.T) { mockHelmClient := NewMockHelmClient(t) mockHelmClient.EXPECT().UninstallReleaseByName(releaseName).Return(assert.AnError) - client := &Client{helmClient: mockHelmClient, helmRepoData: nil} + helmClient := &Client{helmClient: mockHelmClient, helmRepoData: nil} - err := client.Uninstall(releaseName) + err := helmClient.Uninstall(releaseName) require.Error(t, err) assert.ErrorIs(t, err, assert.AnError) @@ -185,9 +180,9 @@ func TestClient_ListDeployedReleases(t *testing.T) { mockHelmClient := NewMockHelmClient(t) mockHelmClient.EXPECT().ListDeployedReleases().Return(releases, nil) - client := &Client{helmClient: mockHelmClient, helmRepoData: nil} + helmClient := &Client{helmClient: mockHelmClient, helmRepoData: nil} - result, err := client.ListDeployedReleases() + result, err := helmClient.ListDeployedReleases() require.NoError(t, err) assert.Equal(t, releases, result) @@ -197,9 +192,9 @@ func TestClient_ListDeployedReleases(t *testing.T) { mockHelmClient := NewMockHelmClient(t) mockHelmClient.EXPECT().ListDeployedReleases().Return(nil, assert.AnError) - client := &Client{helmClient: mockHelmClient, helmRepoData: nil} + helmClient := &Client{helmClient: mockHelmClient, helmRepoData: nil} - result, err := client.ListDeployedReleases() + result, err := helmClient.ListDeployedReleases() require.Error(t, err) assert.Nil(t, result) @@ -239,20 +234,19 @@ func TestClient_SatisfiesDependencies(t *testing.T) { PlainHttp: true, } - mockHelmClient := NewMockHelmClient(t) - mockHelmClient.EXPECT().GetChart("oci://some.where/testing/testComponent", mock.Anything).Return(nil, "", assert.AnError) - - chartSpec := &helmclient.ChartSpec{ + chartSpec := &client.ChartSpec{ ReleaseName: "testComponent", ChartName: "testComponent", Namespace: "testNS", Version: "0.1.1", } + mockHelmClient := NewMockHelmClient(t) + mockHelmClient.EXPECT().GetChart(chartSpec).Return(nil, "", assert.AnError) + sut := &Client{ helmClient: mockHelmClient, helmRepoData: repoConfigData, - actionConfig: new(action.Configuration), } // when @@ -278,21 +272,21 @@ func TestClient_SatisfiesDependencies(t *testing.T) { Version: "3.*.*", }}, }} - mockHelmClient := NewMockHelmClient(t) - mockHelmClient.EXPECT().GetChart("oci://some.where/testing/testComponent", mock.Anything).Return(helmChart, "myPath", nil) - mockHelmClient.EXPECT().ListDeployedReleases().Return(nil, assert.AnError) - chartSpec := &helmclient.ChartSpec{ + chartSpec := &client.ChartSpec{ ReleaseName: "testComponent", ChartName: "testComponent", Namespace: "testNS", Version: "0.1.1", } + mockHelmClient := NewMockHelmClient(t) + mockHelmClient.EXPECT().GetChart(chartSpec).Return(helmChart, "myPath", nil) + mockHelmClient.EXPECT().ListDeployedReleases().Return(nil, assert.AnError) + sut := &Client{ helmClient: mockHelmClient, helmRepoData: repoConfigData, - actionConfig: new(action.Configuration), } // when @@ -318,25 +312,24 @@ func TestClient_SatisfiesDependencies(t *testing.T) { "k8s.cloudogu.com/ces-dependency/k8s-etcd": "3.2.1", }, }} + chartSpec := &client.ChartSpec{ + ReleaseName: "testComponent", + ChartName: "testComponent", + Namespace: "testNS", + Version: "0.1.1", + } + mockHelmClient := NewMockHelmClient(t) - mockHelmClient.EXPECT().GetChart("oci://some.where/testing/testComponent", mock.Anything).Return(helmChart, "myPath", nil) + mockHelmClient.EXPECT().GetChart(chartSpec).Return(helmChart, "myPath", nil) var deployedReleases []*release.Release mockHelmClient.EXPECT().ListDeployedReleases().Return(deployedReleases, nil) mockDepChecker := newMockDependencyChecker(t) mockDepChecker.EXPECT().CheckSatisfied(dependencies, deployedReleases).Return(assert.AnError) - chartSpec := &helmclient.ChartSpec{ - ReleaseName: "testComponent", - ChartName: "testComponent", - Namespace: "testNS", - Version: "0.1.1", - } - sut := &Client{ helmClient: mockHelmClient, helmRepoData: repoConfigData, - actionConfig: new(action.Configuration), dependencyChecker: mockDepChecker, } @@ -365,25 +358,24 @@ func TestClient_SatisfiesDependencies(t *testing.T) { "k8s.cloudogu.com/ces-dependency/k8s-etcd": "3.2.1", }, }} + chartSpec := &client.ChartSpec{ + ReleaseName: "testComponent", + ChartName: "testComponent", + Namespace: "testNS", + Version: "0.1.1", + } + mockHelmClient := NewMockHelmClient(t) - mockHelmClient.EXPECT().GetChart("oci://some.where/testing/testComponent", mock.Anything).Return(helmChart, "myPath", nil) + mockHelmClient.EXPECT().GetChart(chartSpec).Return(helmChart, "myPath", nil) deployedReleases := []*release.Release{createRelease("k8s-etcd", "3.2.1")} mockHelmClient.EXPECT().ListDeployedReleases().Return(deployedReleases, nil) mockDepChecker := newMockDependencyChecker(t) mockDepChecker.EXPECT().CheckSatisfied(dependencies, deployedReleases).Return(nil) - chartSpec := &helmclient.ChartSpec{ - ReleaseName: "testComponent", - ChartName: "testComponent", - Namespace: "testNS", - Version: "0.1.1", - } - sut := &Client{ helmClient: mockHelmClient, helmRepoData: repoConfigData, - actionConfig: new(action.Configuration), dependencyChecker: mockDepChecker, } @@ -408,28 +400,24 @@ func TestClient_SatisfiesDependencies(t *testing.T) { "k8s.cloudogu.com/ces-dependency/k8s-etcd": "3.2.1", }, }} + chartSpec := &client.ChartSpec{ + ReleaseName: "testComponent", + ChartName: "testComponent", + Namespace: "testNS", + } + mockHelmClient := NewMockHelmClient(t) - mockHelmClient.EXPECT().GetChart("oci://some.where/testing/testComponent", mock.Anything).Return(helmChart, "myPath", nil) + mockHelmClient.EXPECT().GetChart(chartSpec).Return(helmChart, "myPath", nil) deployedReleases := []*release.Release{createRelease("k8s-etcd", "3.2.1")} mockHelmClient.EXPECT().ListDeployedReleases().Return(deployedReleases, nil) + mockHelmClient.EXPECT().Tags(fmt.Sprintf("%s/%s", repoConfigData.Endpoint, chartSpec.ChartName)).Return([]string{"1.2.3", "1.0.5"}, nil) mockDepChecker := newMockDependencyChecker(t) mockDepChecker.EXPECT().CheckSatisfied(dependencies, deployedReleases).Return(nil) - chartSpec := &helmclient.ChartSpec{ - ReleaseName: "testComponent", - ChartName: "testComponent", - Namespace: "testNS", - } - - mockedTagResolver := newMockTagResolver(t) - mockedTagResolver.EXPECT().Tags(fmt.Sprintf("%s/%s", repoConfigData.Endpoint, chartSpec.ChartName)).Return([]string{"1.2.3", "1.0.5"}, nil) - sut := &Client{ helmClient: mockHelmClient, helmRepoData: repoConfigData, - tagResolver: mockedTagResolver, - actionConfig: new(action.Configuration), dependencyChecker: mockDepChecker, } @@ -449,22 +437,18 @@ func TestClient_SatisfiesDependencies(t *testing.T) { PlainHttp: true, } - mockHelmClient := NewMockHelmClient(t) - - chartSpec := &helmclient.ChartSpec{ + chartSpec := &client.ChartSpec{ ReleaseName: "testComponent", ChartName: "testComponent", Namespace: "testNS", } - mockedTagResolver := newMockTagResolver(t) - mockedTagResolver.EXPECT().Tags(fmt.Sprintf("%s/%s", repoConfigData.Endpoint, chartSpec.ChartName)).Return(nil, assert.AnError) + mockHelmClient := NewMockHelmClient(t) + mockHelmClient.EXPECT().Tags(fmt.Sprintf("%s/%s", repoConfigData.Endpoint, chartSpec.ChartName)).Return(nil, assert.AnError) sut := &Client{ helmClient: mockHelmClient, helmRepoData: repoConfigData, - tagResolver: mockedTagResolver, - actionConfig: new(action.Configuration), } // when @@ -486,17 +470,17 @@ func Test_patchChartVersion(t *testing.T) { PlainHttp: false, } - chartSpec := &helmclient.ChartSpec{ + chartSpec := &client.ChartSpec{ ReleaseName: "k8s-etcd", ChartName: "oci://some.endpoint/testing/myChart", } - mockedTagResolver := newMockTagResolver(t) - mockedTagResolver.EXPECT().Tags(strings.TrimPrefix(chartSpec.ChartName, ociSchemePrefix)).Return([]string{"1.2.3", "1.0.5"}, nil) + mockedHelmClient := NewMockHelmClient(t) + mockedHelmClient.EXPECT().Tags(strings.TrimPrefix(chartSpec.ChartName, ociSchemePrefix)).Return([]string{"1.2.3", "1.0.5"}, nil) sut := &Client{ + helmClient: mockedHelmClient, helmRepoData: repoConfigData, - tagResolver: mockedTagResolver, } // when @@ -514,17 +498,17 @@ func Test_patchChartVersion(t *testing.T) { PlainHttp: false, } - chartSpec := &helmclient.ChartSpec{ + chartSpec := &client.ChartSpec{ ReleaseName: "k8s-etcd", ChartName: "oci://some.endpoint/testing/myChart", } - mockedTagResolver := newMockTagResolver(t) - mockedTagResolver.EXPECT().Tags(strings.TrimPrefix(chartSpec.ChartName, ociSchemePrefix)).Return([]string{}, nil) + mockedHelmClient := NewMockHelmClient(t) + mockedHelmClient.EXPECT().Tags(strings.TrimPrefix(chartSpec.ChartName, ociSchemePrefix)).Return([]string{}, nil) sut := &Client{ + helmClient: mockedHelmClient, helmRepoData: repoConfigData, - tagResolver: mockedTagResolver, } // when @@ -542,17 +526,17 @@ func Test_patchChartVersion(t *testing.T) { PlainHttp: false, } - chartSpec := &helmclient.ChartSpec{ + chartSpec := &client.ChartSpec{ ReleaseName: "k8s-etcd", ChartName: "oci://some.endpoint/testing/myChart", } - mockedTagResolver := newMockTagResolver(t) - mockedTagResolver.EXPECT().Tags(strings.TrimPrefix(chartSpec.ChartName, ociSchemePrefix)).Return(nil, assert.AnError) + mockedHelmClient := NewMockHelmClient(t) + mockedHelmClient.EXPECT().Tags(strings.TrimPrefix(chartSpec.ChartName, ociSchemePrefix)).Return(nil, assert.AnError) sut := &Client{ + helmClient: mockedHelmClient, helmRepoData: repoConfigData, - tagResolver: mockedTagResolver, } // when diff --git a/pkg/helm/dependencyChecker_test.go b/pkg/helm/dependencyChecker_test.go index bed66c5..334a4be 100644 --- a/pkg/helm/dependencyChecker_test.go +++ b/pkg/helm/dependencyChecker_test.go @@ -128,6 +128,15 @@ func Test_installedDependencyChecker_CheckSatisfied(t *testing.T) { }, wantErr: assert.NoError, }, + { + name: "should succeed for version-range with pre-release", + args: args{ + // the upper bound must be a pre-release as well due to a bug, see https://github.com/Masterminds/semver/issues/177 + dependencies: []Dependency{createDependency("k8s-etcd", ">=3.0.0-0 <4.0-0")}, + deployedReleases: []*release.Release{createRelease("k8s-etcd", "3.0.0-2")}, + }, + wantErr: assert.NoError, + }, { name: "should fail if one dependency is not installed", args: args{ diff --git a/pkg/helm/mock_HelmClient_test.go b/pkg/helm/mock_HelmClient_test.go index 45f88e9..e7e2424 100644 --- a/pkg/helm/mock_HelmClient_test.go +++ b/pkg/helm/mock_HelmClient_test.go @@ -6,15 +6,13 @@ import ( action "helm.sh/helm/v3/pkg/action" chart "helm.sh/helm/v3/pkg/chart" - context "context" + client "github.com/cloudogu/k8s-component-operator/pkg/helm/client" - helmclient "github.com/mittwald/go-helm-client" + context "context" mock "github.com/stretchr/testify/mock" release "helm.sh/helm/v3/pkg/release" - - repo "helm.sh/helm/v3/pkg/repo" ) // MockHelmClient is an autogenerated mock type for the HelmClient type @@ -30,74 +28,32 @@ func (_m *MockHelmClient) EXPECT() *MockHelmClient_Expecter { return &MockHelmClient_Expecter{mock: &_m.Mock} } -// AddOrUpdateChartRepo provides a mock function with given fields: entry -func (_m *MockHelmClient) AddOrUpdateChartRepo(entry repo.Entry) error { - ret := _m.Called(entry) - - var r0 error - if rf, ok := ret.Get(0).(func(repo.Entry) error); ok { - r0 = rf(entry) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockHelmClient_AddOrUpdateChartRepo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddOrUpdateChartRepo' -type MockHelmClient_AddOrUpdateChartRepo_Call struct { - *mock.Call -} - -// AddOrUpdateChartRepo is a helper method to define mock.On call -// - entry repo.Entry -func (_e *MockHelmClient_Expecter) AddOrUpdateChartRepo(entry interface{}) *MockHelmClient_AddOrUpdateChartRepo_Call { - return &MockHelmClient_AddOrUpdateChartRepo_Call{Call: _e.mock.On("AddOrUpdateChartRepo", entry)} -} - -func (_c *MockHelmClient_AddOrUpdateChartRepo_Call) Run(run func(entry repo.Entry)) *MockHelmClient_AddOrUpdateChartRepo_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(repo.Entry)) - }) - return _c -} - -func (_c *MockHelmClient_AddOrUpdateChartRepo_Call) Return(_a0 error) *MockHelmClient_AddOrUpdateChartRepo_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockHelmClient_AddOrUpdateChartRepo_Call) RunAndReturn(run func(repo.Entry) error) *MockHelmClient_AddOrUpdateChartRepo_Call { - _c.Call.Return(run) - return _c -} - -// GetChart provides a mock function with given fields: chartName, chartPathOptions -func (_m *MockHelmClient) GetChart(chartName string, chartPathOptions *action.ChartPathOptions) (*chart.Chart, string, error) { - ret := _m.Called(chartName, chartPathOptions) +// GetChart provides a mock function with given fields: spec +func (_m *MockHelmClient) GetChart(spec *client.ChartSpec) (*chart.Chart, string, error) { + ret := _m.Called(spec) var r0 *chart.Chart var r1 string var r2 error - if rf, ok := ret.Get(0).(func(string, *action.ChartPathOptions) (*chart.Chart, string, error)); ok { - return rf(chartName, chartPathOptions) + if rf, ok := ret.Get(0).(func(*client.ChartSpec) (*chart.Chart, string, error)); ok { + return rf(spec) } - if rf, ok := ret.Get(0).(func(string, *action.ChartPathOptions) *chart.Chart); ok { - r0 = rf(chartName, chartPathOptions) + if rf, ok := ret.Get(0).(func(*client.ChartSpec) *chart.Chart); ok { + r0 = rf(spec) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*chart.Chart) } } - if rf, ok := ret.Get(1).(func(string, *action.ChartPathOptions) string); ok { - r1 = rf(chartName, chartPathOptions) + if rf, ok := ret.Get(1).(func(*client.ChartSpec) string); ok { + r1 = rf(spec) } else { r1 = ret.Get(1).(string) } - if rf, ok := ret.Get(2).(func(string, *action.ChartPathOptions) error); ok { - r2 = rf(chartName, chartPathOptions) + if rf, ok := ret.Get(2).(func(*client.ChartSpec) error); ok { + r2 = rf(spec) } else { r2 = ret.Error(2) } @@ -111,15 +67,14 @@ type MockHelmClient_GetChart_Call struct { } // GetChart is a helper method to define mock.On call -// - chartName string -// - chartPathOptions *action.ChartPathOptions -func (_e *MockHelmClient_Expecter) GetChart(chartName interface{}, chartPathOptions interface{}) *MockHelmClient_GetChart_Call { - return &MockHelmClient_GetChart_Call{Call: _e.mock.On("GetChart", chartName, chartPathOptions)} +// - spec *client.ChartSpec +func (_e *MockHelmClient_Expecter) GetChart(spec interface{}) *MockHelmClient_GetChart_Call { + return &MockHelmClient_GetChart_Call{Call: _e.mock.On("GetChart", spec)} } -func (_c *MockHelmClient_GetChart_Call) Run(run func(chartName string, chartPathOptions *action.ChartPathOptions)) *MockHelmClient_GetChart_Call { +func (_c *MockHelmClient_GetChart_Call) Run(run func(spec *client.ChartSpec)) *MockHelmClient_GetChart_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(*action.ChartPathOptions)) + run(args[0].(*client.ChartSpec)) }) return _c } @@ -129,7 +84,7 @@ func (_c *MockHelmClient_GetChart_Call) Return(_a0 *chart.Chart, _a1 string, _a2 return _c } -func (_c *MockHelmClient_GetChart_Call) RunAndReturn(run func(string, *action.ChartPathOptions) (*chart.Chart, string, error)) *MockHelmClient_GetChart_Call { +func (_c *MockHelmClient_GetChart_Call) RunAndReturn(run func(*client.ChartSpec) (*chart.Chart, string, error)) *MockHelmClient_GetChart_Call { _c.Call.Return(run) return _c } @@ -243,25 +198,25 @@ func (_c *MockHelmClient_GetReleaseValues_Call) RunAndReturn(run func(string, bo return _c } -// InstallChart provides a mock function with given fields: ctx, spec, opts -func (_m *MockHelmClient) InstallChart(ctx context.Context, spec *helmclient.ChartSpec, opts *helmclient.GenericHelmOptions) (*release.Release, error) { - ret := _m.Called(ctx, spec, opts) +// InstallChart provides a mock function with given fields: ctx, spec +func (_m *MockHelmClient) InstallChart(ctx context.Context, spec *client.ChartSpec) (*release.Release, error) { + ret := _m.Called(ctx, spec) var r0 *release.Release var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *helmclient.ChartSpec, *helmclient.GenericHelmOptions) (*release.Release, error)); ok { - return rf(ctx, spec, opts) + if rf, ok := ret.Get(0).(func(context.Context, *client.ChartSpec) (*release.Release, error)); ok { + return rf(ctx, spec) } - if rf, ok := ret.Get(0).(func(context.Context, *helmclient.ChartSpec, *helmclient.GenericHelmOptions) *release.Release); ok { - r0 = rf(ctx, spec, opts) + if rf, ok := ret.Get(0).(func(context.Context, *client.ChartSpec) *release.Release); ok { + r0 = rf(ctx, spec) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*release.Release) } } - if rf, ok := ret.Get(1).(func(context.Context, *helmclient.ChartSpec, *helmclient.GenericHelmOptions) error); ok { - r1 = rf(ctx, spec, opts) + if rf, ok := ret.Get(1).(func(context.Context, *client.ChartSpec) error); ok { + r1 = rf(ctx, spec) } else { r1 = ret.Error(1) } @@ -276,15 +231,14 @@ type MockHelmClient_InstallChart_Call struct { // InstallChart is a helper method to define mock.On call // - ctx context.Context -// - spec *helmclient.ChartSpec -// - opts *helmclient.GenericHelmOptions -func (_e *MockHelmClient_Expecter) InstallChart(ctx interface{}, spec interface{}, opts interface{}) *MockHelmClient_InstallChart_Call { - return &MockHelmClient_InstallChart_Call{Call: _e.mock.On("InstallChart", ctx, spec, opts)} +// - spec *client.ChartSpec +func (_e *MockHelmClient_Expecter) InstallChart(ctx interface{}, spec interface{}) *MockHelmClient_InstallChart_Call { + return &MockHelmClient_InstallChart_Call{Call: _e.mock.On("InstallChart", ctx, spec)} } -func (_c *MockHelmClient_InstallChart_Call) Run(run func(ctx context.Context, spec *helmclient.ChartSpec, opts *helmclient.GenericHelmOptions)) *MockHelmClient_InstallChart_Call { +func (_c *MockHelmClient_InstallChart_Call) Run(run func(ctx context.Context, spec *client.ChartSpec)) *MockHelmClient_InstallChart_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*helmclient.ChartSpec), args[2].(*helmclient.GenericHelmOptions)) + run(args[0].(context.Context), args[1].(*client.ChartSpec)) }) return _c } @@ -294,30 +248,30 @@ func (_c *MockHelmClient_InstallChart_Call) Return(_a0 *release.Release, _a1 err return _c } -func (_c *MockHelmClient_InstallChart_Call) RunAndReturn(run func(context.Context, *helmclient.ChartSpec, *helmclient.GenericHelmOptions) (*release.Release, error)) *MockHelmClient_InstallChart_Call { +func (_c *MockHelmClient_InstallChart_Call) RunAndReturn(run func(context.Context, *client.ChartSpec) (*release.Release, error)) *MockHelmClient_InstallChart_Call { _c.Call.Return(run) return _c } -// InstallOrUpgradeChart provides a mock function with given fields: ctx, spec, opts -func (_m *MockHelmClient) InstallOrUpgradeChart(ctx context.Context, spec *helmclient.ChartSpec, opts *helmclient.GenericHelmOptions) (*release.Release, error) { - ret := _m.Called(ctx, spec, opts) +// InstallOrUpgradeChart provides a mock function with given fields: ctx, spec +func (_m *MockHelmClient) InstallOrUpgradeChart(ctx context.Context, spec *client.ChartSpec) (*release.Release, error) { + ret := _m.Called(ctx, spec) var r0 *release.Release var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *helmclient.ChartSpec, *helmclient.GenericHelmOptions) (*release.Release, error)); ok { - return rf(ctx, spec, opts) + if rf, ok := ret.Get(0).(func(context.Context, *client.ChartSpec) (*release.Release, error)); ok { + return rf(ctx, spec) } - if rf, ok := ret.Get(0).(func(context.Context, *helmclient.ChartSpec, *helmclient.GenericHelmOptions) *release.Release); ok { - r0 = rf(ctx, spec, opts) + if rf, ok := ret.Get(0).(func(context.Context, *client.ChartSpec) *release.Release); ok { + r0 = rf(ctx, spec) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*release.Release) } } - if rf, ok := ret.Get(1).(func(context.Context, *helmclient.ChartSpec, *helmclient.GenericHelmOptions) error); ok { - r1 = rf(ctx, spec, opts) + if rf, ok := ret.Get(1).(func(context.Context, *client.ChartSpec) error); ok { + r1 = rf(ctx, spec) } else { r1 = ret.Error(1) } @@ -332,15 +286,14 @@ type MockHelmClient_InstallOrUpgradeChart_Call struct { // InstallOrUpgradeChart is a helper method to define mock.On call // - ctx context.Context -// - spec *helmclient.ChartSpec -// - opts *helmclient.GenericHelmOptions -func (_e *MockHelmClient_Expecter) InstallOrUpgradeChart(ctx interface{}, spec interface{}, opts interface{}) *MockHelmClient_InstallOrUpgradeChart_Call { - return &MockHelmClient_InstallOrUpgradeChart_Call{Call: _e.mock.On("InstallOrUpgradeChart", ctx, spec, opts)} +// - spec *client.ChartSpec +func (_e *MockHelmClient_Expecter) InstallOrUpgradeChart(ctx interface{}, spec interface{}) *MockHelmClient_InstallOrUpgradeChart_Call { + return &MockHelmClient_InstallOrUpgradeChart_Call{Call: _e.mock.On("InstallOrUpgradeChart", ctx, spec)} } -func (_c *MockHelmClient_InstallOrUpgradeChart_Call) Run(run func(ctx context.Context, spec *helmclient.ChartSpec, opts *helmclient.GenericHelmOptions)) *MockHelmClient_InstallOrUpgradeChart_Call { +func (_c *MockHelmClient_InstallOrUpgradeChart_Call) Run(run func(ctx context.Context, spec *client.ChartSpec)) *MockHelmClient_InstallOrUpgradeChart_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*helmclient.ChartSpec), args[2].(*helmclient.GenericHelmOptions)) + run(args[0].(context.Context), args[1].(*client.ChartSpec)) }) return _c } @@ -350,49 +303,7 @@ func (_c *MockHelmClient_InstallOrUpgradeChart_Call) Return(_a0 *release.Release return _c } -func (_c *MockHelmClient_InstallOrUpgradeChart_Call) RunAndReturn(run func(context.Context, *helmclient.ChartSpec, *helmclient.GenericHelmOptions) (*release.Release, error)) *MockHelmClient_InstallOrUpgradeChart_Call { - _c.Call.Return(run) - return _c -} - -// LintChart provides a mock function with given fields: spec -func (_m *MockHelmClient) LintChart(spec *helmclient.ChartSpec) error { - ret := _m.Called(spec) - - var r0 error - if rf, ok := ret.Get(0).(func(*helmclient.ChartSpec) error); ok { - r0 = rf(spec) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockHelmClient_LintChart_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LintChart' -type MockHelmClient_LintChart_Call struct { - *mock.Call -} - -// LintChart is a helper method to define mock.On call -// - spec *helmclient.ChartSpec -func (_e *MockHelmClient_Expecter) LintChart(spec interface{}) *MockHelmClient_LintChart_Call { - return &MockHelmClient_LintChart_Call{Call: _e.mock.On("LintChart", spec)} -} - -func (_c *MockHelmClient_LintChart_Call) Run(run func(spec *helmclient.ChartSpec)) *MockHelmClient_LintChart_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(*helmclient.ChartSpec)) - }) - return _c -} - -func (_c *MockHelmClient_LintChart_Call) Return(_a0 error) *MockHelmClient_LintChart_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockHelmClient_LintChart_Call) RunAndReturn(run func(*helmclient.ChartSpec) error) *MockHelmClient_LintChart_Call { +func (_c *MockHelmClient_InstallOrUpgradeChart_Call) RunAndReturn(run func(context.Context, *client.ChartSpec) (*release.Release, error)) *MockHelmClient_InstallOrUpgradeChart_Call { _c.Call.Return(run) return _c } @@ -450,61 +361,6 @@ func (_c *MockHelmClient_ListDeployedReleases_Call) RunAndReturn(run func() ([]* return _c } -// ListReleaseHistory provides a mock function with given fields: name, max -func (_m *MockHelmClient) ListReleaseHistory(name string, max int) ([]*release.Release, error) { - ret := _m.Called(name, max) - - var r0 []*release.Release - var r1 error - if rf, ok := ret.Get(0).(func(string, int) ([]*release.Release, error)); ok { - return rf(name, max) - } - if rf, ok := ret.Get(0).(func(string, int) []*release.Release); ok { - r0 = rf(name, max) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]*release.Release) - } - } - - if rf, ok := ret.Get(1).(func(string, int) error); ok { - r1 = rf(name, max) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockHelmClient_ListReleaseHistory_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListReleaseHistory' -type MockHelmClient_ListReleaseHistory_Call struct { - *mock.Call -} - -// ListReleaseHistory is a helper method to define mock.On call -// - name string -// - max int -func (_e *MockHelmClient_Expecter) ListReleaseHistory(name interface{}, max interface{}) *MockHelmClient_ListReleaseHistory_Call { - return &MockHelmClient_ListReleaseHistory_Call{Call: _e.mock.On("ListReleaseHistory", name, max)} -} - -func (_c *MockHelmClient_ListReleaseHistory_Call) Run(run func(name string, max int)) *MockHelmClient_ListReleaseHistory_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(int)) - }) - return _c -} - -func (_c *MockHelmClient_ListReleaseHistory_Call) Return(_a0 []*release.Release, _a1 error) *MockHelmClient_ListReleaseHistory_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockHelmClient_ListReleaseHistory_Call) RunAndReturn(run func(string, int) ([]*release.Release, error)) *MockHelmClient_ListReleaseHistory_Call { - _c.Call.Return(run) - return _c -} - // ListReleasesByStateMask provides a mock function with given fields: _a0 func (_m *MockHelmClient) ListReleasesByStateMask(_a0 action.ListStates) ([]*release.Release, error) { ret := _m.Called(_a0) @@ -560,11 +416,11 @@ func (_c *MockHelmClient_ListReleasesByStateMask_Call) RunAndReturn(run func(act } // RollbackRelease provides a mock function with given fields: spec -func (_m *MockHelmClient) RollbackRelease(spec *helmclient.ChartSpec) error { +func (_m *MockHelmClient) RollbackRelease(spec *client.ChartSpec) error { ret := _m.Called(spec) var r0 error - if rf, ok := ret.Get(0).(func(*helmclient.ChartSpec) error); ok { + if rf, ok := ret.Get(0).(func(*client.ChartSpec) error); ok { r0 = rf(spec) } else { r0 = ret.Error(0) @@ -579,14 +435,14 @@ type MockHelmClient_RollbackRelease_Call struct { } // RollbackRelease is a helper method to define mock.On call -// - spec *helmclient.ChartSpec +// - spec *client.ChartSpec func (_e *MockHelmClient_Expecter) RollbackRelease(spec interface{}) *MockHelmClient_RollbackRelease_Call { return &MockHelmClient_RollbackRelease_Call{Call: _e.mock.On("RollbackRelease", spec)} } -func (_c *MockHelmClient_RollbackRelease_Call) Run(run func(spec *helmclient.ChartSpec)) *MockHelmClient_RollbackRelease_Call { +func (_c *MockHelmClient_RollbackRelease_Call) Run(run func(spec *client.ChartSpec)) *MockHelmClient_RollbackRelease_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(*helmclient.ChartSpec)) + run(args[0].(*client.ChartSpec)) }) return _c } @@ -596,63 +452,30 @@ func (_c *MockHelmClient_RollbackRelease_Call) Return(_a0 error) *MockHelmClient return _c } -func (_c *MockHelmClient_RollbackRelease_Call) RunAndReturn(run func(*helmclient.ChartSpec) error) *MockHelmClient_RollbackRelease_Call { - _c.Call.Return(run) - return _c -} - -// SetDebugLog provides a mock function with given fields: debugLog -func (_m *MockHelmClient) SetDebugLog(debugLog action.DebugLog) { - _m.Called(debugLog) -} - -// MockHelmClient_SetDebugLog_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetDebugLog' -type MockHelmClient_SetDebugLog_Call struct { - *mock.Call -} - -// SetDebugLog is a helper method to define mock.On call -// - debugLog action.DebugLog -func (_e *MockHelmClient_Expecter) SetDebugLog(debugLog interface{}) *MockHelmClient_SetDebugLog_Call { - return &MockHelmClient_SetDebugLog_Call{Call: _e.mock.On("SetDebugLog", debugLog)} -} - -func (_c *MockHelmClient_SetDebugLog_Call) Run(run func(debugLog action.DebugLog)) *MockHelmClient_SetDebugLog_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(action.DebugLog)) - }) - return _c -} - -func (_c *MockHelmClient_SetDebugLog_Call) Return() *MockHelmClient_SetDebugLog_Call { - _c.Call.Return() - return _c -} - -func (_c *MockHelmClient_SetDebugLog_Call) RunAndReturn(run func(action.DebugLog)) *MockHelmClient_SetDebugLog_Call { +func (_c *MockHelmClient_RollbackRelease_Call) RunAndReturn(run func(*client.ChartSpec) error) *MockHelmClient_RollbackRelease_Call { _c.Call.Return(run) return _c } -// TemplateChart provides a mock function with given fields: spec, options -func (_m *MockHelmClient) TemplateChart(spec *helmclient.ChartSpec, options *helmclient.HelmTemplateOptions) ([]byte, error) { - ret := _m.Called(spec, options) +// Tags provides a mock function with given fields: ref +func (_m *MockHelmClient) Tags(ref string) ([]string, error) { + ret := _m.Called(ref) - var r0 []byte + var r0 []string var r1 error - if rf, ok := ret.Get(0).(func(*helmclient.ChartSpec, *helmclient.HelmTemplateOptions) ([]byte, error)); ok { - return rf(spec, options) + if rf, ok := ret.Get(0).(func(string) ([]string, error)); ok { + return rf(ref) } - if rf, ok := ret.Get(0).(func(*helmclient.ChartSpec, *helmclient.HelmTemplateOptions) []byte); ok { - r0 = rf(spec, options) + if rf, ok := ret.Get(0).(func(string) []string); ok { + r0 = rf(ref) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) + r0 = ret.Get(0).([]string) } } - if rf, ok := ret.Get(1).(func(*helmclient.ChartSpec, *helmclient.HelmTemplateOptions) error); ok { - r1 = rf(spec, options) + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(ref) } else { r1 = ret.Error(1) } @@ -660,41 +483,40 @@ func (_m *MockHelmClient) TemplateChart(spec *helmclient.ChartSpec, options *hel return r0, r1 } -// MockHelmClient_TemplateChart_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TemplateChart' -type MockHelmClient_TemplateChart_Call struct { +// MockHelmClient_Tags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Tags' +type MockHelmClient_Tags_Call struct { *mock.Call } -// TemplateChart is a helper method to define mock.On call -// - spec *helmclient.ChartSpec -// - options *helmclient.HelmTemplateOptions -func (_e *MockHelmClient_Expecter) TemplateChart(spec interface{}, options interface{}) *MockHelmClient_TemplateChart_Call { - return &MockHelmClient_TemplateChart_Call{Call: _e.mock.On("TemplateChart", spec, options)} +// Tags is a helper method to define mock.On call +// - ref string +func (_e *MockHelmClient_Expecter) Tags(ref interface{}) *MockHelmClient_Tags_Call { + return &MockHelmClient_Tags_Call{Call: _e.mock.On("Tags", ref)} } -func (_c *MockHelmClient_TemplateChart_Call) Run(run func(spec *helmclient.ChartSpec, options *helmclient.HelmTemplateOptions)) *MockHelmClient_TemplateChart_Call { +func (_c *MockHelmClient_Tags_Call) Run(run func(ref string)) *MockHelmClient_Tags_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(*helmclient.ChartSpec), args[1].(*helmclient.HelmTemplateOptions)) + run(args[0].(string)) }) return _c } -func (_c *MockHelmClient_TemplateChart_Call) Return(_a0 []byte, _a1 error) *MockHelmClient_TemplateChart_Call { +func (_c *MockHelmClient_Tags_Call) Return(_a0 []string, _a1 error) *MockHelmClient_Tags_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *MockHelmClient_TemplateChart_Call) RunAndReturn(run func(*helmclient.ChartSpec, *helmclient.HelmTemplateOptions) ([]byte, error)) *MockHelmClient_TemplateChart_Call { +func (_c *MockHelmClient_Tags_Call) RunAndReturn(run func(string) ([]string, error)) *MockHelmClient_Tags_Call { _c.Call.Return(run) return _c } // UninstallRelease provides a mock function with given fields: spec -func (_m *MockHelmClient) UninstallRelease(spec *helmclient.ChartSpec) error { +func (_m *MockHelmClient) UninstallRelease(spec *client.ChartSpec) error { ret := _m.Called(spec) var r0 error - if rf, ok := ret.Get(0).(func(*helmclient.ChartSpec) error); ok { + if rf, ok := ret.Get(0).(func(*client.ChartSpec) error); ok { r0 = rf(spec) } else { r0 = ret.Error(0) @@ -709,14 +531,14 @@ type MockHelmClient_UninstallRelease_Call struct { } // UninstallRelease is a helper method to define mock.On call -// - spec *helmclient.ChartSpec +// - spec *client.ChartSpec func (_e *MockHelmClient_Expecter) UninstallRelease(spec interface{}) *MockHelmClient_UninstallRelease_Call { return &MockHelmClient_UninstallRelease_Call{Call: _e.mock.On("UninstallRelease", spec)} } -func (_c *MockHelmClient_UninstallRelease_Call) Run(run func(spec *helmclient.ChartSpec)) *MockHelmClient_UninstallRelease_Call { +func (_c *MockHelmClient_UninstallRelease_Call) Run(run func(spec *client.ChartSpec)) *MockHelmClient_UninstallRelease_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(*helmclient.ChartSpec)) + run(args[0].(*client.ChartSpec)) }) return _c } @@ -726,7 +548,7 @@ func (_c *MockHelmClient_UninstallRelease_Call) Return(_a0 error) *MockHelmClien return _c } -func (_c *MockHelmClient_UninstallRelease_Call) RunAndReturn(run func(*helmclient.ChartSpec) error) *MockHelmClient_UninstallRelease_Call { +func (_c *MockHelmClient_UninstallRelease_Call) RunAndReturn(run func(*client.ChartSpec) error) *MockHelmClient_UninstallRelease_Call { _c.Call.Return(run) return _c } @@ -773,66 +595,25 @@ func (_c *MockHelmClient_UninstallReleaseByName_Call) RunAndReturn(run func(stri return _c } -// UpdateChartRepos provides a mock function with given fields: -func (_m *MockHelmClient) UpdateChartRepos() error { - ret := _m.Called() - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// MockHelmClient_UpdateChartRepos_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateChartRepos' -type MockHelmClient_UpdateChartRepos_Call struct { - *mock.Call -} - -// UpdateChartRepos is a helper method to define mock.On call -func (_e *MockHelmClient_Expecter) UpdateChartRepos() *MockHelmClient_UpdateChartRepos_Call { - return &MockHelmClient_UpdateChartRepos_Call{Call: _e.mock.On("UpdateChartRepos")} -} - -func (_c *MockHelmClient_UpdateChartRepos_Call) Run(run func()) *MockHelmClient_UpdateChartRepos_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockHelmClient_UpdateChartRepos_Call) Return(_a0 error) *MockHelmClient_UpdateChartRepos_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *MockHelmClient_UpdateChartRepos_Call) RunAndReturn(run func() error) *MockHelmClient_UpdateChartRepos_Call { - _c.Call.Return(run) - return _c -} - -// UpgradeChart provides a mock function with given fields: ctx, spec, opts -func (_m *MockHelmClient) UpgradeChart(ctx context.Context, spec *helmclient.ChartSpec, opts *helmclient.GenericHelmOptions) (*release.Release, error) { - ret := _m.Called(ctx, spec, opts) +// UpgradeChart provides a mock function with given fields: ctx, spec +func (_m *MockHelmClient) UpgradeChart(ctx context.Context, spec *client.ChartSpec) (*release.Release, error) { + ret := _m.Called(ctx, spec) var r0 *release.Release var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *helmclient.ChartSpec, *helmclient.GenericHelmOptions) (*release.Release, error)); ok { - return rf(ctx, spec, opts) + if rf, ok := ret.Get(0).(func(context.Context, *client.ChartSpec) (*release.Release, error)); ok { + return rf(ctx, spec) } - if rf, ok := ret.Get(0).(func(context.Context, *helmclient.ChartSpec, *helmclient.GenericHelmOptions) *release.Release); ok { - r0 = rf(ctx, spec, opts) + if rf, ok := ret.Get(0).(func(context.Context, *client.ChartSpec) *release.Release); ok { + r0 = rf(ctx, spec) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*release.Release) } } - if rf, ok := ret.Get(1).(func(context.Context, *helmclient.ChartSpec, *helmclient.GenericHelmOptions) error); ok { - r1 = rf(ctx, spec, opts) + if rf, ok := ret.Get(1).(func(context.Context, *client.ChartSpec) error); ok { + r1 = rf(ctx, spec) } else { r1 = ret.Error(1) } @@ -847,15 +628,14 @@ type MockHelmClient_UpgradeChart_Call struct { // UpgradeChart is a helper method to define mock.On call // - ctx context.Context -// - spec *helmclient.ChartSpec -// - opts *helmclient.GenericHelmOptions -func (_e *MockHelmClient_Expecter) UpgradeChart(ctx interface{}, spec interface{}, opts interface{}) *MockHelmClient_UpgradeChart_Call { - return &MockHelmClient_UpgradeChart_Call{Call: _e.mock.On("UpgradeChart", ctx, spec, opts)} +// - spec *client.ChartSpec +func (_e *MockHelmClient_Expecter) UpgradeChart(ctx interface{}, spec interface{}) *MockHelmClient_UpgradeChart_Call { + return &MockHelmClient_UpgradeChart_Call{Call: _e.mock.On("UpgradeChart", ctx, spec)} } -func (_c *MockHelmClient_UpgradeChart_Call) Run(run func(ctx context.Context, spec *helmclient.ChartSpec, opts *helmclient.GenericHelmOptions)) *MockHelmClient_UpgradeChart_Call { +func (_c *MockHelmClient_UpgradeChart_Call) Run(run func(ctx context.Context, spec *client.ChartSpec)) *MockHelmClient_UpgradeChart_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*helmclient.ChartSpec), args[2].(*helmclient.GenericHelmOptions)) + run(args[0].(context.Context), args[1].(*client.ChartSpec)) }) return _c } @@ -865,7 +645,7 @@ func (_c *MockHelmClient_UpgradeChart_Call) Return(_a0 *release.Release, _a1 err return _c } -func (_c *MockHelmClient_UpgradeChart_Call) RunAndReturn(run func(context.Context, *helmclient.ChartSpec, *helmclient.GenericHelmOptions) (*release.Release, error)) *MockHelmClient_UpgradeChart_Call { +func (_c *MockHelmClient_UpgradeChart_Call) RunAndReturn(run func(context.Context, *client.ChartSpec) (*release.Release, error)) *MockHelmClient_UpgradeChart_Call { _c.Call.Return(run) return _c } diff --git a/pkg/helm/mock_tagResolver_test.go b/pkg/helm/mock_tagResolver_test.go deleted file mode 100644 index c89cb7d..0000000 --- a/pkg/helm/mock_tagResolver_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// Code generated by mockery v2.20.0. DO NOT EDIT. - -package helm - -import mock "github.com/stretchr/testify/mock" - -// mockTagResolver is an autogenerated mock type for the tagResolver type -type mockTagResolver struct { - mock.Mock -} - -type mockTagResolver_Expecter struct { - mock *mock.Mock -} - -func (_m *mockTagResolver) EXPECT() *mockTagResolver_Expecter { - return &mockTagResolver_Expecter{mock: &_m.Mock} -} - -// Tags provides a mock function with given fields: ref -func (_m *mockTagResolver) Tags(ref string) ([]string, error) { - ret := _m.Called(ref) - - var r0 []string - var r1 error - if rf, ok := ret.Get(0).(func(string) ([]string, error)); ok { - return rf(ref) - } - if rf, ok := ret.Get(0).(func(string) []string); ok { - r0 = rf(ref) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(ref) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockTagResolver_Tags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Tags' -type mockTagResolver_Tags_Call struct { - *mock.Call -} - -// Tags is a helper method to define mock.On call -// - ref string -func (_e *mockTagResolver_Expecter) Tags(ref interface{}) *mockTagResolver_Tags_Call { - return &mockTagResolver_Tags_Call{Call: _e.mock.On("Tags", ref)} -} - -func (_c *mockTagResolver_Tags_Call) Run(run func(ref string)) *mockTagResolver_Tags_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string)) - }) - return _c -} - -func (_c *mockTagResolver_Tags_Call) Return(_a0 []string, _a1 error) *mockTagResolver_Tags_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockTagResolver_Tags_Call) RunAndReturn(run func(string) ([]string, error)) *mockTagResolver_Tags_Call { - _c.Call.Return(run) - return _c -} - -type mockConstructorTestingTnewMockTagResolver interface { - mock.TestingT - Cleanup(func()) -} - -// newMockTagResolver creates a new instance of mockTagResolver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func newMockTagResolver(t mockConstructorTestingTnewMockTagResolver) *mockTagResolver { - mock := &mockTagResolver{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -}