Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: adopt namespace metadata #2494

Merged
merged 19 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ const (
ZarfConnectAnnotationDescription = "zarf.dev/connect-description"
ZarfConnectAnnotationURL = "zarf.dev/connect-url"

ZarfManagedByLabel = "app.kubernetes.io/managed-by"
ZarfCleanupScriptsPath = "/opt/zarf"

ZarfPackagePrefix = "zarf-package-"
Expand Down
29 changes: 17 additions & 12 deletions src/internal/packager/helm/post-render.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ import (

"github.com/defenseunicorns/pkg/helpers"
"github.com/defenseunicorns/zarf/src/config"
"github.com/defenseunicorns/zarf/src/pkg/k8s"
"github.com/defenseunicorns/zarf/src/pkg/message"
"github.com/defenseunicorns/zarf/src/pkg/utils"
"github.com/defenseunicorns/zarf/src/types"
"helm.sh/helm/v3/pkg/releaseutil"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/yaml"

"k8s.io/apimachinery/pkg/api/errors"
AustinAbro321 marked this conversation as resolved.
Show resolved Hide resolved
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
)
Expand All @@ -35,7 +37,16 @@ func (h *Helm) newRenderer() (*renderer, error) {

namespaces := make(map[string]*corev1.Namespace)
if h.cluster != nil {
namespaces[h.chart.Namespace] = h.cluster.NewZarfManagedNamespace(h.chart.Namespace)
namespace, err := h.cluster.K8s.GetNamespace(h.chart.Namespace)
if err != nil && !errors.IsNotFound(err) {
return nil, fmt.Errorf("unable to check for existing namespace %q in cluster: %s", h.chart.Namespace, err)
AustinAbro321 marked this conversation as resolved.
Show resolved Hide resolved
}
if h.cfg.DeployOpts.AdoptExistingResources && !errors.IsNotFound(err) {
AustinAbro321 marked this conversation as resolved.
Show resolved Hide resolved
namespaces[h.chart.Namespace] = k8s.UpdateNamespaceToBeZarfManaged(namespace)
} else {
namespaces[h.chart.Namespace] = k8s.NewZarfManagedNamespace(h.chart.Namespace)
}

AustinAbro321 marked this conversation as resolved.
Show resolved Hide resolved
}

return &renderer{
Expand Down Expand Up @@ -167,22 +178,16 @@ func (r *renderer) editHelmResources(resources []releaseutil.Manifest, finalMani

switch rawData.GetKind() {
case "Namespace":
var namespace corev1.Namespace
namespace := new(corev1.Namespace)
AustinAbro321 marked this conversation as resolved.
Show resolved Hide resolved
// parse the namespace resource so it can be applied out-of-band by zarf instead of helm to avoid helm ns shenanigans
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(rawData.UnstructuredContent(), &namespace); err != nil {
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(rawData.UnstructuredContent(), namespace); err != nil {
message.WarnErrf(err, "could not parse namespace %s", rawData.GetName())
} else {
message.Debugf("Matched helm namespace %s for zarf annotation", namespace.Name)
if namespace.Labels == nil {
// Ensure label map exists to avoid nil panic
namespace.Labels = make(map[string]string)
}
// Now track this namespace by zarf
namespace.Labels[config.ZarfManagedByLabel] = "zarf"
namespace.Labels["zarf-helm-release"] = r.chart.ReleaseName
AustinAbro321 marked this conversation as resolved.
Show resolved Hide resolved
namespace = k8s.UpdateNamespaceToBeZarfManaged(namespace)

// Add it to the stack
r.namespaces[namespace.Name] = &namespace
r.namespaces[namespace.Name] = namespace
AustinAbro321 marked this conversation as resolved.
Show resolved Hide resolved
}
// skip so we can strip namespaces from helm's brain
continue
Expand All @@ -207,7 +212,7 @@ func (r *renderer) editHelmResources(resources []releaseutil.Manifest, finalMani
namespace := rawData.GetNamespace()
if _, exists := r.namespaces[namespace]; !exists && namespace != "" {
// if this is the first time seeing this ns, we need to track that to create it as well
r.namespaces[namespace] = r.cluster.NewZarfManagedNamespace(namespace)
r.namespaces[namespace] = k8s.NewZarfManagedNamespace(namespace)
}

// If we have been asked to adopt existing resources, process those now as well
Expand Down
10 changes: 2 additions & 8 deletions src/pkg/cluster/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ package cluster
import (
"time"

"github.com/defenseunicorns/zarf/src/config"
"github.com/defenseunicorns/zarf/src/pkg/k8s"
"github.com/defenseunicorns/zarf/src/pkg/message"
)
Expand All @@ -20,13 +19,8 @@ type Cluster struct {
const (
// DefaultTimeout is the default time to wait for a cluster to be ready.
DefaultTimeout = 30 * time.Second
agentLabel = "zarf.dev/agent"
)

var labels = k8s.Labels{
config.ZarfManagedByLabel: "zarf",
}

// NewClusterOrDie creates a new Cluster instance and waits up to 30 seconds for the cluster to be ready or throws a fatal error.
func NewClusterOrDie() *Cluster {
c, err := NewClusterWithWait(DefaultTimeout)
Expand All @@ -45,7 +39,7 @@ func NewClusterWithWait(timeout time.Duration) (*Cluster, error) {
c := &Cluster{}
var err error

c.K8s, err = k8s.New(message.Debugf, labels)
c.K8s, err = k8s.New(message.Debugf)
if err != nil {
return nil, err
}
Expand All @@ -65,7 +59,7 @@ func NewCluster() (*Cluster, error) {
c := &Cluster{}
var err error

c.K8s, err = k8s.New(message.Debugf, labels)
c.K8s, err = k8s.New(message.Debugf)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion src/pkg/cluster/injector.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ func (c *Cluster) buildInjectionPod(node, image string, payloadConfigmaps []stri
pod.Labels["app"] = "zarf-injector"

// Ensure zarf agent doesn't break the injector on future runs
pod.Labels[agentLabel] = "ignore"
pod.Labels[k8s.ZarfManagedByLabel] = "ignore"
AustinAbro321 marked this conversation as resolved.
Show resolved Hide resolved

// Bind the pod to the node the image was found on
pod.Spec.NodeName = node
Expand Down
9 changes: 5 additions & 4 deletions src/pkg/cluster/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
corev1 "k8s.io/api/core/v1"

"github.com/defenseunicorns/zarf/src/config"
"github.com/defenseunicorns/zarf/src/pkg/k8s"
"github.com/defenseunicorns/zarf/src/pkg/message"
"github.com/defenseunicorns/zarf/src/types"
)
Expand Down Expand Up @@ -88,8 +89,8 @@ func (c *Cluster) UpdateZarfManagedImageSecrets(state *types.ZarfState) {
}

// Check if this is a Zarf managed secret or is in a namespace the Zarf agent will take action in
if currentRegistrySecret.Labels[config.ZarfManagedByLabel] == "zarf" ||
(namespace.Labels[agentLabel] != "skip" && namespace.Labels[agentLabel] != "ignore") {
if currentRegistrySecret.Labels[k8s.ZarfManagedByLabel] == "zarf" ||
(namespace.Labels[k8s.AgentLabel] != "skip" && namespace.Labels[k8s.AgentLabel] != "ignore") {
spinner.Updatef("Updating existing Zarf-managed image secret for namespace: '%s'", namespace.Name)

// Create the secret
Expand Down Expand Up @@ -122,8 +123,8 @@ func (c *Cluster) UpdateZarfManagedGitSecrets(state *types.ZarfState) {
}

// Check if this is a Zarf managed secret or is in a namespace the Zarf agent will take action in
if currentGitSecret.Labels[config.ZarfManagedByLabel] == "zarf" ||
(namespace.Labels[agentLabel] != "skip" && namespace.Labels[agentLabel] != "ignore") {
if currentGitSecret.Labels[k8s.ZarfManagedByLabel] == "zarf" ||
(namespace.Labels[k8s.AgentLabel] != "skip" && namespace.Labels[k8s.AgentLabel] != "ignore") {
spinner.Updatef("Updating existing Zarf-managed git secret for namespace: '%s'", namespace.Name)

// Create the secret
Expand Down
6 changes: 3 additions & 3 deletions src/pkg/cluster/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func (c *Cluster) InitZarfState(initOptions types.ZarfInitOptions) error {
namespace.Labels = make(map[string]string)
}
// This label will tell the Zarf Agent to ignore this namespace.
namespace.Labels[agentLabel] = "ignore"
namespace.Labels[k8s.AgentLabel] = "ignore"
namespaceCopy := namespace
if _, err = c.UpdateNamespace(&namespaceCopy); err != nil {
// This is not a hard failure, but we should log it.
Expand All @@ -101,7 +101,7 @@ func (c *Cluster) InitZarfState(initOptions types.ZarfInitOptions) error {

// Try to create the zarf namespace.
spinner.Updatef("Creating the Zarf namespace")
zarfNamespace := c.NewZarfManagedNamespace(ZarfNamespaceName)
zarfNamespace := k8s.NewZarfManagedNamespace(ZarfNamespaceName)
if _, err := c.CreateNamespace(zarfNamespace); err != nil {
return fmt.Errorf("unable to create the zarf namespace: %w", err)
}
Expand Down Expand Up @@ -241,7 +241,7 @@ func (c *Cluster) SaveZarfState(state *types.ZarfState) error {
Name: ZarfStateSecretName,
Namespace: ZarfNamespaceName,
Labels: map[string]string{
config.ZarfManagedByLabel: "zarf",
k8s.ZarfManagedByLabel: "zarf",
},
},
Type: corev1.SecretTypeOpaque,
Expand Down
7 changes: 4 additions & 3 deletions src/pkg/cluster/zarf.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"time"

"github.com/defenseunicorns/zarf/src/config"
"github.com/defenseunicorns/zarf/src/pkg/k8s"
"github.com/defenseunicorns/zarf/src/pkg/message"
"github.com/defenseunicorns/zarf/src/types"
autoscalingV2 "k8s.io/api/autoscaling/v2"
Expand Down Expand Up @@ -69,16 +70,16 @@ func (c *Cluster) StripZarfLabelsAndSecretsFromNamespaces() {

deleteOptions := metav1.DeleteOptions{}
listOptions := metav1.ListOptions{
LabelSelector: config.ZarfManagedByLabel + "=zarf",
LabelSelector: k8s.ZarfManagedByLabel + "=zarf",
}

if namespaces, err := c.GetNamespaces(); err != nil {
spinner.Errorf(err, "Unable to get k8s namespaces")
} else {
for _, namespace := range namespaces.Items {
if _, ok := namespace.Labels[agentLabel]; ok {
if _, ok := namespace.Labels[k8s.AgentLabel]; ok {
spinner.Updatef("Removing Zarf Agent label for namespace %s", namespace.Name)
delete(namespace.Labels, agentLabel)
delete(namespace.Labels, k8s.AgentLabel)
namespaceCopy := namespace
if _, err = c.UpdateNamespace(&namespaceCopy); err != nil {
// This is not a hard failure, but we should log it
Expand Down
15 changes: 9 additions & 6 deletions src/pkg/k8s/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@ import (
"k8s.io/client-go/tools/clientcmd"
)

// cannot import config.ZarfManagedByLabel due to import cycle
const zarfManagedByLabel = "app.kubernetes.io/managed-by"
const (
//ZarfManagedByLabel is used to denote Zarf manages the lifecycle of a resource
ZarfManagedByLabel = "app.kubernetes.io/managed-by"
//AgentLabel is used to give instructions to the Zarf agent
AgentLabel = "zarf.dev/agent"
)
AustinAbro321 marked this conversation as resolved.
Show resolved Hide resolved

// New creates a new K8s client.
func New(logger Log, defaultLabels Labels) (*K8s, error) {
func New(logger Log) (*K8s, error) {
klog.SetLogger(funcr.New(func(_, args string) {
logger(args)
}, funcr.Options{}))
Expand All @@ -38,13 +42,12 @@ func New(logger Log, defaultLabels Labels) (*K8s, error) {
RestConfig: config,
Clientset: clientset,
Log: logger,
Labels: defaultLabels,
}, nil
}

// NewWithWait is a convenience function that creates a new K8s client and waits for the cluster to be healthy.
func NewWithWait(logger Log, defaultLabels Labels, timeout time.Duration) (*K8s, error) {
k, err := New(logger, defaultLabels)
func NewWithWait(logger Log, timeout time.Duration) (*K8s, error) {
k, err := New(logger)
if err != nil {
return nil, err
}
Expand Down
25 changes: 20 additions & 5 deletions src/pkg/k8s/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ func (k *K8s) GetNamespaces() (*corev1.NamespaceList, error) {
return k.Clientset.CoreV1().Namespaces().List(context.TODO(), metaOptions)
}

// GetNamespace gets a namespace by name
func (k *K8s) GetNamespace(name string) (*corev1.Namespace, error) {
AustinAbro321 marked this conversation as resolved.
Show resolved Hide resolved
getOptions := metav1.GetOptions{}
return k.Clientset.CoreV1().Namespaces().Get(context.TODO(), name, getOptions)
}

// UpdateNamespace updates the given namespace in the cluster.
func (k *K8s) UpdateNamespace(namespace *corev1.Namespace) (*corev1.Namespace, error) {
updateOptions := metav1.UpdateOptions{}
Expand Down Expand Up @@ -62,19 +68,28 @@ func (k *K8s) DeleteNamespace(ctx context.Context, name string) error {
}

// NewZarfManagedNamespace returns a corev1.Namespace with Zarf-managed labels
func (k *K8s) NewZarfManagedNamespace(name string) *corev1.Namespace {
return &corev1.Namespace{
func NewZarfManagedNamespace(name string) *corev1.Namespace {
namespace := &corev1.Namespace{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1.SchemeGroupVersion.String(),
Kind: "Namespace",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: map[string]string{
zarfManagedByLabel: "zarf",
},
},
}
namespace = UpdateNamespaceToBeZarfManaged(namespace)
return namespace
}

// UpdateNamespaceToBeZarfManaged adds & deletes labels to a namespace go object which will signal Zarf to manage it
func UpdateNamespaceToBeZarfManaged(namespace *corev1.Namespace) *corev1.Namespace {
if namespace.Labels == nil {
namespace.Labels = make(map[string]string)
}
namespace.Labels[ZarfManagedByLabel] = "zarf"
delete(namespace.Labels, AgentLabel)
return namespace
}

// IsInitialNamespace returns true if the given namespace name is an initial k8s namespace: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/#initial-namespaces
Expand Down
2 changes: 1 addition & 1 deletion src/pkg/k8s/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (k *K8s) GenerateSecret(namespace, name string, secretType corev1.SecretTyp
Name: name,
Namespace: namespace,
Labels: map[string]string{
zarfManagedByLabel: "zarf",
ZarfManagedByLabel: "zarf",
},
},
Type: secretType,
Expand Down
1 change: 0 additions & 1 deletion src/pkg/k8s/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ type K8s struct {
Clientset kubernetes.Interface
RestConfig *rest.Config
Log Log
Labels Labels
}

// PodLookup is a struct for specifying a pod to target for data injection or lookups.
Expand Down
2 changes: 1 addition & 1 deletion src/pkg/packager/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ func (p *Packager) setupState() (err error) {

// Try to create the zarf namespace
spinner.Updatef("Creating the Zarf namespace")
zarfNamespace := p.cluster.NewZarfManagedNamespace(cluster.ZarfNamespaceName)
zarfNamespace := k8s.NewZarfManagedNamespace(cluster.ZarfNamespaceName)
if _, err := p.cluster.CreateNamespace(zarfNamespace); err != nil {
spinner.Fatalf(err, "Unable to create the zarf namespace")
}
Expand Down
7 changes: 7 additions & 0 deletions src/test/e2e/25_helm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,13 @@ func testHelmAdoption(t *testing.T) {
require.NoError(t, err)
require.Contains(t, string(helmOut), "zarf-f53a99d4a4dd9a3575bedf59cd42d48d751ae866")

existingLabel, _, err := e2e.Kubectl("get", "ns", "dos-games", "-o=jsonpath={.metadata.labels.keep-this}")
require.Equal(t, "label", existingLabel)
require.NoError(t, err)
existingAnnotation, _, err := e2e.Kubectl("get", "ns", "dos-games", "-o=jsonpath={.metadata.annotations.keep-this}")
require.Equal(t, "annotation", existingAnnotation)
require.NoError(t, err)

// Remove the package.
stdOut, stdErr, err = e2e.Zarf("package", "remove", "dos-games", "--confirm")
require.NoError(t, err, stdOut, stdErr)
Expand Down
4 changes: 4 additions & 0 deletions src/test/packages/25-manifest-adoption/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ apiVersion: v1
kind: Namespace
metadata:
name: dos-games
labels:
keep-this: label
annotations:
keep-this: annotation
---
# This is a normal deployment manifest for dos-games that should be "adopted" by Helm/Zarf
apiVersion: apps/v1
Expand Down
Loading