From 999dc470109866b0c5d3b54013b46440ac8e2861 Mon Sep 17 00:00:00 2001 From: Xavi Garcia Date: Fri, 2 Feb 2024 18:26:42 +0100 Subject: [PATCH 1/5] Implements DependencyUpdate for helm charts A new flag `disableDependencyUpdate` is added to the `fleet.yaml` file in order to disable the feature, which is active by default. A new package `helmupdater` is added to implement the dependencies update. The implementation is based on helm's when ussing the `--dependency-update` flag in the install command. Dependencies are applied to the bundle (upstream) to they're resolved already when applying downstream. Refers to: https://github.com/rancher/fleet/issues/1672 Signed-off-by: Xavi Garcia --- .github/workflows/e2e-ci.yml | 8 +- charts/fleet-crd/templates/crds.yaml | 16 ++ e2e/assets/deps-charts/gitrepo.yaml | 11 + .../deps-charts/no-fleet-yaml/Chart.yaml | 10 + .../no-fleet-yaml/templates/configmap.yaml | 7 + .../deps-charts/no-fleet-yaml/values.yaml | 1 + .../deps-charts/with-fleet-yaml/Chart.yaml | 10 + .../deps-charts/with-fleet-yaml/fleet.yaml | 8 + .../with-fleet-yaml/templates/configmap.yaml | 7 + .../deps-charts/with-fleet-yaml/values.yaml | 1 + e2e/single-cluster/helm_dependencies_test.go | 160 +++++++++++ integrationtests/cli/apply/apply_test.go | 251 ++++++++++++++++++ .../remote-chart-with-deps/fleet.yaml | 8 + .../simple-with-fleet-yaml-no-deps/Chart.yaml | 10 + .../simple-with-fleet-yaml-no-deps/fleet.yaml | 8 + .../templates/configmap.yaml | 7 + .../values.yaml | 1 + .../simple-with-fleet-yaml/Chart.yaml | 10 + .../simple-with-fleet-yaml/fleet.yaml | 8 + .../templates/configmap.yaml | 7 + .../simple-with-fleet-yaml/values.yaml | 1 + .../deps-charts/no-fleet-yaml/Chart.yaml | 10 + .../no-fleet-yaml/templates/configmap.yaml | 7 + .../deps-charts/no-fleet-yaml/values.yaml | 1 + .../fleet.yaml | 9 + .../remote-chart-with-deps/fleet.yaml | 8 + .../simple-with-fleet-yaml-no-deps/Chart.yaml | 10 + .../simple-with-fleet-yaml-no-deps/fleet.yaml | 8 + .../templates/configmap.yaml | 7 + .../values.yaml | 1 + .../simple-with-fleet-yaml/Chart.yaml | 10 + .../simple-with-fleet-yaml/fleet.yaml | 8 + .../templates/configmap.yaml | 7 + .../simple-with-fleet-yaml/values.yaml | 1 + .../helmrepository/deps-chart-1.0.0.tgz | Bin 0 -> 454 bytes .../helmrepository/deps-chart/Chart.yaml | 10 + .../deps-chart/templates/configmap.yaml | 7 + .../helmrepository/deps-chart/values.yaml | 1 + .../cli/assets/helmrepository/index.yaml | 11 + integrationtests/cli/helpers.go | 15 +- internal/bundlereader/loaddirectory.go | 14 +- internal/bundlereader/loaddirectory_test.go | 2 +- internal/bundlereader/resources.go | 11 +- internal/helmupdater/helmupdater.go | 64 +++++ internal/helmupdater/helmupdater_test.go | 101 +++++++ .../v1alpha1/bundledeployment_types.go | 3 + 46 files changed, 865 insertions(+), 11 deletions(-) create mode 100644 e2e/assets/deps-charts/gitrepo.yaml create mode 100644 e2e/assets/deps-charts/no-fleet-yaml/Chart.yaml create mode 100644 e2e/assets/deps-charts/no-fleet-yaml/templates/configmap.yaml create mode 100644 e2e/assets/deps-charts/no-fleet-yaml/values.yaml create mode 100644 e2e/assets/deps-charts/with-fleet-yaml/Chart.yaml create mode 100644 e2e/assets/deps-charts/with-fleet-yaml/fleet.yaml create mode 100644 e2e/assets/deps-charts/with-fleet-yaml/templates/configmap.yaml create mode 100644 e2e/assets/deps-charts/with-fleet-yaml/values.yaml create mode 100644 e2e/single-cluster/helm_dependencies_test.go create mode 100644 integrationtests/cli/assets/deps-charts/multi-chart/remote-chart-with-deps/fleet.yaml create mode 100644 integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml-no-deps/Chart.yaml create mode 100644 integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml-no-deps/fleet.yaml create mode 100644 integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml-no-deps/templates/configmap.yaml create mode 100644 integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml-no-deps/values.yaml create mode 100644 integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml/Chart.yaml create mode 100644 integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml/fleet.yaml create mode 100644 integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml/templates/configmap.yaml create mode 100644 integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml/values.yaml create mode 100644 integrationtests/cli/assets/deps-charts/no-fleet-yaml/Chart.yaml create mode 100644 integrationtests/cli/assets/deps-charts/no-fleet-yaml/templates/configmap.yaml create mode 100644 integrationtests/cli/assets/deps-charts/no-fleet-yaml/values.yaml create mode 100644 integrationtests/cli/assets/deps-charts/remote-chart-with-deps-disabled/fleet.yaml create mode 100644 integrationtests/cli/assets/deps-charts/remote-chart-with-deps/fleet.yaml create mode 100644 integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml-no-deps/Chart.yaml create mode 100644 integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml-no-deps/fleet.yaml create mode 100644 integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml-no-deps/templates/configmap.yaml create mode 100644 integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml-no-deps/values.yaml create mode 100644 integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml/Chart.yaml create mode 100644 integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml/fleet.yaml create mode 100644 integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml/templates/configmap.yaml create mode 100644 integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml/values.yaml create mode 100644 integrationtests/cli/assets/helmrepository/deps-chart-1.0.0.tgz create mode 100644 integrationtests/cli/assets/helmrepository/deps-chart/Chart.yaml create mode 100644 integrationtests/cli/assets/helmrepository/deps-chart/templates/configmap.yaml create mode 100644 integrationtests/cli/assets/helmrepository/deps-chart/values.yaml create mode 100644 internal/helmupdater/helmupdater.go create mode 100644 internal/helmupdater/helmupdater_test.go diff --git a/.github/workflows/e2e-ci.yml b/.github/workflows/e2e-ci.yml index c0f9ca90e7..c669c8fc6b 100644 --- a/.github/workflows/e2e-ci.yml +++ b/.github/workflows/e2e-ci.yml @@ -114,13 +114,17 @@ jobs: e2e/testenv/infra/infra setup --helm-registry=true ginkgo --label-filter='helm-registry' e2e/single-cluster + # 4. Run tests requiring a Helm registry and the git server + e2e/testenv/infra/infra setup --git-server=true + ginkgo --label-filter='infra-setup && helm-registry && !oci-registry' e2e/single-cluster/ + e2e/testenv/infra/infra teardown --helm-registry=true - # 4. Run tests requiring an OCI registry + # 5. Run tests requiring an OCI registry e2e/testenv/infra/infra setup --oci-registry=true ginkgo --label-filter='oci-registry' e2e/single-cluster - # 5. Tear down all infra + # 6. Tear down all infra e2e/testenv/infra/infra teardown - diff --git a/charts/fleet-crd/templates/crds.yaml b/charts/fleet-crd/templates/crds.yaml index ac30aacd1f..2977c20105 100644 --- a/charts/fleet-crd/templates/crds.yaml +++ b/charts/fleet-crd/templates/crds.yaml @@ -245,6 +245,10 @@ spec: description: DisableDNS can be used to customize Helm's EnableDNS option, which Fleet sets to `true` by default. type: boolean + disableDependencyUpdate: + description: DisableDependencyUpdate allows skipping chart + dependencies update + type: boolean disablePreProcess: description: DisablePreProcess disables template processing in values @@ -547,6 +551,10 @@ spec: description: DisableDNS can be used to customize Helm's EnableDNS option, which Fleet sets to `true` by default. type: boolean + disableDependencyUpdate: + description: DisableDependencyUpdate allows skipping chart + dependencies update + type: boolean disablePreProcess: description: DisablePreProcess disables template processing in values @@ -1239,6 +1247,10 @@ spec: description: DisableDNS can be used to customize Helm's EnableDNS option, which Fleet sets to `true` by default. type: boolean + disableDependencyUpdate: + description: DisableDependencyUpdate allows skipping chart dependencies + update + type: boolean disablePreProcess: description: DisablePreProcess disables template processing in values @@ -1934,6 +1946,10 @@ spec: description: DisableDNS can be used to customize Helm's EnableDNS option, which Fleet sets to `true` by default. type: boolean + disableDependencyUpdate: + description: DisableDependencyUpdate allows skipping chart + dependencies update + type: boolean disablePreProcess: description: DisablePreProcess disables template processing in values diff --git a/e2e/assets/deps-charts/gitrepo.yaml b/e2e/assets/deps-charts/gitrepo.yaml new file mode 100644 index 0000000000..a43265ecb7 --- /dev/null +++ b/e2e/assets/deps-charts/gitrepo.yaml @@ -0,0 +1,11 @@ +kind: GitRepo +apiVersion: fleet.cattle.io/v1alpha1 +metadata: + name: {{.Name}} +spec: + repo: {{.Repo}} + branch: {{.Branch}} + helmSecretName: "helm-secret" + targetNamespace: {{.TargetNamespace}} + paths: + - examples diff --git a/e2e/assets/deps-charts/no-fleet-yaml/Chart.yaml b/e2e/assets/deps-charts/no-fleet-yaml/Chart.yaml new file mode 100644 index 0000000000..893980b620 --- /dev/null +++ b/e2e/assets/deps-charts/no-fleet-yaml/Chart.yaml @@ -0,0 +1,10 @@ +apiVersion: v2 +appVersion: 1.16.0 +description: A chart for testing dependencies +name: deps-chart +type: application +version: 1.0.0 +dependencies: +- name: sleeper-chart + version: "0.1.0" + repository: {{.HelmRepoUrl}} diff --git a/e2e/assets/deps-charts/no-fleet-yaml/templates/configmap.yaml b/e2e/assets/deps-charts/no-fleet-yaml/templates/configmap.yaml new file mode 100644 index 0000000000..c6b509ce7b --- /dev/null +++ b/e2e/assets/deps-charts/no-fleet-yaml/templates/configmap.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-simple-deps-chart +data: + test: "valuedeps" + name: {{ .Values.name }} diff --git a/e2e/assets/deps-charts/no-fleet-yaml/values.yaml b/e2e/assets/deps-charts/no-fleet-yaml/values.yaml new file mode 100644 index 0000000000..1d9970923f --- /dev/null +++ b/e2e/assets/deps-charts/no-fleet-yaml/values.yaml @@ -0,0 +1 @@ +name: deps-default-name diff --git a/e2e/assets/deps-charts/with-fleet-yaml/Chart.yaml b/e2e/assets/deps-charts/with-fleet-yaml/Chart.yaml new file mode 100644 index 0000000000..893980b620 --- /dev/null +++ b/e2e/assets/deps-charts/with-fleet-yaml/Chart.yaml @@ -0,0 +1,10 @@ +apiVersion: v2 +appVersion: 1.16.0 +description: A chart for testing dependencies +name: deps-chart +type: application +version: 1.0.0 +dependencies: +- name: sleeper-chart + version: "0.1.0" + repository: {{.HelmRepoUrl}} diff --git a/e2e/assets/deps-charts/with-fleet-yaml/fleet.yaml b/e2e/assets/deps-charts/with-fleet-yaml/fleet.yaml new file mode 100644 index 0000000000..72225fa9b1 --- /dev/null +++ b/e2e/assets/deps-charts/with-fleet-yaml/fleet.yaml @@ -0,0 +1,8 @@ +namespace: {{.TestNamespace}} +helm: + releaseName: simple-with-fleet-yaml + chart: "" + repo: "" + version: "" + disableDependencyUpdate: {{.DisableDependencyUpdate}} + diff --git a/e2e/assets/deps-charts/with-fleet-yaml/templates/configmap.yaml b/e2e/assets/deps-charts/with-fleet-yaml/templates/configmap.yaml new file mode 100644 index 0000000000..c6b509ce7b --- /dev/null +++ b/e2e/assets/deps-charts/with-fleet-yaml/templates/configmap.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-simple-deps-chart +data: + test: "valuedeps" + name: {{ .Values.name }} diff --git a/e2e/assets/deps-charts/with-fleet-yaml/values.yaml b/e2e/assets/deps-charts/with-fleet-yaml/values.yaml new file mode 100644 index 0000000000..1d9970923f --- /dev/null +++ b/e2e/assets/deps-charts/with-fleet-yaml/values.yaml @@ -0,0 +1 @@ +name: deps-default-name diff --git a/e2e/single-cluster/helm_dependencies_test.go b/e2e/single-cluster/helm_dependencies_test.go new file mode 100644 index 0000000000..3cb697365c --- /dev/null +++ b/e2e/single-cluster/helm_dependencies_test.go @@ -0,0 +1,160 @@ +package singlecluster_test + +import ( + "fmt" + "math/rand" + "os" + "path" + "path/filepath" + "strings" + + "github.com/rancher/fleet/e2e/testenv" + "github.com/rancher/fleet/e2e/testenv/githelper" + "github.com/rancher/fleet/e2e/testenv/kubectl" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + cp "github.com/otiai10/copy" +) + +func getChartMuseumExternalAddr(env *testenv.Env) string { + username := os.Getenv("GIT_HTTP_USER") + passwd := os.Getenv("GIT_HTTP_PASSWORD") + Expect(username).ToNot(Equal("")) + Expect(passwd).ToNot(Equal("")) + return fmt.Sprintf("https://%s:%s@chartmuseum-service.%s.svc.cluster.local:8081", username, passwd, env.Namespace) +} + +func setupChartDepsInTmpDir(chartDir string, tmpDir string, namespace string, disableDependencyUpdate bool, env *testenv.Env) { + err := cp.Copy(chartDir, tmpDir) + Expect(err).ToNot(HaveOccurred()) + // replace the helm repo url + helmRepoUrl := getChartMuseumExternalAddr(env) + out := filepath.Join(tmpDir, "Chart.yaml") + in := filepath.Join(chartDir, "Chart.yaml") + err = testenv.Template(out, in, struct { + HelmRepoUrl string + }{ + helmRepoUrl, + }) + Expect(err).ToNot(HaveOccurred()) + + if _, err = os.Stat(filepath.Join(chartDir, "fleet.yaml")); !os.IsNotExist(err) { + out = filepath.Join(tmpDir, "fleet.yaml") + in = filepath.Join(chartDir, "fleet.yaml") + err = testenv.Template(out, in, struct { + TestNamespace string + DisableDependencyUpdate bool + }{ + namespace, + disableDependencyUpdate, + }) + Expect(err).ToNot(HaveOccurred()) + } +} + +var _ = Describe("Helm dependency update tests", Label("infra-setup", "helm-registry"), func() { + var ( + asset string + k kubectl.Command + gh *githelper.Git + clonedir string + inClusterRepoURL string + tmpDir string + gitrepoName string + r = rand.New(rand.NewSource(GinkgoRandomSeed())) + namespace string + disableDependencyUpdate bool + ) + + JustBeforeEach(func() { + k = env.Kubectl.Namespace(env.Namespace) + // Build git repo URL reachable _within_ the cluster, for the GitRepo + host, err := githelper.BuildGitHostname(env.Namespace) + Expect(err).ToNot(HaveOccurred()) + + addr, err := githelper.GetExternalRepoAddr(env, port, repoName) + Expect(err).ToNot(HaveOccurred()) + gh = githelper.NewHTTP(addr) + + inClusterRepoURL = gh.GetInClusterURL(host, port, repoName) + + tmpDir, _ = os.MkdirTemp("", "fleet-") + clonedir = path.Join(tmpDir, repoName) + + gitrepoName = testenv.RandomFilename("gitrepo-test", r) + + // setup the tmp chart dir. + // we use a tmp dir because dependencies are downloaded to the directory + tmpChart := GinkgoT().TempDir() + setupChartDepsInTmpDir(testenv.AssetPath(asset), tmpChart, namespace, disableDependencyUpdate, env) + + _, err = gh.Create(clonedir, tmpChart, "examples") + Expect(err).ToNot(HaveOccurred()) + + err = testenv.ApplyTemplate(k, testenv.AssetPath("deps-charts/gitrepo.yaml"), struct { + Name string + Repo string + Branch string + TargetNamespace string + }{ + gitrepoName, + inClusterRepoURL, + gh.Branch, + namespace, // to avoid conflicts with other tests + }) + Expect(err).ToNot(HaveOccurred()) + }) + + AfterEach(func() { + out, err := k.Delete("gitrepo", gitrepoName) + Expect(err).ToNot(HaveOccurred(), out) + out, err = k.Delete("ns", namespace) + Expect(err).ToNot(HaveOccurred(), out) + }) + + When("applying a gitrepo resource", func() { + Context("containing a helm chart with dependencies and no fleet.yaml", func() { + BeforeEach(func() { + namespace = "no-fleet-yaml" + asset = "deps-charts/" + namespace + disableDependencyUpdate = false + }) + It("deploys the chart plus its dependencies", func() { + Eventually(func() bool { + outConfigMaps, _ := k.Namespace(namespace).Get("configmaps") + outPods, _ := k.Namespace(namespace).Get("pods") + return strings.Contains(outConfigMaps, "test-simple-deps-chart") && strings.Contains(outPods, "sleeper-") + }).Should(BeTrue()) + }) + }) + Context("containing a helm chart with dependencies and fleet.yaml with disableDependencyUpdate=false", func() { + BeforeEach(func() { + namespace = "with-fleet-yaml" + asset = "deps-charts/" + namespace + disableDependencyUpdate = false + }) + It("deploys the chart plus its dependencies", func() { + Eventually(func() bool { + outConfigMaps, _ := k.Namespace(namespace).Get("configmaps") + outPods, _ := k.Namespace(namespace).Get("pods") + return strings.Contains(outConfigMaps, "test-simple-deps-chart") && strings.Contains(outPods, "sleeper-") + }).Should(BeTrue()) + }) + }) + Context("containing a helm chart with dependencies and fleet.yaml with disableDependencyUpdate=true", func() { + BeforeEach(func() { + namespace = "with-fleet-yaml" + asset = "deps-charts/" + namespace + disableDependencyUpdate = true + }) + It("deploys the chart, but not its dependencies", func() { + Eventually(func() bool { + outConfigMaps, _ := k.Namespace(namespace).Get("configmaps") + outPods, _ := k.Namespace(namespace).Get("pods") + return strings.Contains(outConfigMaps, "test-simple-deps-chart") && !strings.Contains(outPods, "sleeper-") + }).Should(BeTrue()) + }) + }) + }) +}) diff --git a/integrationtests/cli/apply/apply_test.go b/integrationtests/cli/apply/apply_test.go index db77994e79..e17cf2e510 100644 --- a/integrationtests/cli/apply/apply_test.go +++ b/integrationtests/cli/apply/apply_test.go @@ -1,11 +1,18 @@ package apply import ( + "os" + "path" + "path/filepath" + "strings" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + cp "github.com/otiai10/copy" "github.com/rancher/fleet/integrationtests/cli" "github.com/rancher/fleet/internal/cmd/cli/apply" + "github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1" ) var _ = Describe("Fleet apply", Ordered, func() { @@ -237,3 +244,247 @@ var _ = Describe("Fleet apply", Ordered, func() { }) }) }) + +var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func() { + + var ( + dirs []string + name string + options apply.Options + tmpDirRel string + tmpDir string + repo = repository{ + port: port, + } + ) + + JustBeforeEach(func() { + // start a fake helm repository + repo.startRepository(false) + tmpDir = GinkgoT().TempDir() + err := cp.Copy(path.Join(cli.AssetsPath, "deps-charts", name), tmpDir) + Expect(err).NotTo(HaveOccurred()) + // get the relative path because fleet apply needs a relative path + pwd, err := os.Getwd() + Expect(err).NotTo(HaveOccurred()) + tmpDirRel, err = filepath.Rel(pwd, tmpDir) + Expect(err).NotTo(HaveOccurred()) + dirs = []string{tmpDirRel} + err = fleetApply(name, dirs, options) + Expect(err).NotTo(HaveOccurred()) + }) + + When("folder contains helm chart with no fleet.yaml", func() { + BeforeEach(func() { + name = "no-fleet-yaml" + }) + + It("then a Bundle is created with all the resources, including the dependencies, and keepResources is false", func() { + Eventually(func() bool { + bundle, err := cli.GetBundleFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + Expect(len(bundle.Spec.Resources)).To(Equal(5)) + files, err := getAllFilesInDir(tmpDir) + Expect(err).NotTo(HaveOccurred()) + Expect(len(files)).To(Equal(len(bundle.Spec.Resources))) + for _, file := range files { + presentInBundleResources(file, bundle.Spec.Resources) + } + // explicitly check for dependency files + presentInBundleResources(path.Join(tmpDir, "Chart.lock"), bundle.Spec.Resources) + presentInBundleResources(path.Join(tmpDir, "charts/config-chart-0.1.0.tgz"), bundle.Spec.Resources) + + return !bundle.Spec.KeepResources + }).Should(BeTrue()) + }) + }) + + When("folder contains helm chart with fleet.yaml, disableDependencyUpdate is not set", func() { + BeforeEach(func() { + name = "simple-with-fleet-yaml" + }) + + It("then a Bundle is created with all the resources, including the dependencies, and keepResources is false", func() { + Eventually(func() bool { + bundle, err := cli.GetBundleFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + Expect(len(bundle.Spec.Resources)).To(Equal(6)) + files, err := getAllFilesInDir(tmpDirRel) + Expect(err).NotTo(HaveOccurred()) + Expect(len(files)).To(Equal(len(bundle.Spec.Resources))) + for _, file := range files { + presentInBundleResources(file, bundle.Spec.Resources) + } + // explicitly check for dependency files + presentInBundleResources(path.Join(tmpDirRel, "Chart.lock"), bundle.Spec.Resources) + presentInBundleResources(path.Join(tmpDirRel, "charts/config-chart-0.1.0.tgz"), bundle.Spec.Resources) + return !bundle.Spec.KeepResources + }).Should(BeTrue()) + }) + }) + + When("folder contains helm chart with fleet.yaml, disableDependencyUpdate is set to true", func() { + BeforeEach(func() { + name = "simple-with-fleet-yaml-no-deps" + }) + + It("then a Bundle is created with all the resources, dependencies should not be in the bundle", func() { + Eventually(func() bool { + bundle, err := cli.GetBundleFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + Expect(len(bundle.Spec.Resources)).To(Equal(4)) + files, err := getAllFilesInDir(tmpDirRel) + Expect(err).NotTo(HaveOccurred()) + Expect(len(files)).To(Equal(len(bundle.Spec.Resources))) + for _, file := range files { + presentInBundleResources(file, bundle.Spec.Resources) + } + // explicitly check for dependency files (they should not exist) + notPresentInBundleResources(path.Join(tmpDirRel, "Chart.lock"), bundle.Spec.Resources) + notPresentInBundleResources(path.Join(tmpDirRel, "charts/config-chart-0.1.0.tgz"), bundle.Spec.Resources) + return !bundle.Spec.KeepResources + }).Should(BeTrue()) + }) + }) + + When("folder contains fleet.yaml defining a remote chart which has dependencies", func() { + BeforeEach(func() { + name = "remote-chart-with-deps" + }) + + It("then a Bundle is created with all the resources, dependencies should be in the bundle", func() { + Eventually(func() bool { + bundle, err := cli.GetBundleFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + Expect(len(bundle.Spec.Resources)).To(Equal(6)) + presentInBundleResources(path.Join(tmpDirRel, "fleet.yaml"), bundle.Spec.Resources) + // as files were unpacked from the downloaded chart we can't just + // list the files in the original folder and compare. + // Files are only located in the bundle resources + onlyPresentInBundleResources("Chart.yaml", bundle.Spec.Resources) + onlyPresentInBundleResources("values.yaml", bundle.Spec.Resources) + onlyPresentInBundleResources("templates/configmap.yaml", bundle.Spec.Resources) + onlyPresentInBundleResources("Chart.lock", bundle.Spec.Resources) + onlyPresentInBundleResources("charts/config-chart-0.1.0.tgz", bundle.Spec.Resources) + return !bundle.Spec.KeepResources + }).Should(BeTrue()) + }) + }) + + When("folder contains fleet.yaml defining a remote chart which has dependencies, and disableDependencyUpdate is set", func() { + BeforeEach(func() { + name = "remote-chart-with-deps-disabled" + }) + + It("then a Bundle is created with all the resources, dependencies should not be in the bundle", func() { + Eventually(func() bool { + bundle, err := cli.GetBundleFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + Expect(len(bundle.Spec.Resources)).To(Equal(4)) + presentInBundleResources(path.Join(tmpDirRel, "fleet.yaml"), bundle.Spec.Resources) + // as files were unpacked from the downloaded chart we can't just + // list the files in the original folder and compare. + // Files are only located in the bundle resources + onlyPresentInBundleResources("Chart.yaml", bundle.Spec.Resources) + onlyPresentInBundleResources("values.yaml", bundle.Spec.Resources) + onlyPresentInBundleResources("templates/configmap.yaml", bundle.Spec.Resources) + return !bundle.Spec.KeepResources + }).Should(BeTrue()) + }) + }) + + When("folder contains multiple charts with different options", func() { + BeforeEach(func() { + name = "multi-chart" + }) + + It("then Bundles are created with the corresponding resources, depending if they should update dependencies", func() { + Eventually(func() bool { + bundle, err := cli.GetBundleListFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + Expect(len(bundle)).To(Equal(3)) + deploymentA := bundle[0] + deploymentB := bundle[1] + deploymentC := bundle[2] + + // deploymentA corresponds to multi-chart/remote-chart-with-deps + Expect(len(deploymentA.Spec.Resources)).To(Equal(6)) + presentInBundleResources(path.Join(tmpDirRel, "remote-chart-with-deps", "fleet.yaml"), deploymentA.Spec.Resources) + // as files were unpacked from the downloaded chart we can't just + // list the files in the original folder and compare. + // Files are only located in the bundle resources + onlyPresentInBundleResources("Chart.yaml", deploymentA.Spec.Resources) + onlyPresentInBundleResources("values.yaml", deploymentA.Spec.Resources) + onlyPresentInBundleResources("templates/configmap.yaml", deploymentA.Spec.Resources) + onlyPresentInBundleResources("Chart.lock", deploymentA.Spec.Resources) + onlyPresentInBundleResources("charts/config-chart-0.1.0.tgz", deploymentA.Spec.Resources) + + // deploymentB corresponds to multi-chart/simple-with-fleet-yaml + Expect(len(deploymentB.Spec.Resources)).To(Equal(6)) + files, err := getAllFilesInDir(path.Join(tmpDirRel, "simple-with-fleet-yaml")) + Expect(err).NotTo(HaveOccurred()) + Expect(len(files)).To(Equal(len(deploymentB.Spec.Resources))) + for _, file := range files { + presentInBundleResources(file, deploymentB.Spec.Resources) + } + // explicitly check for dependency files + presentInBundleResources(path.Join(tmpDirRel, "simple-with-fleet-yaml", "Chart.lock"), deploymentB.Spec.Resources) + presentInBundleResources(path.Join(tmpDirRel, "simple-with-fleet-yaml", "charts/config-chart-0.1.0.tgz"), deploymentB.Spec.Resources) + + // deploymentC corresponds to multi-char/simple-with-fleet-yaml-no-deps + Expect(len(deploymentC.Spec.Resources)).To(Equal(4)) + files, err = getAllFilesInDir(path.Join(tmpDirRel, "simple-with-fleet-yaml-no-deps")) + Expect(err).NotTo(HaveOccurred()) + Expect(len(files)).To(Equal(len(deploymentC.Spec.Resources))) + for _, file := range files { + presentInBundleResources(file, deploymentC.Spec.Resources) + } + // explicitly check for dependency files (they should not exist) + notPresentInBundleResources(path.Join(tmpDirRel, "simple-with-fleet-yaml-no-deps", "Chart.lock"), deploymentC.Spec.Resources) + notPresentInBundleResources(path.Join(tmpDirRel, "simple-with-fleet-yaml-no-deps", "charts/config-chart-0.1.0.tgz"), deploymentC.Spec.Resources) + return !deploymentA.Spec.KeepResources && !deploymentB.Spec.KeepResources && !deploymentC.Spec.KeepResources + }).Should(BeTrue()) + }) + }) + + AfterEach(func() { + err := repo.stopRepository() + Expect(err).NotTo(HaveOccurred()) + }) +}) + +func presentInBundleResources(path string, resources []v1alpha1.BundleResource) { + isPresent, err := cli.IsResourcePresentInBundle(path, resources) + Expect(err).NotTo(HaveOccurred()) + Expect(isPresent).Should(BeTrue()) +} + +func onlyPresentInBundleResources(path string, resources []v1alpha1.BundleResource) { + found := false + for _, resource := range resources { + if strings.HasSuffix(resource.Name, path) { + found = true + } + } + Expect(found).Should(BeTrue()) +} + +func notPresentInBundleResources(path string, resources []v1alpha1.BundleResource) { + isPresent, err := cli.IsResourcePresentInBundle(path, resources) + Expect(err).To(HaveOccurred()) + Expect(isPresent).Should(BeFalse()) +} + +func getAllFilesInDir(chartPath string) ([]string, error) { + var files []string + err := filepath.Walk(chartPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + files = append(files, path) + } + return nil + }) + return files, err +} diff --git a/integrationtests/cli/assets/deps-charts/multi-chart/remote-chart-with-deps/fleet.yaml b/integrationtests/cli/assets/deps-charts/multi-chart/remote-chart-with-deps/fleet.yaml new file mode 100644 index 0000000000..116fbbf3cc --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/multi-chart/remote-chart-with-deps/fleet.yaml @@ -0,0 +1,8 @@ +namespace: test-deps-helm-yamls-with-fleet +helm: + releaseName: simple-with-fleet-yaml + chart: deps-chart + repo: http://localhost:3000 + version: 1.0.0 + force: false + diff --git a/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml-no-deps/Chart.yaml b/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml-no-deps/Chart.yaml new file mode 100644 index 0000000000..52dc058c1c --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml-no-deps/Chart.yaml @@ -0,0 +1,10 @@ +apiVersion: v2 +appVersion: 1.16.0 +description: A chart for testing dependencies +name: deps-chart +type: application +version: 1.0.0 +dependencies: +- name: config-chart + version: "0.1.0" + repository: "http://localhost:3000" diff --git a/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml-no-deps/fleet.yaml b/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml-no-deps/fleet.yaml new file mode 100644 index 0000000000..a3d77664e0 --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml-no-deps/fleet.yaml @@ -0,0 +1,8 @@ +namespace: test-deps-helm-yamls-with-fleet +helm: + releaseName: simple-with-fleet-yaml-no-deps + chart: "" + repo: "" + version: "" + disableDependencyUpdate: true + diff --git a/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml-no-deps/templates/configmap.yaml b/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml-no-deps/templates/configmap.yaml new file mode 100644 index 0000000000..c6b509ce7b --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml-no-deps/templates/configmap.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-simple-deps-chart +data: + test: "valuedeps" + name: {{ .Values.name }} diff --git a/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml-no-deps/values.yaml b/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml-no-deps/values.yaml new file mode 100644 index 0000000000..1d9970923f --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml-no-deps/values.yaml @@ -0,0 +1 @@ +name: deps-default-name diff --git a/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml/Chart.yaml b/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml/Chart.yaml new file mode 100644 index 0000000000..52dc058c1c --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml/Chart.yaml @@ -0,0 +1,10 @@ +apiVersion: v2 +appVersion: 1.16.0 +description: A chart for testing dependencies +name: deps-chart +type: application +version: 1.0.0 +dependencies: +- name: config-chart + version: "0.1.0" + repository: "http://localhost:3000" diff --git a/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml/fleet.yaml b/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml/fleet.yaml new file mode 100644 index 0000000000..74c08e0abc --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml/fleet.yaml @@ -0,0 +1,8 @@ +namespace: test-deps-helm-yamls-with-fleet +helm: + releaseName: simple-with-fleet-yaml + chart: "" + repo: "" + version: "" + force: false + diff --git a/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml/templates/configmap.yaml b/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml/templates/configmap.yaml new file mode 100644 index 0000000000..c6b509ce7b --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml/templates/configmap.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-simple-deps-chart +data: + test: "valuedeps" + name: {{ .Values.name }} diff --git a/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml/values.yaml b/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml/values.yaml new file mode 100644 index 0000000000..1d9970923f --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/multi-chart/simple-with-fleet-yaml/values.yaml @@ -0,0 +1 @@ +name: deps-default-name diff --git a/integrationtests/cli/assets/deps-charts/no-fleet-yaml/Chart.yaml b/integrationtests/cli/assets/deps-charts/no-fleet-yaml/Chart.yaml new file mode 100644 index 0000000000..52dc058c1c --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/no-fleet-yaml/Chart.yaml @@ -0,0 +1,10 @@ +apiVersion: v2 +appVersion: 1.16.0 +description: A chart for testing dependencies +name: deps-chart +type: application +version: 1.0.0 +dependencies: +- name: config-chart + version: "0.1.0" + repository: "http://localhost:3000" diff --git a/integrationtests/cli/assets/deps-charts/no-fleet-yaml/templates/configmap.yaml b/integrationtests/cli/assets/deps-charts/no-fleet-yaml/templates/configmap.yaml new file mode 100644 index 0000000000..c6b509ce7b --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/no-fleet-yaml/templates/configmap.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-simple-deps-chart +data: + test: "valuedeps" + name: {{ .Values.name }} diff --git a/integrationtests/cli/assets/deps-charts/no-fleet-yaml/values.yaml b/integrationtests/cli/assets/deps-charts/no-fleet-yaml/values.yaml new file mode 100644 index 0000000000..1d9970923f --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/no-fleet-yaml/values.yaml @@ -0,0 +1 @@ +name: deps-default-name diff --git a/integrationtests/cli/assets/deps-charts/remote-chart-with-deps-disabled/fleet.yaml b/integrationtests/cli/assets/deps-charts/remote-chart-with-deps-disabled/fleet.yaml new file mode 100644 index 0000000000..ccd2f60cbf --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/remote-chart-with-deps-disabled/fleet.yaml @@ -0,0 +1,9 @@ +namespace: test-deps-helm-yamls-with-fleet +helm: + releaseName: simple-with-fleet-yaml + chart: deps-chart + repo: http://localhost:3000 + version: 1.0.0 + force: false + disableDependencyUpdate: true + diff --git a/integrationtests/cli/assets/deps-charts/remote-chart-with-deps/fleet.yaml b/integrationtests/cli/assets/deps-charts/remote-chart-with-deps/fleet.yaml new file mode 100644 index 0000000000..116fbbf3cc --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/remote-chart-with-deps/fleet.yaml @@ -0,0 +1,8 @@ +namespace: test-deps-helm-yamls-with-fleet +helm: + releaseName: simple-with-fleet-yaml + chart: deps-chart + repo: http://localhost:3000 + version: 1.0.0 + force: false + diff --git a/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml-no-deps/Chart.yaml b/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml-no-deps/Chart.yaml new file mode 100644 index 0000000000..52dc058c1c --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml-no-deps/Chart.yaml @@ -0,0 +1,10 @@ +apiVersion: v2 +appVersion: 1.16.0 +description: A chart for testing dependencies +name: deps-chart +type: application +version: 1.0.0 +dependencies: +- name: config-chart + version: "0.1.0" + repository: "http://localhost:3000" diff --git a/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml-no-deps/fleet.yaml b/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml-no-deps/fleet.yaml new file mode 100644 index 0000000000..a3d77664e0 --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml-no-deps/fleet.yaml @@ -0,0 +1,8 @@ +namespace: test-deps-helm-yamls-with-fleet +helm: + releaseName: simple-with-fleet-yaml-no-deps + chart: "" + repo: "" + version: "" + disableDependencyUpdate: true + diff --git a/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml-no-deps/templates/configmap.yaml b/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml-no-deps/templates/configmap.yaml new file mode 100644 index 0000000000..c6b509ce7b --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml-no-deps/templates/configmap.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-simple-deps-chart +data: + test: "valuedeps" + name: {{ .Values.name }} diff --git a/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml-no-deps/values.yaml b/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml-no-deps/values.yaml new file mode 100644 index 0000000000..1d9970923f --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml-no-deps/values.yaml @@ -0,0 +1 @@ +name: deps-default-name diff --git a/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml/Chart.yaml b/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml/Chart.yaml new file mode 100644 index 0000000000..52dc058c1c --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml/Chart.yaml @@ -0,0 +1,10 @@ +apiVersion: v2 +appVersion: 1.16.0 +description: A chart for testing dependencies +name: deps-chart +type: application +version: 1.0.0 +dependencies: +- name: config-chart + version: "0.1.0" + repository: "http://localhost:3000" diff --git a/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml/fleet.yaml b/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml/fleet.yaml new file mode 100644 index 0000000000..74c08e0abc --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml/fleet.yaml @@ -0,0 +1,8 @@ +namespace: test-deps-helm-yamls-with-fleet +helm: + releaseName: simple-with-fleet-yaml + chart: "" + repo: "" + version: "" + force: false + diff --git a/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml/templates/configmap.yaml b/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml/templates/configmap.yaml new file mode 100644 index 0000000000..c6b509ce7b --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml/templates/configmap.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-simple-deps-chart +data: + test: "valuedeps" + name: {{ .Values.name }} diff --git a/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml/values.yaml b/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml/values.yaml new file mode 100644 index 0000000000..1d9970923f --- /dev/null +++ b/integrationtests/cli/assets/deps-charts/simple-with-fleet-yaml/values.yaml @@ -0,0 +1 @@ +name: deps-default-name diff --git a/integrationtests/cli/assets/helmrepository/deps-chart-1.0.0.tgz b/integrationtests/cli/assets/helmrepository/deps-chart-1.0.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..7862bdddc08ea7df7fd3376c92f82dd392b85e03 GIT binary patch literal 454 zcmV;%0XhC3iwFQU54~jo1MOB#Z{r{k&6!^@%H0kaV@tlRdfsdABO5np{ecXLHmdsH zcWh@Juhea|Qj&I6-T~wR2FzpLOr@=h$~_K#x49KK=ebfa_1WC>B%S*+K%`3LBFR-E zfeV>PwE=ar6RmJ?9tS$gPdu8p=Fzmeeq;RnA4~tApt|t?Ft+gt+vdu9(0ycS`gi`N z%ohIhOc4J{n3FrywuF_p;v|}B%x`4~8V*X^wKz4OFB|a5|7Cf>BA7jteOH^Ovk!n} z&6dZsY)hs=+uj-94=1wjy|=|~*YqVe``-CN5(!)O(YO2gv5J4MTiYP9dhJZ`@A^-z z4Elc$toT1-bI|Sv3E(~b=W@lrN=E)uA%p(k11r6*bd86`N24hy=NG}N`adV%7VUM! z;Jx@4xjM%`Wo=3IU&6T(A^z`yKk7fheweN*;9=tbJ6hIij}>|>sNkBVVyyZiXXvTu wXjSI@o-__qJvQZ%ad^8de13-baZWL#0bX8$$OHrg1biHR0i+&tegG5z05h2DTmS$7 literal 0 HcmV?d00001 diff --git a/integrationtests/cli/assets/helmrepository/deps-chart/Chart.yaml b/integrationtests/cli/assets/helmrepository/deps-chart/Chart.yaml new file mode 100644 index 0000000000..52dc058c1c --- /dev/null +++ b/integrationtests/cli/assets/helmrepository/deps-chart/Chart.yaml @@ -0,0 +1,10 @@ +apiVersion: v2 +appVersion: 1.16.0 +description: A chart for testing dependencies +name: deps-chart +type: application +version: 1.0.0 +dependencies: +- name: config-chart + version: "0.1.0" + repository: "http://localhost:3000" diff --git a/integrationtests/cli/assets/helmrepository/deps-chart/templates/configmap.yaml b/integrationtests/cli/assets/helmrepository/deps-chart/templates/configmap.yaml new file mode 100644 index 0000000000..c6b509ce7b --- /dev/null +++ b/integrationtests/cli/assets/helmrepository/deps-chart/templates/configmap.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-simple-deps-chart +data: + test: "valuedeps" + name: {{ .Values.name }} diff --git a/integrationtests/cli/assets/helmrepository/deps-chart/values.yaml b/integrationtests/cli/assets/helmrepository/deps-chart/values.yaml new file mode 100644 index 0000000000..1d9970923f --- /dev/null +++ b/integrationtests/cli/assets/helmrepository/deps-chart/values.yaml @@ -0,0 +1 @@ +name: deps-default-name diff --git a/integrationtests/cli/assets/helmrepository/index.yaml b/integrationtests/cli/assets/helmrepository/index.yaml index 7fdb2b943e..0d28bcdb30 100644 --- a/integrationtests/cli/assets/helmrepository/index.yaml +++ b/integrationtests/cli/assets/helmrepository/index.yaml @@ -11,4 +11,15 @@ entries: urls: - http://localhost:3000/config-chart-0.1.0.tgz version: 0.1.0 + deps-chart: + - apiVersion: v2 + appVersion: 1.16.0 + created: "2023-01-03T11:05:22.378628588+01:00" + description: A Helm chart for testing dependency update + digest: 8bae7a819851c6d96bacd213288d7892985951ab3ae79f27c3300f6426aec759 + name: testrepo + type: application + urls: + - http://localhost:3000/deps-chart-1.0.0.tgz + version: 1.0.0 generated: "2023-01-03T11:05:22.378100239+01:00" diff --git a/integrationtests/cli/helpers.go b/integrationtests/cli/helpers.go index d1e1805f88..10924d7e7e 100644 --- a/integrationtests/cli/helpers.go +++ b/integrationtests/cli/helpers.go @@ -7,6 +7,7 @@ import ( "os" "strings" + "github.com/rancher/fleet/internal/content" "github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1" "k8s.io/apimachinery/pkg/util/yaml" @@ -62,8 +63,18 @@ func IsResourcePresentInBundle(resourcePath string, resources []v1alpha1.BundleR } for _, resource := range resources { - if strings.ReplaceAll(resource.Content, "\n", "") == strings.ReplaceAll(string(resourceFile), "\n", "") { - return true, nil + if resource.Encoding == "base64+gz" { + resourceFileEncoded, err := content.Base64GZ(resourceFile) + if err != nil { + return false, err + } + if resource.Content == resourceFileEncoded { + return true, nil + } + } else { + if strings.ReplaceAll(resource.Content, "\n", "") == strings.ReplaceAll(string(resourceFile), "\n", "") { + return true, nil + } } } diff --git a/internal/bundlereader/loaddirectory.go b/internal/bundlereader/loaddirectory.go index 6ca1d700e4..63d69a7e1f 100644 --- a/internal/bundlereader/loaddirectory.go +++ b/internal/bundlereader/loaddirectory.go @@ -19,6 +19,7 @@ import ( "github.com/hashicorp/go-getter" "github.com/pkg/errors" "github.com/rancher/fleet/internal/content" + "github.com/rancher/fleet/internal/helmupdater" fleet "github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1" "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/downloader" @@ -151,10 +152,10 @@ func readFleetIgnore(path string) ([]string, error) { return ignored, nil } -func loadDirectory(ctx context.Context, compress bool, prefix, base, source, version string, auth Auth) ([]fleet.BundleResource, error) { +func loadDirectory(ctx context.Context, compress bool, disableDependencyUpdate bool, prefix, base, source, version string, auth Auth) ([]fleet.BundleResource, error) { var resources []fleet.BundleResource - files, err := GetContent(ctx, base, source, version, auth) + files, err := GetContent(ctx, base, source, version, auth, disableDependencyUpdate) if err != nil { return nil, err } @@ -181,7 +182,7 @@ func loadDirectory(ctx context.Context, compress bool, prefix, base, source, ver } // GetContent uses go-getter (and Helm for OCI) to read the files from directories and servers. -func GetContent(ctx context.Context, base, source, version string, auth Auth) (map[string][]byte, error) { +func GetContent(ctx context.Context, base, source, version string, auth Auth, disableDependencyUpdate bool) (map[string][]byte, error) { temp, err := os.MkdirTemp("", "fleet") if err != nil { return nil, err @@ -266,6 +267,13 @@ func GetContent(ctx context.Context, base, source, version string, auth Auth) (m } if info.IsDir() { + // If the folder is a helm chart and dependency updates are not disabled, + // try to update possible dependencies. + if !disableDependencyUpdate && helmupdater.FoundChartYamlInDirectory(path) { + if err = helmupdater.UpdateHelmDependencies(path); err != nil { + return err + } + } // Skip .fleetignore'd and hidden directories if ignore || strings.HasPrefix(filepath.Base(path), ".") { return filepath.SkipDir diff --git a/internal/bundlereader/loaddirectory_test.go b/internal/bundlereader/loaddirectory_test.go index 1d3d7a0772..4f88968eb5 100644 --- a/internal/bundlereader/loaddirectory_test.go +++ b/internal/bundlereader/loaddirectory_test.go @@ -333,7 +333,7 @@ func TestGetContent(t *testing.T) { root := createDirStruct(t, base, c.directoryStructure) - files, err := bundlereader.GetContent(context.Background(), root, root, "", bundlereader.Auth{}) + files, err := bundlereader.GetContent(context.Background(), root, root, "", bundlereader.Auth{}, false) assert.NoError(t, err) assert.Equal(t, len(c.expectedFiles), len(files)) diff --git a/internal/bundlereader/resources.go b/internal/bundlereader/resources.go index 90a86eebb8..26b73d86ab 100644 --- a/internal/bundlereader/resources.go +++ b/internal/bundlereader/resources.go @@ -62,7 +62,12 @@ func readResources(ctx context.Context, spec *fleet.BundleSpec, compress bool, b return nil, err } - resources, err := loadDirectories(ctx, compress, directories...) + // helm chart dependency update is enabled by default + disableDependencyUpdate := false + if spec.Helm != nil { + disableDependencyUpdate = spec.Helm.DisableDependencyUpdate + } + resources, err := loadDirectories(ctx, compress, disableDependencyUpdate, directories...) if err != nil { return nil, err } @@ -198,7 +203,7 @@ func checksum(helm *fleet.HelmOptions) string { return fmt.Sprintf(".chart/%x", sha256.Sum256([]byte(helm.Chart + ":" + helm.Repo + ":" + helm.Version)[:])) } -func loadDirectories(ctx context.Context, compress bool, directories ...directory) (map[string][]fleet.BundleResource, error) { +func loadDirectories(ctx context.Context, compress bool, disableDependencyUpdate bool, directories ...directory) (map[string][]fleet.BundleResource, error) { var ( sem = semaphore.NewWeighted(4) result = map[string][]fleet.BundleResource{} @@ -216,7 +221,7 @@ func loadDirectories(ctx context.Context, compress bool, directories ...director dir := dir eg.Go(func() error { defer sem.Release(1) - resources, err := loadDirectory(ctx, compress, dir.prefix, dir.base, dir.source, dir.version, dir.auth) + resources, err := loadDirectory(ctx, compress, disableDependencyUpdate, dir.prefix, dir.base, dir.source, dir.version, dir.auth) if err != nil { return err } diff --git a/internal/helmupdater/helmupdater.go b/internal/helmupdater/helmupdater.go new file mode 100644 index 0000000000..2e9ed031ea --- /dev/null +++ b/internal/helmupdater/helmupdater.go @@ -0,0 +1,64 @@ +package helmupdater + +import ( + "os" + "path/filepath" + + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/downloader" + "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/registry" +) + +const ( + ChartYaml = "Chart.yaml" +) + +func FoundChartYamlInDirectory(path string) bool { + chartPath := filepath.Join(path, ChartYaml) + if _, err := os.Stat(chartPath); err != nil { + return false + } + return true +} + +// UpdateHelmDependencies checks if the helm chart located at the given directory has unmet dependencies and, if so, updates them +func UpdateHelmDependencies(path string) error { + // load the chart to check if there are unmet dependencies first + chartRequested, err := loader.Load(path) + if err != nil { + return err + } + + if req := chartRequested.Metadata.Dependencies; req != nil { + if err := action.CheckDependencies(chartRequested, req); err != nil { + settings := cli.New() + registryClient, err := registry.NewClient( + registry.ClientOptDebug(settings.Debug), + registry.ClientOptEnableCache(true), + registry.ClientOptWriter(os.Stderr), + registry.ClientOptCredentialsFile(settings.RegistryConfig), + ) + if err != nil { + return err + } + man := &downloader.Manager{ + Out: os.Stdout, + ChartPath: path, + Keyring: "", + SkipUpdate: false, + Getters: getter.All(settings), + RepositoryConfig: settings.RegistryConfig, + RepositoryCache: settings.RepositoryCache, + Debug: true, + RegistryClient: registryClient, + } + if err := man.Update(); err != nil { + return err + } + } + } + return nil +} diff --git a/internal/helmupdater/helmupdater_test.go b/internal/helmupdater/helmupdater_test.go new file mode 100644 index 0000000000..8915000409 --- /dev/null +++ b/internal/helmupdater/helmupdater_test.go @@ -0,0 +1,101 @@ +package helmupdater_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/rancher/fleet/internal/helmupdater" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type fsNodeSimple struct { + name string + children []fsNodeSimple + isDir bool +} + +// createDirStruct generates and populates a directory structure which root is node, placing it at basePath. +func createDirStruct(t *testing.T, basePath string, node fsNodeSimple) { + path := filepath.Join(basePath, node.name) + + if !node.isDir { + _, err := os.Create(path) + require.NoError(t, err) + return + } + + err := os.Mkdir(path, 0777) + require.NoError(t, err) + + for _, c := range node.children { + createDirStruct(t, path, c) + } +} + +func TestFoundChartYamlInDirectory(t *testing.T) { + cases := []struct { + name string + testDir string + directoryStructure fsNodeSimple + expectedResult bool + }{ + { + name: "simple case having Chart yaml file in the root", + testDir: "simpleok", + directoryStructure: fsNodeSimple{ + name: helmupdater.ChartYaml, + }, + expectedResult: true, + }, + { + name: "Chart.yml file instead of Chart.yaml", + testDir: "extensionnotyaml", + directoryStructure: fsNodeSimple{ + name: "Chart.yml", + }, + expectedResult: false, + }, + { + name: "simple case not having Chart yaml file in the root", + testDir: "simplenotfound", + directoryStructure: fsNodeSimple{ + name: "whatever.foo", + }, + expectedResult: false, + }, + { + name: "Chart.yaml is located in a subdirectory", + testDir: "subdir", + directoryStructure: fsNodeSimple{ + isDir: true, + children: []fsNodeSimple{ + { + name: helmupdater.ChartYaml, + }, + }, + name: "whatever", + }, + expectedResult: false, + }, + } + + base, err := os.MkdirTemp("", "test-fleet") + require.NoError(t, err) + + defer os.RemoveAll(base) + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + testDirPath := filepath.Join(base, c.testDir) + err := os.Mkdir(testDirPath, 0777) + require.NoError(t, err) + defer os.RemoveAll(testDirPath) + + createDirStruct(t, testDirPath, c.directoryStructure) + found := helmupdater.FoundChartYamlInDirectory(testDirPath) + + assert.Equal(t, c.expectedResult, found) + }) + } +} diff --git a/pkg/apis/fleet.cattle.io/v1alpha1/bundledeployment_types.go b/pkg/apis/fleet.cattle.io/v1alpha1/bundledeployment_types.go index 91d4b00e2f..0cb9daddb9 100644 --- a/pkg/apis/fleet.cattle.io/v1alpha1/bundledeployment_types.go +++ b/pkg/apis/fleet.cattle.io/v1alpha1/bundledeployment_types.go @@ -238,6 +238,9 @@ type HelmOptions struct { // SkipSchemaValidation allows skipping schema validation against the chart values SkipSchemaValidation bool `json:"skipSchemaValidation,omitempty"` + + // DisableDependencyUpdate allows skipping chart dependencies update + DisableDependencyUpdate bool `json:"disableDependencyUpdate,omitempty"` } // IgnoreOptions defines conditions to be ignored when monitoring the Bundle. From 702ba99dcc4f9c3df8ffb576bccc1cf62f643029 Mon Sep 17 00:00:00 2001 From: Xavi Garcia Date: Tue, 13 Feb 2024 13:34:05 +0100 Subject: [PATCH 2/5] Changes after code-review Signed-off-by: Xavi Garcia --- .github/workflows/e2e-ci.yml | 8 +- e2e/single-cluster/helm_dependencies_test.go | 1 + integrationtests/cli/apply/apply_test.go | 97 ++++++++++++-------- internal/helmupdater/helmupdater.go | 4 +- internal/helmupdater/helmupdater_test.go | 4 +- 5 files changed, 67 insertions(+), 47 deletions(-) diff --git a/.github/workflows/e2e-ci.yml b/.github/workflows/e2e-ci.yml index c669c8fc6b..c0f9ca90e7 100644 --- a/.github/workflows/e2e-ci.yml +++ b/.github/workflows/e2e-ci.yml @@ -114,17 +114,13 @@ jobs: e2e/testenv/infra/infra setup --helm-registry=true ginkgo --label-filter='helm-registry' e2e/single-cluster - # 4. Run tests requiring a Helm registry and the git server - e2e/testenv/infra/infra setup --git-server=true - ginkgo --label-filter='infra-setup && helm-registry && !oci-registry' e2e/single-cluster/ - e2e/testenv/infra/infra teardown --helm-registry=true - # 5. Run tests requiring an OCI registry + # 4. Run tests requiring an OCI registry e2e/testenv/infra/infra setup --oci-registry=true ginkgo --label-filter='oci-registry' e2e/single-cluster - # 6. Tear down all infra + # 5. Tear down all infra e2e/testenv/infra/infra teardown - diff --git a/e2e/single-cluster/helm_dependencies_test.go b/e2e/single-cluster/helm_dependencies_test.go index 3cb697365c..7aaf680157 100644 --- a/e2e/single-cluster/helm_dependencies_test.go +++ b/e2e/single-cluster/helm_dependencies_test.go @@ -111,6 +111,7 @@ var _ = Describe("Helm dependency update tests", Label("infra-setup", "helm-regi Expect(err).ToNot(HaveOccurred(), out) out, err = k.Delete("ns", namespace) Expect(err).ToNot(HaveOccurred(), out) + os.RemoveAll(tmpDir) }) When("applying a gitrepo resource", func() { diff --git a/integrationtests/cli/apply/apply_test.go b/integrationtests/cli/apply/apply_test.go index e17cf2e510..28f7dc4caf 100644 --- a/integrationtests/cli/apply/apply_test.go +++ b/integrationtests/cli/apply/apply_test.go @@ -279,10 +279,13 @@ var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func name = "no-fleet-yaml" }) - It("then a Bundle is created with all the resources, including the dependencies, and keepResources is false", func() { + It("creates a Bundle with all the resources, including the dependencies", func() { Eventually(func() bool { bundle, err := cli.GetBundleFromOutput(buf) Expect(err).NotTo(HaveOccurred()) + // files expected are: + // Chart.yaml + values.yaml + templates/configmap.yaml + + // Chart.lock + charts/config-chart-0.1.0.tgz Expect(len(bundle.Spec.Resources)).To(Equal(5)) files, err := getAllFilesInDir(tmpDir) Expect(err).NotTo(HaveOccurred()) @@ -293,8 +296,7 @@ var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func // explicitly check for dependency files presentInBundleResources(path.Join(tmpDir, "Chart.lock"), bundle.Spec.Resources) presentInBundleResources(path.Join(tmpDir, "charts/config-chart-0.1.0.tgz"), bundle.Spec.Resources) - - return !bundle.Spec.KeepResources + return true }).Should(BeTrue()) }) }) @@ -304,10 +306,13 @@ var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func name = "simple-with-fleet-yaml" }) - It("then a Bundle is created with all the resources, including the dependencies, and keepResources is false", func() { + It("creates a Bundle with all the resources, including the dependencies", func() { Eventually(func() bool { bundle, err := cli.GetBundleFromOutput(buf) Expect(err).NotTo(HaveOccurred()) + // files expected are: + // Chart.yaml + values.yaml + templates/configmap.yaml + fleet.yaml + + // Chart.lock + charts/config-chart-0.1.0.tgz Expect(len(bundle.Spec.Resources)).To(Equal(6)) files, err := getAllFilesInDir(tmpDirRel) Expect(err).NotTo(HaveOccurred()) @@ -318,7 +323,7 @@ var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func // explicitly check for dependency files presentInBundleResources(path.Join(tmpDirRel, "Chart.lock"), bundle.Spec.Resources) presentInBundleResources(path.Join(tmpDirRel, "charts/config-chart-0.1.0.tgz"), bundle.Spec.Resources) - return !bundle.Spec.KeepResources + return true }).Should(BeTrue()) }) }) @@ -328,10 +333,12 @@ var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func name = "simple-with-fleet-yaml-no-deps" }) - It("then a Bundle is created with all the resources, dependencies should not be in the bundle", func() { + It("creates a Bundle with all the resources, dependencies should not be in the bundle", func() { Eventually(func() bool { bundle, err := cli.GetBundleFromOutput(buf) Expect(err).NotTo(HaveOccurred()) + // files expected are: + // Chart.yaml + values.yaml + templates/configmap.yaml + fleet.yaml Expect(len(bundle.Spec.Resources)).To(Equal(4)) files, err := getAllFilesInDir(tmpDirRel) Expect(err).NotTo(HaveOccurred()) @@ -342,7 +349,7 @@ var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func // explicitly check for dependency files (they should not exist) notPresentInBundleResources(path.Join(tmpDirRel, "Chart.lock"), bundle.Spec.Resources) notPresentInBundleResources(path.Join(tmpDirRel, "charts/config-chart-0.1.0.tgz"), bundle.Spec.Resources) - return !bundle.Spec.KeepResources + return true }).Should(BeTrue()) }) }) @@ -352,10 +359,13 @@ var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func name = "remote-chart-with-deps" }) - It("then a Bundle is created with all the resources, dependencies should be in the bundle", func() { + It("creates a Bundle with all the resources, dependencies should be in the bundle", func() { Eventually(func() bool { bundle, err := cli.GetBundleFromOutput(buf) Expect(err).NotTo(HaveOccurred()) + // expected files are: + // fleet.yaml + Chart.yaml + values.yaml + templates/configmap.yaml + + // Chart.lock + charts/config-chart-0.1.0.tgz Expect(len(bundle.Spec.Resources)).To(Equal(6)) presentInBundleResources(path.Join(tmpDirRel, "fleet.yaml"), bundle.Spec.Resources) // as files were unpacked from the downloaded chart we can't just @@ -366,7 +376,7 @@ var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func onlyPresentInBundleResources("templates/configmap.yaml", bundle.Spec.Resources) onlyPresentInBundleResources("Chart.lock", bundle.Spec.Resources) onlyPresentInBundleResources("charts/config-chart-0.1.0.tgz", bundle.Spec.Resources) - return !bundle.Spec.KeepResources + return true }).Should(BeTrue()) }) }) @@ -376,10 +386,13 @@ var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func name = "remote-chart-with-deps-disabled" }) - It("then a Bundle is created with all the resources, dependencies should not be in the bundle", func() { + It("creates a Bundle with all the resources, dependencies should not be in the bundle", func() { Eventually(func() bool { bundle, err := cli.GetBundleFromOutput(buf) Expect(err).NotTo(HaveOccurred()) + // expected files are: + // fleet.yaml + + // Chart.yaml + values.yaml + templates/configmap.yaml Expect(len(bundle.Spec.Resources)).To(Equal(4)) presentInBundleResources(path.Join(tmpDirRel, "fleet.yaml"), bundle.Spec.Resources) // as files were unpacked from the downloaded chart we can't just @@ -388,7 +401,7 @@ var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func onlyPresentInBundleResources("Chart.yaml", bundle.Spec.Resources) onlyPresentInBundleResources("values.yaml", bundle.Spec.Resources) onlyPresentInBundleResources("templates/configmap.yaml", bundle.Spec.Resources) - return !bundle.Spec.KeepResources + return true }).Should(BeTrue()) }) }) @@ -398,51 +411,59 @@ var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func name = "multi-chart" }) - It("then Bundles are created with the corresponding resources, depending if they should update dependencies", func() { + It("creates Bundles with the corresponding resources, depending if they should update dependencies", func() { Eventually(func() bool { bundle, err := cli.GetBundleListFromOutput(buf) Expect(err).NotTo(HaveOccurred()) Expect(len(bundle)).To(Equal(3)) - deploymentA := bundle[0] - deploymentB := bundle[1] - deploymentC := bundle[2] - - // deploymentA corresponds to multi-chart/remote-chart-with-deps - Expect(len(deploymentA.Spec.Resources)).To(Equal(6)) - presentInBundleResources(path.Join(tmpDirRel, "remote-chart-with-deps", "fleet.yaml"), deploymentA.Spec.Resources) + remoteDepl := bundle[0] + simpleDepl := bundle[1] + noDepsDepl := bundle[2] + + // remoteDepl corresponds to multi-chart/remote-chart-with-deps + // expected files are: + // fleet.yaml + + // Chart.yaml + values.yaml + templates/configmap.yaml + Chart.lock + charts/config-chart-0.1.0.tgz + Expect(len(remoteDepl.Spec.Resources)).To(Equal(6)) + presentInBundleResources(path.Join(tmpDirRel, "remote-chart-with-deps", "fleet.yaml"), remoteDepl.Spec.Resources) // as files were unpacked from the downloaded chart we can't just // list the files in the original folder and compare. // Files are only located in the bundle resources - onlyPresentInBundleResources("Chart.yaml", deploymentA.Spec.Resources) - onlyPresentInBundleResources("values.yaml", deploymentA.Spec.Resources) - onlyPresentInBundleResources("templates/configmap.yaml", deploymentA.Spec.Resources) - onlyPresentInBundleResources("Chart.lock", deploymentA.Spec.Resources) - onlyPresentInBundleResources("charts/config-chart-0.1.0.tgz", deploymentA.Spec.Resources) - - // deploymentB corresponds to multi-chart/simple-with-fleet-yaml - Expect(len(deploymentB.Spec.Resources)).To(Equal(6)) + onlyPresentInBundleResources("Chart.yaml", remoteDepl.Spec.Resources) + onlyPresentInBundleResources("values.yaml", remoteDepl.Spec.Resources) + onlyPresentInBundleResources("templates/configmap.yaml", remoteDepl.Spec.Resources) + onlyPresentInBundleResources("Chart.lock", remoteDepl.Spec.Resources) + onlyPresentInBundleResources("charts/config-chart-0.1.0.tgz", remoteDepl.Spec.Resources) + + // simpleDepl corresponds to multi-chart/simple-with-fleet-yaml + // expected files are: + // fleet.yaml + Chart.yaml + values.yaml + templates/configmap.yaml + + // Chart.lock + charts/config-chart-0.1.0.tgz + Expect(len(simpleDepl.Spec.Resources)).To(Equal(6)) files, err := getAllFilesInDir(path.Join(tmpDirRel, "simple-with-fleet-yaml")) Expect(err).NotTo(HaveOccurred()) - Expect(len(files)).To(Equal(len(deploymentB.Spec.Resources))) + Expect(len(files)).To(Equal(len(simpleDepl.Spec.Resources))) for _, file := range files { - presentInBundleResources(file, deploymentB.Spec.Resources) + presentInBundleResources(file, simpleDepl.Spec.Resources) } // explicitly check for dependency files - presentInBundleResources(path.Join(tmpDirRel, "simple-with-fleet-yaml", "Chart.lock"), deploymentB.Spec.Resources) - presentInBundleResources(path.Join(tmpDirRel, "simple-with-fleet-yaml", "charts/config-chart-0.1.0.tgz"), deploymentB.Spec.Resources) + presentInBundleResources(path.Join(tmpDirRel, "simple-with-fleet-yaml", "Chart.lock"), simpleDepl.Spec.Resources) + presentInBundleResources(path.Join(tmpDirRel, "simple-with-fleet-yaml", "charts/config-chart-0.1.0.tgz"), simpleDepl.Spec.Resources) - // deploymentC corresponds to multi-char/simple-with-fleet-yaml-no-deps - Expect(len(deploymentC.Spec.Resources)).To(Equal(4)) + // noDepsDepl corresponds to multi-char/simple-with-fleet-yaml-no-deps + // expected files are: + // Chart.yaml + fleet.yaml + values.yaml + templates/configmap.yaml + Expect(len(noDepsDepl.Spec.Resources)).To(Equal(4)) files, err = getAllFilesInDir(path.Join(tmpDirRel, "simple-with-fleet-yaml-no-deps")) Expect(err).NotTo(HaveOccurred()) - Expect(len(files)).To(Equal(len(deploymentC.Spec.Resources))) + Expect(len(files)).To(Equal(len(noDepsDepl.Spec.Resources))) for _, file := range files { - presentInBundleResources(file, deploymentC.Spec.Resources) + presentInBundleResources(file, noDepsDepl.Spec.Resources) } // explicitly check for dependency files (they should not exist) - notPresentInBundleResources(path.Join(tmpDirRel, "simple-with-fleet-yaml-no-deps", "Chart.lock"), deploymentC.Spec.Resources) - notPresentInBundleResources(path.Join(tmpDirRel, "simple-with-fleet-yaml-no-deps", "charts/config-chart-0.1.0.tgz"), deploymentC.Spec.Resources) - return !deploymentA.Spec.KeepResources && !deploymentB.Spec.KeepResources && !deploymentC.Spec.KeepResources + notPresentInBundleResources(path.Join(tmpDirRel, "simple-with-fleet-yaml-no-deps", "Chart.lock"), noDepsDepl.Spec.Resources) + notPresentInBundleResources(path.Join(tmpDirRel, "simple-with-fleet-yaml-no-deps", "charts/config-chart-0.1.0.tgz"), noDepsDepl.Spec.Resources) + return true }).Should(BeTrue()) }) }) diff --git a/internal/helmupdater/helmupdater.go b/internal/helmupdater/helmupdater.go index 2e9ed031ea..89b097ec92 100644 --- a/internal/helmupdater/helmupdater.go +++ b/internal/helmupdater/helmupdater.go @@ -16,6 +16,8 @@ const ( ChartYaml = "Chart.yaml" ) +// FoundChartYamlInDirectory checks if the provided path is a directory containing a `Chart.yaml` file. +// Returns true if it does, false otherwise or if an error happens when checking `/Chart.yaml`. func FoundChartYamlInDirectory(path string) bool { chartPath := filepath.Join(path, ChartYaml) if _, err := os.Stat(chartPath); err != nil { @@ -52,7 +54,7 @@ func UpdateHelmDependencies(path string) error { Getters: getter.All(settings), RepositoryConfig: settings.RegistryConfig, RepositoryCache: settings.RepositoryCache, - Debug: true, + Debug: settings.Debug, RegistryClient: registryClient, } if err := man.Update(); err != nil { diff --git a/internal/helmupdater/helmupdater_test.go b/internal/helmupdater/helmupdater_test.go index 8915000409..e266111697 100644 --- a/internal/helmupdater/helmupdater_test.go +++ b/internal/helmupdater/helmupdater_test.go @@ -45,7 +45,7 @@ func TestFoundChartYamlInDirectory(t *testing.T) { name: "simple case having Chart yaml file in the root", testDir: "simpleok", directoryStructure: fsNodeSimple{ - name: helmupdater.ChartYaml, + name: "Chart.yaml", }, expectedResult: true, }, @@ -72,7 +72,7 @@ func TestFoundChartYamlInDirectory(t *testing.T) { isDir: true, children: []fsNodeSimple{ { - name: helmupdater.ChartYaml, + name: "Chart.yaml", }, }, name: "whatever", From 527b259a202a7b0f84b6fcbd3aa6810c60635e35 Mon Sep 17 00:00:00 2001 From: Xavi Garcia Date: Tue, 13 Feb 2024 14:59:53 +0100 Subject: [PATCH 3/5] Change function name to make it go-idiomatic Signed-off-by: Xavi Garcia --- internal/bundlereader/loaddirectory.go | 2 +- internal/helmupdater/helmupdater.go | 4 ++-- internal/helmupdater/helmupdater_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/bundlereader/loaddirectory.go b/internal/bundlereader/loaddirectory.go index 63d69a7e1f..b071cf0a42 100644 --- a/internal/bundlereader/loaddirectory.go +++ b/internal/bundlereader/loaddirectory.go @@ -269,7 +269,7 @@ func GetContent(ctx context.Context, base, source, version string, auth Auth, di if info.IsDir() { // If the folder is a helm chart and dependency updates are not disabled, // try to update possible dependencies. - if !disableDependencyUpdate && helmupdater.FoundChartYamlInDirectory(path) { + if !disableDependencyUpdate && helmupdater.ChartYAMLExists(path) { if err = helmupdater.UpdateHelmDependencies(path); err != nil { return err } diff --git a/internal/helmupdater/helmupdater.go b/internal/helmupdater/helmupdater.go index 89b097ec92..5dcf72d7bb 100644 --- a/internal/helmupdater/helmupdater.go +++ b/internal/helmupdater/helmupdater.go @@ -16,9 +16,9 @@ const ( ChartYaml = "Chart.yaml" ) -// FoundChartYamlInDirectory checks if the provided path is a directory containing a `Chart.yaml` file. +// ChartYAMLExists checks if the provided path is a directory containing a `Chart.yaml` file. // Returns true if it does, false otherwise or if an error happens when checking `/Chart.yaml`. -func FoundChartYamlInDirectory(path string) bool { +func ChartYAMLExists(path string) bool { chartPath := filepath.Join(path, ChartYaml) if _, err := os.Stat(chartPath); err != nil { return false diff --git a/internal/helmupdater/helmupdater_test.go b/internal/helmupdater/helmupdater_test.go index e266111697..42f30c1882 100644 --- a/internal/helmupdater/helmupdater_test.go +++ b/internal/helmupdater/helmupdater_test.go @@ -34,7 +34,7 @@ func createDirStruct(t *testing.T, basePath string, node fsNodeSimple) { } } -func TestFoundChartYamlInDirectory(t *testing.T) { +func TestChartYAMLExists(t *testing.T) { cases := []struct { name string testDir string @@ -93,7 +93,7 @@ func TestFoundChartYamlInDirectory(t *testing.T) { defer os.RemoveAll(testDirPath) createDirStruct(t, testDirPath, c.directoryStructure) - found := helmupdater.FoundChartYamlInDirectory(testDirPath) + found := helmupdater.ChartYAMLExists(testDirPath) assert.Equal(t, c.expectedResult, found) }) From 6a471d4d0d8d0e2c90e438cb416e7fb10e7ef9d3 Mon Sep 17 00:00:00 2001 From: Xavi Garcia Date: Tue, 13 Feb 2024 16:02:00 +0100 Subject: [PATCH 4/5] Change parameter name to make it shorter Changed from disableDependencyUpdate to disableDepsUpdate Signed-off-by: Xavi Garcia --- internal/bundlereader/loaddirectory.go | 8 ++++---- internal/bundlereader/resources.go | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/bundlereader/loaddirectory.go b/internal/bundlereader/loaddirectory.go index b071cf0a42..38eeacdaf7 100644 --- a/internal/bundlereader/loaddirectory.go +++ b/internal/bundlereader/loaddirectory.go @@ -152,10 +152,10 @@ func readFleetIgnore(path string) ([]string, error) { return ignored, nil } -func loadDirectory(ctx context.Context, compress bool, disableDependencyUpdate bool, prefix, base, source, version string, auth Auth) ([]fleet.BundleResource, error) { +func loadDirectory(ctx context.Context, compress bool, disableDepsUpdate bool, prefix, base, source, version string, auth Auth) ([]fleet.BundleResource, error) { var resources []fleet.BundleResource - files, err := GetContent(ctx, base, source, version, auth, disableDependencyUpdate) + files, err := GetContent(ctx, base, source, version, auth, disableDepsUpdate) if err != nil { return nil, err } @@ -182,7 +182,7 @@ func loadDirectory(ctx context.Context, compress bool, disableDependencyUpdate b } // GetContent uses go-getter (and Helm for OCI) to read the files from directories and servers. -func GetContent(ctx context.Context, base, source, version string, auth Auth, disableDependencyUpdate bool) (map[string][]byte, error) { +func GetContent(ctx context.Context, base, source, version string, auth Auth, disableDepsUpdate bool) (map[string][]byte, error) { temp, err := os.MkdirTemp("", "fleet") if err != nil { return nil, err @@ -269,7 +269,7 @@ func GetContent(ctx context.Context, base, source, version string, auth Auth, di if info.IsDir() { // If the folder is a helm chart and dependency updates are not disabled, // try to update possible dependencies. - if !disableDependencyUpdate && helmupdater.ChartYAMLExists(path) { + if !disableDepsUpdate && helmupdater.ChartYAMLExists(path) { if err = helmupdater.UpdateHelmDependencies(path); err != nil { return err } diff --git a/internal/bundlereader/resources.go b/internal/bundlereader/resources.go index 26b73d86ab..58a7f3a024 100644 --- a/internal/bundlereader/resources.go +++ b/internal/bundlereader/resources.go @@ -63,11 +63,11 @@ func readResources(ctx context.Context, spec *fleet.BundleSpec, compress bool, b } // helm chart dependency update is enabled by default - disableDependencyUpdate := false + disableDepsUpdate := false if spec.Helm != nil { - disableDependencyUpdate = spec.Helm.DisableDependencyUpdate + disableDepsUpdate = spec.Helm.DisableDependencyUpdate } - resources, err := loadDirectories(ctx, compress, disableDependencyUpdate, directories...) + resources, err := loadDirectories(ctx, compress, disableDepsUpdate, directories...) if err != nil { return nil, err } @@ -203,7 +203,7 @@ func checksum(helm *fleet.HelmOptions) string { return fmt.Sprintf(".chart/%x", sha256.Sum256([]byte(helm.Chart + ":" + helm.Repo + ":" + helm.Version)[:])) } -func loadDirectories(ctx context.Context, compress bool, disableDependencyUpdate bool, directories ...directory) (map[string][]fleet.BundleResource, error) { +func loadDirectories(ctx context.Context, compress bool, disableDepsUpdate bool, directories ...directory) (map[string][]fleet.BundleResource, error) { var ( sem = semaphore.NewWeighted(4) result = map[string][]fleet.BundleResource{} @@ -221,7 +221,7 @@ func loadDirectories(ctx context.Context, compress bool, disableDependencyUpdate dir := dir eg.Go(func() error { defer sem.Release(1) - resources, err := loadDirectory(ctx, compress, disableDependencyUpdate, dir.prefix, dir.base, dir.source, dir.version, dir.auth) + resources, err := loadDirectory(ctx, compress, disableDepsUpdate, dir.prefix, dir.base, dir.source, dir.version, dir.auth) if err != nil { return err } From 28c9c5b9b1812a5f9c3f76a796638c5b49b3db7b Mon Sep 17 00:00:00 2001 From: Xavi Garcia Date: Fri, 16 Feb 2024 13:07:46 +0100 Subject: [PATCH 5/5] Delete Eventually and add Gomega custom matchers Eventually is not needed when testing for resources in bundles as we get the bundle from `cli.GetBundleListFromOutput` and the buffer read will be in its final state once `cli.GetBundleListFromOutput` returns. Also addind custom Gomega matchers to avoid using helper functions and to get the exact line when any test assertion fails. Already existing tests have been changed to use the custom matchers and don't use Eventually, also. Signed-off-by: Xavi Garcia --- integrationtests/cli/apply/apply_test.go | 517 +++++++++++------------ 1 file changed, 236 insertions(+), 281 deletions(-) diff --git a/integrationtests/cli/apply/apply_test.go b/integrationtests/cli/apply/apply_test.go index 28f7dc4caf..08391e349e 100644 --- a/integrationtests/cli/apply/apply_test.go +++ b/integrationtests/cli/apply/apply_test.go @@ -1,6 +1,7 @@ package apply import ( + "fmt" "os" "path" "path/filepath" @@ -8,6 +9,8 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/onsi/gomega/gcustom" + "github.com/onsi/gomega/types" cp "github.com/otiai10/copy" "github.com/rancher/fleet/integrationtests/cli" @@ -35,17 +38,12 @@ var _ = Describe("Fleet apply", Ordered, func() { }) It("then a Bundle is created with all the resources and keepResources is false", func() { - Eventually(func() bool { - bundle, err := cli.GetBundleFromOutput(buf) - Expect(err).NotTo(HaveOccurred()) - Expect(len(bundle.Spec.Resources)).To(Equal(2)) - isSvcPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"simple/svc.yaml", bundle.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - isDeploymentPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"simple/deployment.yaml", bundle.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - - return isSvcPresent && isDeploymentPresent && !bundle.Spec.KeepResources - }).Should(BeTrue()) + bundle, err := cli.GetBundleFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + Expect(len(bundle.Spec.Resources)).To(Equal(2)) + Expect(cli.AssetsPath + "simple/svc.yaml").To(bePresentInBundleResources(bundle.Spec.Resources)) + Expect(cli.AssetsPath + "simple/deployment.yaml").To(bePresentInBundleResources(bundle.Spec.Resources)) + Expect(bundle.Spec.KeepResources).Should(BeFalse()) }) }) @@ -56,19 +54,12 @@ var _ = Describe("Fleet apply", Ordered, func() { }) It("then a Bundle is created with all the resources", func() { - Eventually(func() bool { - bundle, err := cli.GetBundleFromOutput(buf) - Expect(err).NotTo(HaveOccurred()) - Expect(len(bundle.Spec.Resources)).To(Equal(3)) - isSvcPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_simple/simple/svc.yaml", bundle.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - isDeploymentPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_simple/simple/deployment.yaml", bundle.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - isREADMEPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_simple/README.md", bundle.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - - return isSvcPresent && isDeploymentPresent && isREADMEPresent - }).Should(BeTrue()) + bundle, err := cli.GetBundleFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + Expect(len(bundle.Spec.Resources)).To(Equal(3)) + Expect(cli.AssetsPath + "nested_simple/simple/svc.yaml").To(bePresentInBundleResources(bundle.Spec.Resources)) + Expect(cli.AssetsPath + "nested_simple/simple/deployment.yaml").To(bePresentInBundleResources(bundle.Spec.Resources)) + Expect(cli.AssetsPath + "nested_simple/README.md").To(bePresentInBundleResources(bundle.Spec.Resources)) }) }) @@ -79,17 +70,11 @@ var _ = Describe("Fleet apply", Ordered, func() { }) It("then a Bundle is created with all the resources", func() { - Eventually(func() bool { - bundle, err := cli.GetBundleFromOutput(buf) - Expect(err).NotTo(HaveOccurred()) - Expect(len(bundle.Spec.Resources)).To(Equal(2)) - isSvcPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_two_levels/nested/svc/svc.yaml", bundle.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - isDeploymentPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_two_levels/nested/deployment/deployment.yaml", bundle.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - - return isSvcPresent && isDeploymentPresent - }).Should(BeTrue()) + bundle, err := cli.GetBundleFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + Expect(len(bundle.Spec.Resources)).To(Equal(2)) + Expect(cli.AssetsPath + "nested_two_levels/nested/svc/svc.yaml").To(bePresentInBundleResources(bundle.Spec.Resources)) + Expect(cli.AssetsPath + "nested_two_levels/nested/deployment/deployment.yaml").To(bePresentInBundleResources(bundle.Spec.Resources)) }) }) @@ -100,33 +85,23 @@ var _ = Describe("Fleet apply", Ordered, func() { }) It("then 3 Bundles are created with the relevant resources", func() { - Eventually(func() bool { - bundle, err := cli.GetBundleListFromOutput(buf) - Expect(err).NotTo(HaveOccurred()) - Expect(len(bundle)).To(Equal(3)) - deploymentA := bundle[0] - deploymentB := bundle[1] - deploymentC := bundle[2] - - Expect(len(deploymentA.Spec.Resources)).To(Equal(2)) - Expect(len(deploymentB.Spec.Resources)).To(Equal(2)) - Expect(len(deploymentC.Spec.Resources)).To(Equal(2)) - - isFleetAPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_multiple/deploymentA/fleet.yaml", deploymentA.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - isSvcAPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_multiple/deploymentA/svc/svc.yaml", deploymentA.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - isFleetBPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_multiple/deploymentB/fleet.yaml", deploymentB.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - isSvcBPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_multiple/deploymentB/svc/nested/svc.yaml", deploymentB.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - isFleetCPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_multiple/deploymentC/fleet.yaml", deploymentC.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - isDeploymentCPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_multiple/deploymentC/deployment.yaml", deploymentC.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - - return isFleetAPresent && isSvcAPresent && isFleetBPresent && isSvcBPresent && isFleetCPresent && isDeploymentCPresent - }).Should(BeTrue()) + bundle, err := cli.GetBundleListFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + Expect(len(bundle)).To(Equal(3)) + deploymentA := bundle[0] + deploymentB := bundle[1] + deploymentC := bundle[2] + + Expect(len(deploymentA.Spec.Resources)).To(Equal(2)) + Expect(len(deploymentB.Spec.Resources)).To(Equal(2)) + Expect(len(deploymentC.Spec.Resources)).To(Equal(2)) + + Expect(cli.AssetsPath + "nested_multiple/deploymentA/fleet.yaml").To(bePresentInBundleResources(deploymentA.Spec.Resources)) + Expect(cli.AssetsPath + "nested_multiple/deploymentA/svc/svc.yaml").To(bePresentInBundleResources(deploymentA.Spec.Resources)) + Expect(cli.AssetsPath + "nested_multiple/deploymentB/fleet.yaml").To(bePresentInBundleResources(deploymentB.Spec.Resources)) + Expect(cli.AssetsPath + "nested_multiple/deploymentB/svc/nested/svc.yaml").To(bePresentInBundleResources(deploymentB.Spec.Resources)) + Expect(cli.AssetsPath + "nested_multiple/deploymentC/fleet.yaml").To(bePresentInBundleResources(deploymentC.Spec.Resources)) + Expect(cli.AssetsPath + "nested_multiple/deploymentC/deployment.yaml").To(bePresentInBundleResources(deploymentC.Spec.Resources)) }) }) @@ -137,37 +112,25 @@ var _ = Describe("Fleet apply", Ordered, func() { }) It("then Bundles are created with all the resources", func() { - Eventually(func() bool { - bundle, err := cli.GetBundleListFromOutput(buf) - Expect(err).NotTo(HaveOccurred()) - Expect(len(bundle)).To(Equal(3)) - root := bundle[0] - deploymentA := bundle[1] - deploymentC := bundle[2] - - Expect(len(deploymentA.Spec.Resources)).To(Equal(2)) - Expect(len(deploymentC.Spec.Resources)).To(Equal(1)) - Expect(len(root.Spec.Resources)).To(Equal(5)) - - isFleetAPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_mixed_two_levels/nested/deploymentA/fleet.yaml", deploymentA.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - isDeploymentAPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_mixed_two_levels/nested/deploymentA/fleet.yaml", deploymentA.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - isFleetCPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_mixed_two_levels/nested/deploymentC/fleet.yaml", deploymentC.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - isRootDeploymentAPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_mixed_two_levels/nested/deploymentA/fleet.yaml", root.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - isRootFleetAPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_mixed_two_levels/nested/deploymentA/fleet.yaml", root.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - isRootSvcBPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_mixed_two_levels/nested/deploymentB/svc.yaml", root.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - isRootFleetCPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_mixed_two_levels/nested/deploymentC/fleet.yaml", root.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - isRootDeploymentDPresent, err := cli.IsResourcePresentInBundle(cli.AssetsPath+"nested_mixed_two_levels/nested/deploymentD/deployment.yaml", root.Spec.Resources) - Expect(err).NotTo(HaveOccurred()) - - return isFleetAPresent && isDeploymentAPresent && isFleetCPresent && isRootDeploymentAPresent && isRootFleetAPresent && isRootSvcBPresent && isRootFleetCPresent && isRootDeploymentDPresent - }).Should(BeTrue()) + bundle, err := cli.GetBundleListFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + Expect(len(bundle)).To(Equal(3)) + root := bundle[0] + deploymentA := bundle[1] + deploymentC := bundle[2] + + Expect(len(deploymentA.Spec.Resources)).To(Equal(2)) + Expect(len(deploymentC.Spec.Resources)).To(Equal(1)) + Expect(len(root.Spec.Resources)).To(Equal(5)) + + Expect(cli.AssetsPath + "nested_mixed_two_levels/nested/deploymentA/fleet.yaml").To(bePresentInBundleResources(deploymentA.Spec.Resources)) + Expect(cli.AssetsPath + "nested_mixed_two_levels/nested/deploymentA/deployment.yaml").To(bePresentInBundleResources(deploymentA.Spec.Resources)) + Expect(cli.AssetsPath + "nested_mixed_two_levels/nested/deploymentC/fleet.yaml").To(bePresentInBundleResources(deploymentC.Spec.Resources)) + Expect(cli.AssetsPath + "nested_mixed_two_levels/nested/deploymentA/fleet.yaml").To(bePresentInBundleResources(root.Spec.Resources)) + Expect(cli.AssetsPath + "nested_mixed_two_levels/nested/deploymentA/deployment.yaml").To(bePresentInBundleResources(root.Spec.Resources)) + Expect(cli.AssetsPath + "nested_mixed_two_levels/nested/deploymentB/svc.yaml").To(bePresentInBundleResources(root.Spec.Resources)) + Expect(cli.AssetsPath + "nested_mixed_two_levels/nested/deploymentC/fleet.yaml").To(bePresentInBundleResources(root.Spec.Resources)) + Expect(cli.AssetsPath + "nested_mixed_two_levels/nested/deploymentD/deployment.yaml").To(bePresentInBundleResources(root.Spec.Resources)) }) }) @@ -178,11 +141,9 @@ var _ = Describe("Fleet apply", Ordered, func() { }) It("then a Bundle is created with keepResources", func() { - Eventually(func() bool { - bundle, err := cli.GetBundleFromOutput(buf) - Expect(err).NotTo(HaveOccurred()) - return bundle.Spec.KeepResources - }).Should(BeTrue()) + bundle, err := cli.GetBundleFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + Expect(bundle.Spec.KeepResources).To(BeTrue()) }) }) @@ -194,16 +155,14 @@ var _ = Describe("Fleet apply", Ordered, func() { }) It("publishes the flag in the bundle options", func() { - Eventually(func() bool { - bundle, err := cli.GetBundleFromOutput(buf) - Expect(err).NotTo(HaveOccurred()) - return bundle.Spec.Helm.TakeOwnership && - bundle.Spec.Helm.Atomic && - bundle.Spec.Helm.Force && - bundle.Spec.Helm.WaitForJobs && - bundle.Spec.Helm.DisablePreProcess && - bundle.Spec.Helm.ReleaseName == "enabled" - }).Should(BeTrue()) + bundle, err := cli.GetBundleFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + Expect(bundle.Spec.Helm.TakeOwnership).To(BeTrue()) + Expect(bundle.Spec.Helm.Atomic).To(BeTrue()) + Expect(bundle.Spec.Helm.Force).To(BeTrue()) + Expect(bundle.Spec.Helm.WaitForJobs).To(BeTrue()) + Expect(bundle.Spec.Helm.DisablePreProcess).To(BeTrue()) + Expect(bundle.Spec.Helm.ReleaseName).To(Equal("enabled")) }) }) @@ -214,16 +173,14 @@ var _ = Describe("Fleet apply", Ordered, func() { }) It("publishes the flag in the bundle options", func() { - Eventually(func() bool { - bundle, err := cli.GetBundleFromOutput(buf) - Expect(err).NotTo(HaveOccurred()) - return bundle.Spec.Helm.TakeOwnership == false && - bundle.Spec.Helm.Atomic == false && - bundle.Spec.Helm.Force == false && - bundle.Spec.Helm.WaitForJobs == false && - bundle.Spec.Helm.DisablePreProcess == false && - bundle.Spec.Helm.ReleaseName == "disabled" - }).Should(BeTrue()) + bundle, err := cli.GetBundleFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + Expect(bundle.Spec.Helm.TakeOwnership).To(BeFalse()) + Expect(bundle.Spec.Helm.Atomic).To(BeFalse()) + Expect(bundle.Spec.Helm.Force).To(BeFalse()) + Expect(bundle.Spec.Helm.WaitForJobs).To(BeFalse()) + Expect(bundle.Spec.Helm.DisablePreProcess).To(BeFalse()) + Expect(bundle.Spec.Helm.ReleaseName).To(Equal("disabled")) }) }) @@ -234,12 +191,10 @@ var _ = Describe("Fleet apply", Ordered, func() { }) It("publishes the flag in the bundle options", func() { - Eventually(func() bool { - bundle, err := cli.GetBundleFromOutput(buf) - Expect(err).NotTo(HaveOccurred()) - return bundle.Spec.Helm.TakeOwnership && - bundle.Spec.Helm.ReleaseName == "kustomize" - }).Should(BeTrue()) + bundle, err := cli.GetBundleFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + Expect(bundle.Spec.Helm.TakeOwnership).To(BeTrue()) + Expect(bundle.Spec.Helm.ReleaseName).To(Equal("kustomize")) }) }) }) @@ -280,24 +235,21 @@ var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func }) It("creates a Bundle with all the resources, including the dependencies", func() { - Eventually(func() bool { - bundle, err := cli.GetBundleFromOutput(buf) - Expect(err).NotTo(HaveOccurred()) - // files expected are: - // Chart.yaml + values.yaml + templates/configmap.yaml + - // Chart.lock + charts/config-chart-0.1.0.tgz - Expect(len(bundle.Spec.Resources)).To(Equal(5)) - files, err := getAllFilesInDir(tmpDir) - Expect(err).NotTo(HaveOccurred()) - Expect(len(files)).To(Equal(len(bundle.Spec.Resources))) - for _, file := range files { - presentInBundleResources(file, bundle.Spec.Resources) - } - // explicitly check for dependency files - presentInBundleResources(path.Join(tmpDir, "Chart.lock"), bundle.Spec.Resources) - presentInBundleResources(path.Join(tmpDir, "charts/config-chart-0.1.0.tgz"), bundle.Spec.Resources) - return true - }).Should(BeTrue()) + bundle, err := cli.GetBundleFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + // files expected are: + // Chart.yaml + values.yaml + templates/configmap.yaml + + // Chart.lock + charts/config-chart-0.1.0.tgz + Expect(len(bundle.Spec.Resources)).To(Equal(5)) + files, err := getAllFilesInDir(tmpDir) + Expect(err).NotTo(HaveOccurred()) + Expect(len(files)).To(Equal(len(bundle.Spec.Resources))) + for _, file := range files { + Expect(file).To(bePresentInBundleResources(bundle.Spec.Resources)) + } + // explicitly check for dependency files + Expect(path.Join(tmpDir, "Chart.lock")).To(bePresentInBundleResources(bundle.Spec.Resources)) + Expect(path.Join(tmpDir, "charts/config-chart-0.1.0.tgz")).To(bePresentInBundleResources(bundle.Spec.Resources)) }) }) @@ -307,24 +259,21 @@ var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func }) It("creates a Bundle with all the resources, including the dependencies", func() { - Eventually(func() bool { - bundle, err := cli.GetBundleFromOutput(buf) - Expect(err).NotTo(HaveOccurred()) - // files expected are: - // Chart.yaml + values.yaml + templates/configmap.yaml + fleet.yaml + - // Chart.lock + charts/config-chart-0.1.0.tgz - Expect(len(bundle.Spec.Resources)).To(Equal(6)) - files, err := getAllFilesInDir(tmpDirRel) - Expect(err).NotTo(HaveOccurred()) - Expect(len(files)).To(Equal(len(bundle.Spec.Resources))) - for _, file := range files { - presentInBundleResources(file, bundle.Spec.Resources) - } - // explicitly check for dependency files - presentInBundleResources(path.Join(tmpDirRel, "Chart.lock"), bundle.Spec.Resources) - presentInBundleResources(path.Join(tmpDirRel, "charts/config-chart-0.1.0.tgz"), bundle.Spec.Resources) - return true - }).Should(BeTrue()) + bundle, err := cli.GetBundleFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + // files expected are: + // Chart.yaml + values.yaml + templates/configmap.yaml + fleet.yaml + + // Chart.lock + charts/config-chart-0.1.0.tgz + Expect(len(bundle.Spec.Resources)).To(Equal(6)) + files, err := getAllFilesInDir(tmpDirRel) + Expect(err).NotTo(HaveOccurred()) + Expect(len(files)).To(Equal(len(bundle.Spec.Resources))) + for _, file := range files { + Expect(file).To(bePresentInBundleResources(bundle.Spec.Resources)) + } + // explicitly check for dependency files + Expect(path.Join(tmpDirRel, "Chart.lock")).To(bePresentInBundleResources(bundle.Spec.Resources)) + Expect(path.Join(tmpDirRel, "charts/config-chart-0.1.0.tgz")).To(bePresentInBundleResources(bundle.Spec.Resources)) }) }) @@ -334,23 +283,22 @@ var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func }) It("creates a Bundle with all the resources, dependencies should not be in the bundle", func() { - Eventually(func() bool { - bundle, err := cli.GetBundleFromOutput(buf) - Expect(err).NotTo(HaveOccurred()) - // files expected are: - // Chart.yaml + values.yaml + templates/configmap.yaml + fleet.yaml - Expect(len(bundle.Spec.Resources)).To(Equal(4)) - files, err := getAllFilesInDir(tmpDirRel) - Expect(err).NotTo(HaveOccurred()) - Expect(len(files)).To(Equal(len(bundle.Spec.Resources))) - for _, file := range files { - presentInBundleResources(file, bundle.Spec.Resources) - } - // explicitly check for dependency files (they should not exist) - notPresentInBundleResources(path.Join(tmpDirRel, "Chart.lock"), bundle.Spec.Resources) - notPresentInBundleResources(path.Join(tmpDirRel, "charts/config-chart-0.1.0.tgz"), bundle.Spec.Resources) - return true - }).Should(BeTrue()) + bundle, err := cli.GetBundleFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + // files expected are: + // Chart.yaml + values.yaml + templates/configmap.yaml + fleet.yaml + Expect(len(bundle.Spec.Resources)).To(Equal(4)) + files, err := getAllFilesInDir(tmpDirRel) + Expect(err).NotTo(HaveOccurred()) + Expect(len(files)).To(Equal(len(bundle.Spec.Resources))) + for _, file := range files { + Expect(file).To(bePresentInBundleResources(bundle.Spec.Resources)) + } + // explicitly check for dependency files (they should not exist in the file system nor in bundle resources) + Expect(path.Join(tmpDirRel, "Chart.lock")).NotTo(BeAnExistingFile()) + Expect(path.Join(tmpDirRel, "Chart.lock")).NotTo(bePresentOnlyInBundleResources(bundle.Spec.Resources)) + Expect(path.Join(tmpDirRel, "charts/config-chart-0.1.0.tgz")).NotTo(BeAnExistingFile()) + Expect(path.Join(tmpDirRel, "charts/config-chart-0.1.0.tgz")).NotTo(bePresentOnlyInBundleResources(bundle.Spec.Resources)) }) }) @@ -360,24 +308,21 @@ var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func }) It("creates a Bundle with all the resources, dependencies should be in the bundle", func() { - Eventually(func() bool { - bundle, err := cli.GetBundleFromOutput(buf) - Expect(err).NotTo(HaveOccurred()) - // expected files are: - // fleet.yaml + Chart.yaml + values.yaml + templates/configmap.yaml + - // Chart.lock + charts/config-chart-0.1.0.tgz - Expect(len(bundle.Spec.Resources)).To(Equal(6)) - presentInBundleResources(path.Join(tmpDirRel, "fleet.yaml"), bundle.Spec.Resources) - // as files were unpacked from the downloaded chart we can't just - // list the files in the original folder and compare. - // Files are only located in the bundle resources - onlyPresentInBundleResources("Chart.yaml", bundle.Spec.Resources) - onlyPresentInBundleResources("values.yaml", bundle.Spec.Resources) - onlyPresentInBundleResources("templates/configmap.yaml", bundle.Spec.Resources) - onlyPresentInBundleResources("Chart.lock", bundle.Spec.Resources) - onlyPresentInBundleResources("charts/config-chart-0.1.0.tgz", bundle.Spec.Resources) - return true - }).Should(BeTrue()) + bundle, err := cli.GetBundleFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + // expected files are: + // fleet.yaml + Chart.yaml + values.yaml + templates/configmap.yaml + + // Chart.lock + charts/config-chart-0.1.0.tgz + Expect(len(bundle.Spec.Resources)).To(Equal(6)) + Expect(path.Join(tmpDirRel, "fleet.yaml")).To(bePresentInBundleResources(bundle.Spec.Resources)) + // as files were unpacked from the downloaded chart we can't just + // list the files in the original folder and compare. + // Files are only located in the bundle resources + Expect("Chart.yaml").To(bePresentOnlyInBundleResources(bundle.Spec.Resources)) + Expect("values.yaml").To(bePresentOnlyInBundleResources(bundle.Spec.Resources)) + Expect("templates/configmap.yaml").To(bePresentOnlyInBundleResources(bundle.Spec.Resources)) + Expect("Chart.lock").To(bePresentOnlyInBundleResources(bundle.Spec.Resources)) + Expect("charts/config-chart-0.1.0.tgz").To(bePresentOnlyInBundleResources(bundle.Spec.Resources)) }) }) @@ -387,22 +332,25 @@ var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func }) It("creates a Bundle with all the resources, dependencies should not be in the bundle", func() { - Eventually(func() bool { - bundle, err := cli.GetBundleFromOutput(buf) - Expect(err).NotTo(HaveOccurred()) - // expected files are: - // fleet.yaml + - // Chart.yaml + values.yaml + templates/configmap.yaml - Expect(len(bundle.Spec.Resources)).To(Equal(4)) - presentInBundleResources(path.Join(tmpDirRel, "fleet.yaml"), bundle.Spec.Resources) - // as files were unpacked from the downloaded chart we can't just - // list the files in the original folder and compare. - // Files are only located in the bundle resources - onlyPresentInBundleResources("Chart.yaml", bundle.Spec.Resources) - onlyPresentInBundleResources("values.yaml", bundle.Spec.Resources) - onlyPresentInBundleResources("templates/configmap.yaml", bundle.Spec.Resources) - return true - }).Should(BeTrue()) + bundle, err := cli.GetBundleFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + // expected files are: + // fleet.yaml + + // Chart.yaml + values.yaml + templates/configmap.yaml + Expect(len(bundle.Spec.Resources)).To(Equal(4)) + Expect(path.Join(tmpDirRel, "fleet.yaml")).To(bePresentInBundleResources(bundle.Spec.Resources)) + // as files were unpacked from the downloaded chart we can't just + // list the files in the original folder and compare. + // Files are only located in the bundle resources + Expect("Chart.yaml").To(bePresentOnlyInBundleResources(bundle.Spec.Resources)) + Expect("values.yaml").To(bePresentOnlyInBundleResources(bundle.Spec.Resources)) + Expect("templates/configmap.yaml").To(bePresentOnlyInBundleResources(bundle.Spec.Resources)) + + // explicitly check for dependency files (they should not exist in the file system nor in bundle resources) + Expect(path.Join(tmpDirRel, "Chart.lock")).NotTo(BeAnExistingFile()) + Expect(path.Join(tmpDirRel, "Chart.lock")).NotTo(bePresentOnlyInBundleResources(bundle.Spec.Resources)) + Expect(path.Join(tmpDirRel, "charts/config-chart-0.1.0.tgz")).NotTo(BeAnExistingFile()) + Expect(path.Join(tmpDirRel, "charts/config-chart-0.1.0.tgz")).NotTo(bePresentOnlyInBundleResources(bundle.Spec.Resources)) }) }) @@ -412,59 +360,58 @@ var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func }) It("creates Bundles with the corresponding resources, depending if they should update dependencies", func() { - Eventually(func() bool { - bundle, err := cli.GetBundleListFromOutput(buf) - Expect(err).NotTo(HaveOccurred()) - Expect(len(bundle)).To(Equal(3)) - remoteDepl := bundle[0] - simpleDepl := bundle[1] - noDepsDepl := bundle[2] - - // remoteDepl corresponds to multi-chart/remote-chart-with-deps - // expected files are: - // fleet.yaml + - // Chart.yaml + values.yaml + templates/configmap.yaml + Chart.lock + charts/config-chart-0.1.0.tgz - Expect(len(remoteDepl.Spec.Resources)).To(Equal(6)) - presentInBundleResources(path.Join(tmpDirRel, "remote-chart-with-deps", "fleet.yaml"), remoteDepl.Spec.Resources) - // as files were unpacked from the downloaded chart we can't just - // list the files in the original folder and compare. - // Files are only located in the bundle resources - onlyPresentInBundleResources("Chart.yaml", remoteDepl.Spec.Resources) - onlyPresentInBundleResources("values.yaml", remoteDepl.Spec.Resources) - onlyPresentInBundleResources("templates/configmap.yaml", remoteDepl.Spec.Resources) - onlyPresentInBundleResources("Chart.lock", remoteDepl.Spec.Resources) - onlyPresentInBundleResources("charts/config-chart-0.1.0.tgz", remoteDepl.Spec.Resources) - - // simpleDepl corresponds to multi-chart/simple-with-fleet-yaml - // expected files are: - // fleet.yaml + Chart.yaml + values.yaml + templates/configmap.yaml + - // Chart.lock + charts/config-chart-0.1.0.tgz - Expect(len(simpleDepl.Spec.Resources)).To(Equal(6)) - files, err := getAllFilesInDir(path.Join(tmpDirRel, "simple-with-fleet-yaml")) - Expect(err).NotTo(HaveOccurred()) - Expect(len(files)).To(Equal(len(simpleDepl.Spec.Resources))) - for _, file := range files { - presentInBundleResources(file, simpleDepl.Spec.Resources) - } - // explicitly check for dependency files - presentInBundleResources(path.Join(tmpDirRel, "simple-with-fleet-yaml", "Chart.lock"), simpleDepl.Spec.Resources) - presentInBundleResources(path.Join(tmpDirRel, "simple-with-fleet-yaml", "charts/config-chart-0.1.0.tgz"), simpleDepl.Spec.Resources) - - // noDepsDepl corresponds to multi-char/simple-with-fleet-yaml-no-deps - // expected files are: - // Chart.yaml + fleet.yaml + values.yaml + templates/configmap.yaml - Expect(len(noDepsDepl.Spec.Resources)).To(Equal(4)) - files, err = getAllFilesInDir(path.Join(tmpDirRel, "simple-with-fleet-yaml-no-deps")) - Expect(err).NotTo(HaveOccurred()) - Expect(len(files)).To(Equal(len(noDepsDepl.Spec.Resources))) - for _, file := range files { - presentInBundleResources(file, noDepsDepl.Spec.Resources) - } - // explicitly check for dependency files (they should not exist) - notPresentInBundleResources(path.Join(tmpDirRel, "simple-with-fleet-yaml-no-deps", "Chart.lock"), noDepsDepl.Spec.Resources) - notPresentInBundleResources(path.Join(tmpDirRel, "simple-with-fleet-yaml-no-deps", "charts/config-chart-0.1.0.tgz"), noDepsDepl.Spec.Resources) - return true - }).Should(BeTrue()) + bundle, err := cli.GetBundleListFromOutput(buf) + Expect(err).NotTo(HaveOccurred()) + Expect(len(bundle)).To(Equal(3)) + remoteDepl := bundle[0] + simpleDepl := bundle[1] + noDepsDepl := bundle[2] + + // remoteDepl corresponds to multi-chart/remote-chart-with-deps + // expected files are: + // fleet.yaml + + // Chart.yaml + values.yaml + templates/configmap.yaml + Chart.lock + charts/config-chart-0.1.0.tgz + Expect(len(remoteDepl.Spec.Resources)).To(Equal(6)) + Expect(path.Join(tmpDirRel, "remote-chart-with-deps", "fleet.yaml")).To(bePresentInBundleResources(remoteDepl.Spec.Resources)) + // as files were unpacked from the downloaded chart we can't just + // list the files in the original folder and compare. + // Files are only located in the bundle resources + Expect("Chart.yaml").To(bePresentOnlyInBundleResources(remoteDepl.Spec.Resources)) + Expect("values.yaml").To(bePresentOnlyInBundleResources(remoteDepl.Spec.Resources)) + Expect("templates/configmap.yaml").To(bePresentOnlyInBundleResources(remoteDepl.Spec.Resources)) + Expect("Chart.lock").To(bePresentOnlyInBundleResources(remoteDepl.Spec.Resources)) + Expect("charts/config-chart-0.1.0.tgz").To(bePresentOnlyInBundleResources(remoteDepl.Spec.Resources)) + + // simpleDepl corresponds to multi-chart/simple-with-fleet-yaml + // expected files are: + // fleet.yaml + Chart.yaml + values.yaml + templates/configmap.yaml + + // Chart.lock + charts/config-chart-0.1.0.tgz + Expect(len(simpleDepl.Spec.Resources)).To(Equal(6)) + files, err := getAllFilesInDir(path.Join(tmpDirRel, "simple-with-fleet-yaml")) + Expect(err).NotTo(HaveOccurred()) + Expect(len(files)).To(Equal(len(simpleDepl.Spec.Resources))) + for _, file := range files { + Expect(file).To(bePresentInBundleResources(simpleDepl.Spec.Resources)) + } + // explicitly check for dependency files + Expect(path.Join(tmpDirRel, "simple-with-fleet-yaml", "Chart.lock")).To(bePresentInBundleResources(simpleDepl.Spec.Resources)) + Expect(path.Join(tmpDirRel, "simple-with-fleet-yaml", "charts/config-chart-0.1.0.tgz")).To(bePresentInBundleResources(simpleDepl.Spec.Resources)) + + // noDepsDepl corresponds to multi-char/simple-with-fleet-yaml-no-deps + // expected files are: + // Chart.yaml + fleet.yaml + values.yaml + templates/configmap.yaml + Expect(len(noDepsDepl.Spec.Resources)).To(Equal(4)) + files, err = getAllFilesInDir(path.Join(tmpDirRel, "simple-with-fleet-yaml-no-deps")) + Expect(err).NotTo(HaveOccurred()) + Expect(len(files)).To(Equal(len(noDepsDepl.Spec.Resources))) + for _, file := range files { + Expect(file).To(bePresentInBundleResources(noDepsDepl.Spec.Resources)) + } + // explicitly check for dependency files (they should not exist in the file system nor in bundle resources) + Expect(path.Join(tmpDirRel, "simple-with-fleet-yaml-no-deps", "Chart.lock")).NotTo(BeAnExistingFile()) + Expect(path.Join(tmpDirRel, "simple-with-fleet-yaml-no-deps", "Chart.lock")).NotTo(bePresentOnlyInBundleResources(noDepsDepl.Spec.Resources)) + Expect(path.Join(tmpDirRel, "simple-with-fleet-yaml-no-deps", "charts/config-chart-0.1.0.tgz")).NotTo(BeAnExistingFile()) + Expect(path.Join(tmpDirRel, "simple-with-fleet-yaml-no-deps", "charts/config-chart-0.1.0.tgz")).NotTo(bePresentOnlyInBundleResources(noDepsDepl.Spec.Resources)) }) }) @@ -474,26 +421,34 @@ var _ = Describe("Fleet apply with helm charts with dependencies", Ordered, func }) }) -func presentInBundleResources(path string, resources []v1alpha1.BundleResource) { - isPresent, err := cli.IsResourcePresentInBundle(path, resources) - Expect(err).NotTo(HaveOccurred()) - Expect(isPresent).Should(BeTrue()) -} - -func onlyPresentInBundleResources(path string, resources []v1alpha1.BundleResource) { - found := false - for _, resource := range resources { - if strings.HasSuffix(resource.Name, path) { - found = true +func bePresentInBundleResources(expected interface{}) types.GomegaMatcher { + return gcustom.MakeMatcher(func(path string) (bool, error) { + resources, ok := expected.([]v1alpha1.BundleResource) + if !ok { + return false, fmt.Errorf("BePresentInBundleResources matcher expects []v1alpha1.BundleResource") } - } - Expect(found).Should(BeTrue()) + isPresent, err := cli.IsResourcePresentInBundle(path, resources) + if err != nil { + return false, fmt.Errorf("Failed to check for path in resources: %s", err.Error()) + } + return isPresent, nil + }).WithTemplate("Expected:\n{{.FormattedActual}}\n{{.To}} be present in \n{{format .Data 1}}").WithTemplateData(expected) } -func notPresentInBundleResources(path string, resources []v1alpha1.BundleResource) { - isPresent, err := cli.IsResourcePresentInBundle(path, resources) - Expect(err).To(HaveOccurred()) - Expect(isPresent).Should(BeFalse()) +func bePresentOnlyInBundleResources(expected interface{}) types.GomegaMatcher { + return gcustom.MakeMatcher(func(path string) (bool, error) { + resources, ok := expected.([]v1alpha1.BundleResource) + if !ok { + return false, fmt.Errorf("bePresentOnlyInBundleResources matcher expects []v1alpha1.BundleResource") + } + found := false + for _, resource := range resources { + if strings.HasSuffix(resource.Name, path) { + found = true + } + } + return found, nil + }).WithTemplate("Expected:\n{{.FormattedActual}}\n{{.To}} be present in \n{{format .Data 1}}").WithTemplateData(expected) } func getAllFilesInDir(chartPath string) ([]string, error) {