Skip to content

Commit

Permalink
Fix namespace target customization support with no defaults (#3052)
Browse files Browse the repository at this point in the history
* Improve namespace target customization tests

These tests now verify that the created namespace does bear expected
labels and annotations.
This commit also paves the way for additional tests with customizations
over unconfigured namespace labels and annotations, which currently
cause a panic.

* Initialise options maps when empty

This prevents panics when namespace labels or annotations are
configured as target customizations over nonexistent defaults.

* Use main branch of `rancher/fleet-test-data

The required changes made in that repo have been merged.
  • Loading branch information
weyfonk committed Nov 15, 2024
1 parent 02e4067 commit bfa033e
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 102 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
kind: GitRepo
apiVersion: fleet.cattle.io/v1alpha1
metadata:
name: test
namespace: fleet-local
spec:
repo: https://github.com/rancher/fleet-test-data
branch: master
paths:
- target-customization-namespace-labels/without-default-values
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ spec:
repo: https://github.com/rancher/fleet-test-data
branch: master
paths:
- target-customization-namespace-labels
- target-customization-namespace-labels/with-default-values
101 changes: 0 additions & 101 deletions e2e/single-cluster/targetCustomization_test.go

This file was deleted.

166 changes: 166 additions & 0 deletions e2e/single-cluster/target_customization_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package singlecluster_test

import (
"strings"

"github.com/rancher/fleet/e2e/testenv"
"github.com/rancher/fleet/e2e/testenv/kubectl"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("Helm deploy options", func() {
var (
asset string
k kubectl.Command
)
BeforeEach(func() {
k = env.Kubectl.Namespace(env.Namespace)
})

JustBeforeEach(func() {
out, err := k.Apply("-f", testenv.AssetPath(asset))
Expect(err).ToNot(HaveOccurred(), out)
})

AfterEach(func() {
out, err := k.Delete("-f", testenv.AssetPath(asset))
Expect(err).ToNot(HaveOccurred(), out)
})

Describe("namespace labels and annotations in target customizations", func() {
When("namespaceLabels and namespaceAnnotations override existing root configuration", func() {
BeforeEach(func() {
asset = "single-cluster/ns-labels-target-customization.yaml"
})

It("deploys the bundledeployment with the merged namespaceLabels and namespaceAnnotations", func() {
By("setting the namespaces and annotations on the bundle deployment")
out, err := k.Get("cluster", "local", "-o", "jsonpath={.status.namespace}")
Expect(err).ToNot(HaveOccurred(), out)

clusterNS := strings.TrimSpace(out)
clusterK := k.Namespace(clusterNS)

var bundleDeploymentName string

Eventually(func(g Gomega) {
bundleDeploymentNames, _ := clusterK.Get(
"bundledeployments",
"-o",
"jsonpath={.items[*].metadata.name}",
)

for _, bdName := range strings.Split(bundleDeploymentNames, " ") {
if strings.HasPrefix(bdName, "test-target-customization-namespace-labels") {
bundleDeploymentName = bdName
break
}
}

g.Expect(bundleDeploymentName).ToNot(BeEmpty())
}).Should(Succeed())

Eventually(func(g Gomega) {
labels, err := clusterK.Get(
"bundledeployments",
bundleDeploymentName,
"-o",
"jsonpath={.spec.options.namespaceLabels}",
)

g.Expect(err).ToNot(HaveOccurred())
g.Expect(labels).To(Equal(`{"foo":"bar","this.is/a":"test"}`))

annot, err := clusterK.Get(
"bundledeployments",
bundleDeploymentName,
"-o",
"jsonpath={.spec.options.namespaceAnnotations}",
)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(annot).To(Equal(`{"foo":"bar","this.is/another":"test"}`))
}).Should(Succeed())

By("setting the namespaces and annotations on the created namespace")
Eventually(func(g Gomega) {
labels, err := k.Get("ns", "ns-1", "-o", "jsonpath={.metadata.labels}")
g.Expect(err).ToNot(HaveOccurred())
g.Expect(labels).To(Equal(`{"foo":"bar","kubernetes.io/metadata.name":"ns-1","this.is/a":"test"}`))

ann, err := k.Get("ns", "ns-1", "-o", "jsonpath={.metadata.annotations}")
g.Expect(err).ToNot(HaveOccurred())
g.Expect(ann).To(Equal(`{"foo":"bar","this.is/another":"test"}`))
}).Should(Succeed())
})
})

When("namespaceLabels and namespaceAnnotations override empty root configuration", func() {
BeforeEach(func() {
asset = "single-cluster/ns-labels-target-customization-no-defaults.yaml"
})

It("deploys the bundledeployment with the merged namespaceLabels and namespaceAnnotations", func() {
By("setting the namespaces and annotations on the bundle deployment")
out, err := k.Get("cluster", "local", "-o", "jsonpath={.status.namespace}")
Expect(err).ToNot(HaveOccurred(), out)

clusterNS := strings.TrimSpace(out)
clusterK := k.Namespace(clusterNS)

var bundleDeploymentName string

Eventually(func(g Gomega) {
bundleDeploymentNames, _ := clusterK.Get(
"bundledeployments",
"-o",
"jsonpath={.items[*].metadata.name}",
)

for _, bdName := range strings.Split(bundleDeploymentNames, " ") {
if strings.HasPrefix(bdName, "test-target-customization-namespace-labels") {
bundleDeploymentName = bdName
break
}
}

g.Expect(bundleDeploymentName).ToNot(BeEmpty())
}).Should(Succeed())

Eventually(func(g Gomega) {
labels, err := clusterK.Get(
"bundledeployments",
bundleDeploymentName,
"-o",
"jsonpath={.spec.options.namespaceLabels}",
)

g.Expect(err).ToNot(HaveOccurred())
g.Expect(labels).To(Equal(`{"this.is/a":"test"}`))

annot, err := clusterK.Get(
"bundledeployments",
bundleDeploymentName,
"-o",
"jsonpath={.spec.options.namespaceAnnotations}",
)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(annot).To(Equal(`{"this.is/another":"test"}`))
}).Should(Succeed())

By("setting the namespaces and annotations on the created namespace")
Eventually(func(g Gomega) {
labels, err := k.Get("ns", "no-defaults-ns-1", "-o", "jsonpath={.metadata.labels}")
g.Expect(err).ToNot(HaveOccurred())
g.Expect(labels).To(Equal(`{"kubernetes.io/metadata.name":"no-defaults-ns-1","this.is/a":"test"}`))

ann, err := k.Get("ns", "no-defaults-ns-1", "-o", "jsonpath={.metadata.annotations}")
g.Expect(err).ToNot(HaveOccurred())
g.Expect(ann).To(Equal(`{"this.is/another":"test"}`))
}).Should(Succeed())
})
})
})

})
23 changes: 23 additions & 0 deletions internal/cmd/controller/target/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ func (t *Target) BundleDeployment() *fleet.BundleDeployment {
}
bd.Spec.Paused = t.IsPaused()

initialiseOptionsMaps(bd)

for _, bundleTarget := range t.Bundle.Spec.Targets {
if bundleTarget.NamespaceLabels != nil {
for key, value := range *bundleTarget.NamespaceLabels {
Expand Down Expand Up @@ -140,3 +142,24 @@ func (t *Target) state() fleet.BundleState {
func (t *Target) message() string {
return summary.MessageFromDeployment(t.Deployment)
}

// initialiseOptionsMaps initialises options and staged options maps of namespace labels and annotations on bd, if
// needed.
// Assumes that bd is not nil.
func initialiseOptionsMaps(bd *fleet.BundleDeployment) {
if bd.Spec.Options.NamespaceLabels == nil {
bd.Spec.Options.NamespaceLabels = map[string]string{}
}

if bd.Spec.Options.NamespaceAnnotations == nil {
bd.Spec.Options.NamespaceAnnotations = map[string]string{}
}

if bd.Spec.StagedOptions.NamespaceLabels == nil {
bd.Spec.StagedOptions.NamespaceLabels = map[string]string{}
}

if bd.Spec.StagedOptions.NamespaceAnnotations == nil {
bd.Spec.StagedOptions.NamespaceAnnotations = map[string]string{}
}
}

0 comments on commit bfa033e

Please sign in to comment.