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 all commits
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
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ repos:
entry: golangci-lint run --enable-only goimports --fix
types: [go]
language: golang
pass_filenames: true
pass_filenames: false
AustinAbro321 marked this conversation as resolved.
Show resolved Hide resolved
- id: lint
name: golangci-lint go lint
entry: golangci-lint run
Expand Down
1 change: 0 additions & 1 deletion src/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,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
47 changes: 27 additions & 20 deletions src/internal/packager/helm/post-render.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@ import (

"github.com/defenseunicorns/pkg/helpers"
"github.com/defenseunicorns/zarf/src/config"
"github.com/defenseunicorns/zarf/src/pkg/cluster"
"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"

kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
)
Expand All @@ -34,16 +37,27 @@ type renderer struct {
func (h *Helm) newRenderer() (*renderer, error) {
message.Debugf("helm.NewRenderer()")

namespaces := make(map[string]*corev1.Namespace)
if h.cluster != nil {
namespaces[h.chart.Namespace] = h.cluster.NewZarfManagedNamespace(h.chart.Namespace)
rend := &renderer{
Helm: h,
connectStrings: types.ConnectStrings{},
namespaces: map[string]*corev1.Namespace{},
}
if h.cluster == nil {
return rend, nil
}

AustinAbro321 marked this conversation as resolved.
Show resolved Hide resolved
return &renderer{
Helm: h,
connectStrings: make(types.ConnectStrings),
namespaces: namespaces,
}, nil
namespace, err := h.cluster.Clientset.CoreV1().Namespaces().Get(context.TODO(), h.chart.Namespace, metav1.GetOptions{})
lucasrod16 marked this conversation as resolved.
Show resolved Hide resolved
if err != nil && !kerrors.IsNotFound(err) {
return nil, fmt.Errorf("unable to check for existing namespace %q in cluster: %w", h.chart.Namespace, err)
}
if kerrors.IsNotFound(err) {
rend.namespaces[h.chart.Namespace] = cluster.NewZarfManagedNamespace(h.chart.Namespace)
} else if h.cfg.DeployOpts.AdoptExistingResources {
namespace.Labels = cluster.AdoptZarfManagedLabels(namespace.Labels)
rend.namespaces[h.chart.Namespace] = namespace
}

return rend, nil
}

func (r *renderer) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) {
Expand Down Expand Up @@ -169,22 +183,15 @@ func (r *renderer) editHelmResources(ctx context.Context, resources []releaseuti

switch rawData.GetKind() {
case "Namespace":
var namespace corev1.Namespace
namespace := &corev1.Namespace{}
// 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.Labels = cluster.AdoptZarfManagedLabels(namespace.Labels)
// Add it to the stack
r.namespaces[namespace.Name] = &namespace
r.namespaces[namespace.Name] = namespace
}
// skip so we can strip namespaces from helm's brain
continue
Expand All @@ -209,7 +216,7 @@ func (r *renderer) editHelmResources(ctx context.Context, resources []releaseuti
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] = cluster.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 @@ -8,7 +8,6 @@ import (
"context"
"time"

"github.com/defenseunicorns/zarf/src/config"
"github.com/defenseunicorns/zarf/src/pkg/k8s"
"github.com/defenseunicorns/zarf/src/pkg/message"
)
Expand All @@ -21,13 +20,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",
}

// NewClusterWithWait creates a new Cluster instance and waits for the given timeout for the cluster to be ready.
func NewClusterWithWait(ctx context.Context) (*Cluster, error) {
spinner := message.NewProgressSpinner("Waiting for cluster connection")
Expand All @@ -36,7 +30,7 @@ func NewClusterWithWait(ctx context.Context) (*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 @@ -56,7 +50,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 @@ -330,7 +330,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.AgentLabel] = "ignore"

// Bind the pod to the node the image was found on
pod.Spec.NodeName = node
Expand Down
28 changes: 28 additions & 0 deletions src/pkg/cluster/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ package cluster
import (
"context"

"github.com/defenseunicorns/zarf/src/pkg/k8s"
"github.com/defenseunicorns/zarf/src/pkg/message"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// DeleteZarfNamespace deletes the Zarf namespace from the connected cluster.
Expand All @@ -17,3 +20,28 @@ func (c *Cluster) DeleteZarfNamespace(ctx context.Context) error {

return c.DeleteNamespace(ctx, ZarfNamespaceName)
}

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

// AdoptZarfManagedLabels adds & deletes the necessary labels that signal to Zarf it should manage a namespace
func AdoptZarfManagedLabels(labels map[string]string) map[string]string {
if labels == nil {
labels = make(map[string]string)
}
labels[k8s.ZarfManagedByLabel] = "zarf"
delete(labels, k8s.AgentLabel)
return labels
}
9 changes: 5 additions & 4 deletions src/pkg/cluster/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,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 @@ -89,8 +90,8 @@ func (c *Cluster) UpdateZarfManagedImageSecrets(ctx context.Context, state *type
}

// 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 @@ -123,8 +124,8 @@ func (c *Cluster) UpdateZarfManagedGitSecrets(ctx context.Context, state *types.
}

// 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 @@ -92,7 +92,7 @@ func (c *Cluster) InitZarfState(ctx context.Context, initOptions types.ZarfInitO
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(ctx, &namespaceCopy); err != nil {
// This is not a hard failure, but we should log it.
Expand All @@ -102,7 +102,7 @@ func (c *Cluster) InitZarfState(ctx context.Context, initOptions types.ZarfInitO

// Try to create the zarf namespace.
spinner.Updatef("Creating the Zarf namespace")
zarfNamespace := c.NewZarfManagedNamespace(ZarfNamespaceName)
zarfNamespace := NewZarfManagedNamespace(ZarfNamespaceName)
if _, err := c.CreateNamespace(ctx, zarfNamespace); err != nil {
return fmt.Errorf("unable to create the zarf namespace: %w", err)
}
Expand Down Expand Up @@ -244,7 +244,7 @@ func (c *Cluster) SaveZarfState(ctx context.Context, state *types.ZarfState) err
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 @@ -12,6 +12,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 @@ -68,16 +69,16 @@ func (c *Cluster) StripZarfLabelsAndSecretsFromNamespaces(ctx context.Context) {

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

if namespaces, err := c.GetNamespaces(ctx); 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(ctx, &namespaceCopy); err != nil {
// This is not a hard failure, but we should log it
Expand Down
11 changes: 7 additions & 4 deletions src/pkg/k8s/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,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"
)

// 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 @@ -39,7 +43,6 @@ func New(logger Log, defaultLabels Labels) (*K8s, error) {
RestConfig: config,
Clientset: clientset,
Log: logger,
Labels: defaultLabels,
}, nil
}

Expand Down
16 changes: 0 additions & 16 deletions src/pkg/k8s/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,22 +67,6 @@ 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{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1.SchemeGroupVersion.String(),
Kind: "Namespace",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: map[string]string{
zarfManagedByLabel: "zarf",
},
},
}
}

// 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
func (k *K8s) IsInitialNamespace(name string) bool {
if name == "default" {
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 @@ -448,7 +448,7 @@ func (p *Packager) setupState(ctx context.Context) (err error) {

// Try to create the zarf namespace
spinner.Updatef("Creating the Zarf namespace")
zarfNamespace := p.cluster.NewZarfManagedNamespace(cluster.ZarfNamespaceName)
zarfNamespace := cluster.NewZarfManagedNamespace(cluster.ZarfNamespaceName)
if _, err := p.cluster.CreateNamespace(ctx, 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