From 84cd57db8a31e4b7c3ef300c47e81bd93dbe699b Mon Sep 17 00:00:00 2001 From: Mario Manno Date: Wed, 22 Mar 2023 16:58:17 +0100 Subject: [PATCH 1/3] Support ConfigMapKeyRef and SecretKeyRef in same list element --- pkg/helmdeployer/deployer.go | 79 +++++++++++++++++++------------ pkg/helmdeployer/deployer_test.go | 6 +-- 2 files changed, 52 insertions(+), 33 deletions(-) diff --git a/pkg/helmdeployer/deployer.go b/pkg/helmdeployer/deployer.go index 29cac9d93d..7c6c7732cb 100644 --- a/pkg/helmdeployer/deployer.go +++ b/pkg/helmdeployer/deployer.go @@ -436,39 +436,46 @@ func (h *Helm) getValues(options fleet.BundleDeploymentOptions, defaultNamespace if !h.template { for _, valuesFrom := range options.Helm.ValuesFrom { var tempValues map[string]interface{} - if valuesFrom.SecretKeyRef != nil { - name := valuesFrom.SecretKeyRef.Name - namespace := valuesFrom.SecretKeyRef.Namespace + if valuesFrom.ConfigMapKeyRef != nil { + name := valuesFrom.ConfigMapKeyRef.Name + namespace := valuesFrom.ConfigMapKeyRef.Namespace if namespace == "" { namespace = defaultNamespace } - key := valuesFrom.SecretKeyRef.Key + key := valuesFrom.ConfigMapKeyRef.Key if key == "" { key = DefaultKey } - secret, err := h.secretCache.Get(namespace, name) + configMap, err := h.configmapCache.Get(namespace, name) if err != nil { return nil, err } - tempValues, err = processValuesFromObject(name, namespace, key, secret, nil) + tempValues, err = valuesFromConfigMap(name, namespace, key, configMap) if err != nil { return nil, err } - } else if valuesFrom.ConfigMapKeyRef != nil { - name := valuesFrom.ConfigMapKeyRef.Name - namespace := valuesFrom.ConfigMapKeyRef.Namespace + } + if tempValues != nil { + values = mergeValues(values, tempValues) + tempValues = nil + } + + // merge secret last to be compatible with fleet <= 0.6.0 + if valuesFrom.SecretKeyRef != nil { + name := valuesFrom.SecretKeyRef.Name + namespace := valuesFrom.SecretKeyRef.Namespace if namespace == "" { namespace = defaultNamespace } - key := valuesFrom.ConfigMapKeyRef.Key + key := valuesFrom.SecretKeyRef.Key if key == "" { key = DefaultKey } - configMap, err := h.configmapCache.Get(namespace, name) + secret, err := h.secretCache.Get(namespace, name) if err != nil { return nil, err } - tempValues, err = processValuesFromObject(name, namespace, key, nil, configMap) + tempValues, err = valuesFromSecret(name, namespace, key, secret) if err != nil { return nil, err } @@ -731,24 +738,34 @@ func deleteHistory(cfg action.Configuration, bundleID string) error { return nil } -func processValuesFromObject(name, namespace, key string, secret *corev1.Secret, configMap *corev1.ConfigMap) (map[string]interface{}, error) { +func valuesFromSecret(name, namespace, key string, secret *corev1.Secret) (map[string]interface{}, error) { var m map[string]interface{} - if secret != nil { - values, ok := secret.Data[key] - if !ok { - return nil, fmt.Errorf("key %s is missing from secret %s/%s, can't use it in valuesFrom", key, namespace, name) - } - if err := yaml.Unmarshal(values, &m); err != nil { - return nil, err - } - } else if configMap != nil { - values, ok := configMap.Data[key] - if !ok { - return nil, fmt.Errorf("key %s is missing from configmap %s/%s, can't use it in valuesFrom", key, namespace, name) - } - if err := yaml.Unmarshal([]byte(values), &m); err != nil { - return nil, err - } + if secret == nil { + return m, nil + } + + values, ok := secret.Data[key] + if !ok { + return nil, fmt.Errorf("key %s is missing from secret %s/%s, can't use it in valuesFrom", key, namespace, name) + } + if err := yaml.Unmarshal(values, &m); err != nil { + return nil, err + } + return m, nil +} + +func valuesFromConfigMap(name, namespace, key string, configMap *corev1.ConfigMap) (map[string]interface{}, error) { + var m map[string]interface{} + if configMap == nil { + return m, nil + } + + values, ok := configMap.Data[key] + if !ok { + return nil, fmt.Errorf("key %s is missing from configmap %s/%s, can't use it in valuesFrom", key, namespace, name) + } + if err := yaml.Unmarshal([]byte(values), &m); err != nil { + return nil, err } return m, nil } @@ -764,19 +781,21 @@ func mergeMaps(base, other map[string]string) map[string]string { return result } -// mergeValues merges source and destination map, preferring values +// mergeValues merges source and destination map, preferring values over maps // from the source values. This is slightly adapted from: // https://github.com/helm/helm/blob/2332b480c9cb70a0d8a85247992d6155fbe82416/cmd/helm/install.go#L359 func mergeValues(dest, src map[string]interface{}) map[string]interface{} { for k, v := range src { // If the key doesn't exist already, then just set the key to that value if _, exists := dest[k]; !exists { + // new key dest[k] = v continue } nextMap, ok := v.(map[string]interface{}) // If it isn't another map, overwrite the value if !ok { + // new key is not a map, overwrite existing key as we prefer values over maps dest[k] = v continue } diff --git a/pkg/helmdeployer/deployer_test.go b/pkg/helmdeployer/deployer_test.go index a0f60f60d0..f8ace507a2 100644 --- a/pkg/helmdeployer/deployer_test.go +++ b/pkg/helmdeployer/deployer_test.go @@ -32,7 +32,7 @@ func TestValuesFrom(t *testing.T) { configMapName := "configmap-name" configMapNamespace := "configmap-namespace" - configMapValues, err := processValuesFromObject(configMapName, configMapNamespace, key, nil, &corev1.ConfigMap{ + configMapValues, err := valuesFromConfigMap(configMapName, configMapNamespace, key, &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: configMapName, Namespace: configMapNamespace, @@ -45,7 +45,7 @@ func TestValuesFrom(t *testing.T) { secretName := "secret-name" secretNamespace := "secret-namespace" - secretValues, err := processValuesFromObject(secretName, secretNamespace, key, &corev1.Secret{ + secretValues, err := valuesFromSecret(secretName, secretNamespace, key, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: secretName, Namespace: secretNamespace, @@ -53,7 +53,7 @@ func TestValuesFrom(t *testing.T) { Data: map[string][]byte{ key: []byte(secretPayload), }, - }, nil) + }) a.NoError(err) totalValues = mergeValues(totalValues, secretValues) From 57f956f848d00b7ad1566b2465d4b36a918f4f33 Mon Sep 17 00:00:00 2001 From: Mario Manno Date: Wed, 22 Mar 2023 17:53:40 +0100 Subject: [PATCH 2/3] Clean up e2e asset locations and package names --- .github/workflows/fleet-upgrade-in-rancher.yml | 4 ++-- e2e/assets/{ => fleet-upgrade}/gitrepo-simple.yaml | 0 e2e/assets/{simple/gitrepo.yaml => installation/verify.yaml} | 0 e2e/installation/verify_test.go | 2 +- e2e/multi-cluster/bundle_namespace_mapping_test.go | 2 +- e2e/multi-cluster/suite_test.go | 3 ++- e2e/single-cluster/bundle_diffs_test.go | 2 +- e2e/single-cluster/helm_verify_test.go | 2 +- e2e/single-cluster/release_names_test.go | 4 ++-- e2e/single-cluster/single_cluster_test.go | 5 ++--- e2e/single-cluster/suite_test.go | 3 ++- 11 files changed, 14 insertions(+), 13 deletions(-) rename e2e/assets/{ => fleet-upgrade}/gitrepo-simple.yaml (100%) rename e2e/assets/{simple/gitrepo.yaml => installation/verify.yaml} (100%) diff --git a/.github/workflows/fleet-upgrade-in-rancher.yml b/.github/workflows/fleet-upgrade-in-rancher.yml index 41f8e5e513..49b625fcd4 100644 --- a/.github/workflows/fleet-upgrade-in-rancher.yml +++ b/.github/workflows/fleet-upgrade-in-rancher.yml @@ -137,8 +137,8 @@ jobs: - name: Create example workload run: | - kubectl apply -n fleet-local -f e2e/assets/gitrepo-simple.yaml - kubectl apply -n fleet-default -f e2e/assets/gitrepo-simple.yaml + kubectl apply -n fleet-local -f e2e/assets/fleet-upgrade/gitrepo-simple.yaml + kubectl apply -n fleet-default -f e2e/assets/fleet-upgrade/gitrepo-simple.yaml - name: Deploy latest fleet env: diff --git a/e2e/assets/gitrepo-simple.yaml b/e2e/assets/fleet-upgrade/gitrepo-simple.yaml similarity index 100% rename from e2e/assets/gitrepo-simple.yaml rename to e2e/assets/fleet-upgrade/gitrepo-simple.yaml diff --git a/e2e/assets/simple/gitrepo.yaml b/e2e/assets/installation/verify.yaml similarity index 100% rename from e2e/assets/simple/gitrepo.yaml rename to e2e/assets/installation/verify.yaml diff --git a/e2e/installation/verify_test.go b/e2e/installation/verify_test.go index 3f064a18f4..d2b9f737a7 100644 --- a/e2e/installation/verify_test.go +++ b/e2e/installation/verify_test.go @@ -80,7 +80,7 @@ var _ = Describe("Fleet Installation", func() { When("Deploying another bundle still works", func() { var tmpdir string BeforeEach(func() { - asset = "simple/gitrepo.yaml" + asset = "installation/verify.yaml" }) JustBeforeEach(func() { diff --git a/e2e/multi-cluster/bundle_namespace_mapping_test.go b/e2e/multi-cluster/bundle_namespace_mapping_test.go index e1e6adbdf1..5380606209 100644 --- a/e2e/multi-cluster/bundle_namespace_mapping_test.go +++ b/e2e/multi-cluster/bundle_namespace_mapping_test.go @@ -1,4 +1,4 @@ -package mc_examples_test +package multicluster_test import ( "os" diff --git a/e2e/multi-cluster/suite_test.go b/e2e/multi-cluster/suite_test.go index 1ad01194a3..f3fd196eb8 100644 --- a/e2e/multi-cluster/suite_test.go +++ b/e2e/multi-cluster/suite_test.go @@ -1,4 +1,5 @@ -package mc_examples_test +// Package multicluster contains e2e tests deploying to multiple clusters. The tests use kubectl to apply manifests. Expectations are verified by checking cluster resources. Assets refer to the https://github.com/rancher/fleet-test-data git repo. +package multicluster_test import ( "testing" diff --git a/e2e/single-cluster/bundle_diffs_test.go b/e2e/single-cluster/bundle_diffs_test.go index 913c7dfd5b..65aa4226e7 100644 --- a/e2e/single-cluster/bundle_diffs_test.go +++ b/e2e/single-cluster/bundle_diffs_test.go @@ -1,4 +1,4 @@ -package examples_test +package singlecluster_test import ( "encoding/json" diff --git a/e2e/single-cluster/helm_verify_test.go b/e2e/single-cluster/helm_verify_test.go index 543359d448..e8aa170454 100644 --- a/e2e/single-cluster/helm_verify_test.go +++ b/e2e/single-cluster/helm_verify_test.go @@ -1,4 +1,4 @@ -package examples_test +package singlecluster_test import ( "github.com/rancher/fleet/e2e/testenv" diff --git a/e2e/single-cluster/release_names_test.go b/e2e/single-cluster/release_names_test.go index 5db581fec8..af148759da 100644 --- a/e2e/single-cluster/release_names_test.go +++ b/e2e/single-cluster/release_names_test.go @@ -1,4 +1,4 @@ -package examples_test +package singlecluster_test import ( "strings" @@ -10,7 +10,7 @@ import ( . "github.com/onsi/gomega" ) -var _ = Describe("ReleaseName", func() { +var _ = Describe("Release Name", func() { var ( asset string k kubectl.Command diff --git a/e2e/single-cluster/single_cluster_test.go b/e2e/single-cluster/single_cluster_test.go index 05b9c392cc..d3d699089b 100644 --- a/e2e/single-cluster/single_cluster_test.go +++ b/e2e/single-cluster/single_cluster_test.go @@ -1,4 +1,4 @@ -package examples_test +package singlecluster_test import ( "strings" @@ -10,8 +10,7 @@ import ( . "github.com/onsi/gomega" ) -// These tests use the examples from https://github.com/rancher/fleet-examples/tree/master/single-cluster -var _ = Describe("Single Cluster Examples", func() { +var _ = Describe("Single Cluster Deployments", func() { var ( asset string k kubectl.Command diff --git a/e2e/single-cluster/suite_test.go b/e2e/single-cluster/suite_test.go index b48b37611c..2f40434c2e 100644 --- a/e2e/single-cluster/suite_test.go +++ b/e2e/single-cluster/suite_test.go @@ -1,4 +1,5 @@ -package examples_test +// Package singlecluster contains e2e tests deploying to a single cluster. The tests use kubectl to apply manifests. Expectations are verified by checking cluster resources. Assets refer to the https://github.com/rancher/fleet-test-data git repo. +package singlecluster_test import ( "testing" From 0985534d1ce1dd6307275edc73aa073478105e49 Mon Sep 17 00:00:00 2001 From: Mario Manno Date: Thu, 23 Mar 2023 12:03:01 +0100 Subject: [PATCH 3/3] Add e2e test for valuesFrom references --- e2e/assets/gitrepo-template.yaml | 14 ++++ .../single-cluster/values-from-configmap.yaml | 6 ++ .../single-cluster/values-from-secret.yaml | 6 ++ .../bundle_namespace_mapping_test.go | 11 +-- e2e/require-secrets/gitrepo_test.go | 15 ++-- e2e/single-cluster/bundle_diffs_test.go | 2 +- e2e/single-cluster/values_from_test.go | 75 +++++++++++++++++++ e2e/testenv/env.go | 9 ++- e2e/testenv/template.go | 44 ++++++++++- 9 files changed, 159 insertions(+), 23 deletions(-) create mode 100644 e2e/assets/gitrepo-template.yaml create mode 100644 e2e/assets/single-cluster/values-from-configmap.yaml create mode 100644 e2e/assets/single-cluster/values-from-secret.yaml create mode 100644 e2e/single-cluster/values_from_test.go diff --git a/e2e/assets/gitrepo-template.yaml b/e2e/assets/gitrepo-template.yaml new file mode 100644 index 0000000000..8f4f331577 --- /dev/null +++ b/e2e/assets/gitrepo-template.yaml @@ -0,0 +1,14 @@ +kind: GitRepo +apiVersion: fleet.cattle.io/v1alpha1 +metadata: + name: {{ .Name }} + +spec: + repo: https://github.com/rancher/fleet-test-data + branch: {{ .Branch }} + paths: + {{- range .Paths}} + - {{.}} + {{- end}} + + targetNamespace: {{ .TargetNamespace }} diff --git a/e2e/assets/single-cluster/values-from-configmap.yaml b/e2e/assets/single-cluster/values-from-configmap.yaml new file mode 100644 index 0000000000..174c1404de --- /dev/null +++ b/e2e/assets/single-cluster/values-from-configmap.yaml @@ -0,0 +1,6 @@ +name: configmap overrides values from fleet.yaml +config: config option +options: + onlyconfigmap: configmap option + english: + name: configmap override diff --git a/e2e/assets/single-cluster/values-from-secret.yaml b/e2e/assets/single-cluster/values-from-secret.yaml new file mode 100644 index 0000000000..0380ae0a43 --- /dev/null +++ b/e2e/assets/single-cluster/values-from-secret.yaml @@ -0,0 +1,6 @@ +name: secret overrides values from fleet.yaml +secret: xyz secret +options: + onlysecret: secret option + english: + name: secret override diff --git a/e2e/multi-cluster/bundle_namespace_mapping_test.go b/e2e/multi-cluster/bundle_namespace_mapping_test.go index 5380606209..a74ea381df 100644 --- a/e2e/multi-cluster/bundle_namespace_mapping_test.go +++ b/e2e/multi-cluster/bundle_namespace_mapping_test.go @@ -1,8 +1,6 @@ package multicluster_test import ( - "os" - "path" "time" "github.com/rancher/fleet/e2e/testenv" @@ -20,7 +18,6 @@ var _ = Describe("Bundle Namespace Mapping", Label("difficult"), func() { asset string namespace string data any - tmpdir string interval = 2 * time.Second duration = 30 * time.Second @@ -40,19 +37,13 @@ var _ = Describe("Bundle Namespace Mapping", Label("difficult"), func() { }) JustBeforeEach(func() { - tmpdir, _ = os.MkdirTemp("", "fleet-") - output := path.Join(tmpdir, testenv.RandomFilename("manifests.yaml")) - err := testenv.Template(output, testenv.AssetPath(asset), data) + err := testenv.ApplyTemplate(k.Namespace(namespace), testenv.AssetPath(asset), data) Expect(err).ToNot(HaveOccurred()) - - out, err := k.Namespace(namespace).Apply("-f", output) - Expect(err).ToNot(HaveOccurred(), out) }) AfterEach(func() { out, err := k.Delete("ns", namespace) Expect(err).ToNot(HaveOccurred(), out) - os.RemoveAll(tmpdir) }) Context("with bundlenamespacemapping", func() { diff --git a/e2e/require-secrets/gitrepo_test.go b/e2e/require-secrets/gitrepo_test.go index c77b722f6e..0f4daf0a6f 100644 --- a/e2e/require-secrets/gitrepo_test.go +++ b/e2e/require-secrets/gitrepo_test.go @@ -45,23 +45,20 @@ var _ = Describe("Git Repo", func() { ) Expect(err).ToNot(HaveOccurred(), out) - tmpdir, _ = os.MkdirTemp("", "fleet-") - repodir = path.Join(tmpdir, "repo") - repo, err = gh.Create(repodir, testenv.AssetPath("gitrepo/sleeper-chart"), "examples") - Expect(err).ToNot(HaveOccurred()) - - gitrepo := path.Join(tmpdir, "gitrepo.yaml") - err = testenv.Template(gitrepo, testenv.AssetPath("gitrepo/gitrepo.yaml"), struct { + err = testenv.ApplyTemplate(k, testenv.AssetPath("gitrepo/gitrepo.yaml"), struct { Repo string Branch string }{ gh.URL, gh.Branch, }) + Expect(err).ToNot(HaveOccurred(), out) + + tmpdir, _ = os.MkdirTemp("", "fleet-") + repodir = path.Join(tmpdir, "repo") + repo, err = gh.Create(repodir, testenv.AssetPath("gitrepo/sleeper-chart"), "examples") Expect(err).ToNot(HaveOccurred()) - out, err = k.Apply("-f", gitrepo) - Expect(err).ToNot(HaveOccurred(), out) }) AfterEach(func() { diff --git a/e2e/single-cluster/bundle_diffs_test.go b/e2e/single-cluster/bundle_diffs_test.go index 65aa4226e7..ec29eb19d0 100644 --- a/e2e/single-cluster/bundle_diffs_test.go +++ b/e2e/single-cluster/bundle_diffs_test.go @@ -62,7 +62,7 @@ var _ = Describe("BundleDiffs", func() { } return false - }).Should(BeTrue(), "bundledepoloyment should be deleted") + }).Should(BeTrue(), "bundledeployment should be deleted") }) }) diff --git a/e2e/single-cluster/values_from_test.go b/e2e/single-cluster/values_from_test.go new file mode 100644 index 0000000000..8d82aa3f2d --- /dev/null +++ b/e2e/single-cluster/values_from_test.go @@ -0,0 +1,75 @@ +package singlecluster_test + +import ( + "encoding/json" + + "github.com/rancher/fleet/e2e/testenv" + "github.com/rancher/fleet/e2e/testenv/kubectl" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("ValuesFrom", func() { + var ( + // k is the kubectl command for the cluster registration namespace + k kubectl.Command + // kw is the kubectl command for namespace the workload is deployed to + kw kubectl.Command + namespace string + ) + + BeforeEach(func() { + k = env.Kubectl.Namespace(env.Namespace) + namespace = testenv.NewNamespaceName("values-from") + kw = k.Namespace(namespace) + + out, err := k.Create("ns", namespace) + Expect(err).ToNot(HaveOccurred(), out) + + out, err = k.Namespace(namespace).Create("secret", "generic", "secret-values", + "--from-file=values.yaml="+testenv.AssetPath("single-cluster/values-from-secret.yaml")) + Expect(err).ToNot(HaveOccurred(), out) + + out, err = k.Namespace("default").Create("configmap", "configmap-values", + "--from-file=values.yaml="+testenv.AssetPath("single-cluster/values-from-configmap.yaml")) + Expect(err).ToNot(HaveOccurred(), out) + + err = testenv.CreateGitRepo(k, namespace, "values-from", "master", "helm-values-from") + Expect(err).ToNot(HaveOccurred()) + + DeferCleanup(func() { + out, err := k.Delete("gitrepo", "values-from") + Expect(err).ToNot(HaveOccurred(), out) + out, err = k.Delete("ns", namespace) + Expect(err).ToNot(HaveOccurred(), out) + out, err = k.Namespace("default").Delete("configmap", "configmap-values") + Expect(err).ToNot(HaveOccurred(), out) + }) + }) + + When("fleet.yaml makes use of valuesFrom", func() { + Context("referencing a secret as well as a configmap", func() { + It("all referenced resources are available as values to the chart", func() { + Eventually(func() bool { + _, err := kw.Get("configmap", "result") + return err == nil + }).Should(BeTrue()) + + out, err := kw.Get("configmap", "result", "-o", "jsonpath={.data}") + Expect(err).ToNot(HaveOccurred(), out) + result := map[string]string{} + err = json.Unmarshal([]byte(out), &result) + Expect(err).ToNot(HaveOccurred()) + + Expect(result).To(HaveKeyWithValue("name", "secret overrides values from fleet.yaml")) + Expect(result).To(HaveKeyWithValue("secret", "xyz secret")) + Expect(result).To(HaveKeyWithValue("config", "config option")) + Expect(result).To(HaveKeyWithValue("fleetyaml", "from fleet.yaml")) + Expect(result).To(HaveKeyWithValue("englishname", "secret override")) + Expect(result).To(HaveKeyWithValue("optionconfigmap", "configmap option")) + Expect(result).To(HaveKeyWithValue("optionsecret", "secret option")) + }) + }) + }) +}) diff --git a/e2e/testenv/env.go b/e2e/testenv/env.go index d490a2de94..fc895acc05 100644 --- a/e2e/testenv/env.go +++ b/e2e/testenv/env.go @@ -16,9 +16,11 @@ const Timeout = 5 * time.Minute type Env struct { Kubectl kubectl.Command // Upstream context for cluster containing the fleet controller and local agent - Upstream string + Upstream string + // Downstream context for fleet-agent cluster Downstream string - Namespace string + // Namespace which contains the cluster resource (cluster registration namespace) + Namespace string } func New() *Env { @@ -43,6 +45,9 @@ func (e *Env) getShellEnv() { e.Namespace = val } } + +// NewNamespaceName returns a name for a namespace that is unique to the test +// run. e.g. as a targetNamespace for workloads func NewNamespaceName(name string) string { rand.Seed(time.Now().UnixNano()) p := make([]byte, 12) diff --git a/e2e/testenv/template.go b/e2e/testenv/template.go index 5c7d0a5aa3..0724de47a2 100644 --- a/e2e/testenv/template.go +++ b/e2e/testenv/template.go @@ -1,14 +1,55 @@ package testenv import ( + "fmt" "html/template" "math/rand" "os" "path" "strconv" "strings" + + "github.com/rancher/fleet/e2e/testenv/kubectl" + "github.com/rancher/gitjob/e2e/testenv" ) +const gitrepoTemplate = "gitrepo-template.yaml" + +// GitRepoData can be used with the gitrepo-template.yaml asset when no custom +// GitRepo properties are required. All fields are required. +type GitRepoData struct { + Name string + Branch string + Paths []string + TargetNamespace string +} + +// CreateGitRepo uses the template to create a gitrepo resource. The namespace is the TargetNamespace for the workloads. +func CreateGitRepo(k kubectl.Command, namespace string, name string, branch string, paths ...string) error { + return ApplyTemplate(k, AssetPath(gitrepoTemplate), GitRepoData{ + TargetNamespace: namespace, + Name: name, + Branch: branch, + Paths: paths, + }) +} + +// ApplyTemplate templates a file and applies it to the cluster. +func ApplyTemplate(k kubectl.Command, asset string, data interface{}) error { + tmpdir, _ := os.MkdirTemp("", "fleet-") + defer os.RemoveAll(tmpdir) + + output := path.Join(tmpdir, RandomFilename(asset)) + if err := Template(output, testenv.AssetPath(asset), data); err != nil { + return err + } + out, err := k.Apply("-f", output) + if err != nil { + return fmt.Errorf("%w: %s", err, out) + } + return nil +} + // Template loads a gotemplate from a file and writes templated output to // another file, preferably in a temp dir func Template(output string, tmplPath string, data any) error { @@ -33,6 +74,7 @@ func Template(output string, tmplPath string, data any) error { // RandomName returns a slightly random name, so temporary assets don't conflict func RandomFilename(filename string) string { ext := path.Ext(filename) + filename = path.Base(filename) r := strconv.Itoa(rand.Intn(99999)) // nolint:gosec // Non-crypto use - return strings.TrimSuffix(filename, ext) + r + ext + return strings.TrimSuffix(filename, ext) + r + "." + ext }