From e0040ab3acc8249713f2c08a729501c77dbce9dd Mon Sep 17 00:00:00 2001 From: Austin Abro <37223396+AustinAbro321@users.noreply.github.com> Date: Wed, 6 Nov 2024 14:36:49 -0500 Subject: [PATCH] refactor: server side apply over create / update (#3075) Signed-off-by: Austin Abro Signed-off-by: Austin Abro --- src/internal/agent/hooks/utils_test.go | 2 +- src/internal/packager/helm/post-render.go | 54 +--- src/internal/packager2/load_test.go | 2 +- src/pkg/cluster/cluster.go | 2 + src/pkg/cluster/injector.go | 274 ++++++++---------- src/pkg/cluster/injector_test.go | 46 +-- src/pkg/cluster/namespace.go | 8 +- src/pkg/cluster/pvc_test.go | 2 +- src/pkg/cluster/secrets.go | 65 ++--- src/pkg/cluster/secrets_test.go | 1 - src/pkg/cluster/state.go | 58 +--- src/pkg/cluster/state_test.go | 2 +- .../testdata/expected-injection-pod.json | 112 ++++++- .../testdata/expected-injection-service.json | 25 +- src/pkg/cluster/tunnel_test.go | 2 +- src/pkg/cluster/zarf.go | 90 ++---- src/pkg/cluster/zarf_test.go | 10 +- src/pkg/packager/common_test.go | 2 +- src/pkg/packager/deploy.go | 21 +- src/pkg/packager/remove.go | 47 +-- 20 files changed, 376 insertions(+), 449 deletions(-) diff --git a/src/internal/agent/hooks/utils_test.go b/src/internal/agent/hooks/utils_test.go index 84ee8a4c47..05eed3f78e 100644 --- a/src/internal/agent/hooks/utils_test.go +++ b/src/internal/agent/hooks/utils_test.go @@ -32,7 +32,7 @@ type admissionTest struct { func createTestClientWithZarfState(ctx context.Context, t *testing.T, state *types.ZarfState) *cluster.Cluster { t.Helper() - c := &cluster.Cluster{Clientset: fake.NewSimpleClientset()} + c := &cluster.Cluster{Clientset: fake.NewClientset()} stateData, err := json.Marshal(state) require.NoError(t, err) diff --git a/src/internal/packager/helm/post-render.go b/src/internal/packager/helm/post-render.go index b80109ffd0..fc5c93cb75 100644 --- a/src/internal/packager/helm/post-render.go +++ b/src/internal/packager/helm/post-render.go @@ -8,7 +8,6 @@ import ( "bytes" "context" "fmt" - "maps" "os" "path/filepath" "slices" @@ -21,11 +20,11 @@ import ( "github.com/zarf-dev/zarf/src/pkg/utils" "github.com/zarf-dev/zarf/src/types" "helm.sh/helm/v3/pkg/releaseutil" - corev1 "k8s.io/api/core/v1" "k8s.io/client-go/dynamic" "k8s.io/client-go/restmapper" "sigs.k8s.io/yaml" + corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -164,49 +163,14 @@ func (r *renderer) adoptAndUpdateNamespaces(ctx context.Context) error { if err != nil { return err } - // TODO: Refactor as error is not checked instead of checking for not found error. - currentRegistrySecret, _ := c.Clientset.CoreV1().Secrets(name).Get(ctx, config.ZarfImagePullSecretName, metav1.GetOptions{}) - sameSecretData := maps.EqualFunc(currentRegistrySecret.Data, validRegistrySecret.Data, func(v1, v2 []byte) bool { return bytes.Equal(v1, v2) }) - if currentRegistrySecret.Name != config.ZarfImagePullSecretName || !sameSecretData { - err := func() error { - _, err := c.Clientset.CoreV1().Secrets(validRegistrySecret.Namespace).Create(ctx, validRegistrySecret, metav1.CreateOptions{}) - if err != nil && !kerrors.IsAlreadyExists(err) { - return err - } - if err == nil { - return nil - } - _, err = c.Clientset.CoreV1().Secrets(validRegistrySecret.Namespace).Update(ctx, validRegistrySecret, metav1.UpdateOptions{}) - if err != nil { - return err - } - return nil - }() - if err != nil { - message.WarnErrf(err, "Problem creating registry secret for the %s namespace", name) - l.Warn("problem creating registry secret", "namespace", name, "error", err.Error()) - } - - // Create or update the zarf git server secret - gitServerSecret := c.GenerateGitPullCreds(name, config.ZarfGitServerSecretName, r.state.GitServer) - err = func() error { - _, err := c.Clientset.CoreV1().Secrets(gitServerSecret.Namespace).Create(ctx, gitServerSecret, metav1.CreateOptions{}) - if err != nil && !kerrors.IsAlreadyExists(err) { - return err - } - if err == nil { - return nil - } - _, err = c.Clientset.CoreV1().Secrets(gitServerSecret.Namespace).Update(ctx, gitServerSecret, metav1.UpdateOptions{}) - if err != nil { - return err - } - return nil - }() - if err != nil { - message.WarnErrf(err, "Problem creating git server secret for the %s namespace", name) - l.Warn("problem creating git server secret", "namespace", name, "error", err.Error()) - } + _, err = c.Clientset.CoreV1().Secrets(*validRegistrySecret.Namespace).Apply(ctx, validRegistrySecret, metav1.ApplyOptions{Force: true, FieldManager: cluster.FieldManagerName}) + if err != nil { + return fmt.Errorf("problem applying registry secret for the %s namespace: %w", name, err) + } + gitServerSecret := c.GenerateGitPullCreds(name, config.ZarfGitServerSecretName, r.state.GitServer) + _, err = c.Clientset.CoreV1().Secrets(*gitServerSecret.Namespace).Apply(ctx, gitServerSecret, metav1.ApplyOptions{Force: true, FieldManager: cluster.FieldManagerName}) + if err != nil { + return fmt.Errorf("problem applying git server secret for the %s namespace: %w", name, err) } } return nil diff --git a/src/internal/packager2/load_test.go b/src/internal/packager2/load_test.go index e5f815244d..98069a0032 100644 --- a/src/internal/packager2/load_test.go +++ b/src/internal/packager2/load_test.go @@ -148,7 +148,7 @@ func TestPackageFromSourceOrCluster(t *testing.T) { require.Equal(t, "test", pkg.Metadata.Name) c := &cluster.Cluster{ - Clientset: fake.NewSimpleClientset(), + Clientset: fake.NewClientset(), } _, err = c.RecordPackageDeployment(ctx, pkg, nil) require.NoError(t, err) diff --git a/src/pkg/cluster/cluster.go b/src/pkg/cluster/cluster.go index 4686deed76..463a97d7e2 100644 --- a/src/pkg/cluster/cluster.go +++ b/src/pkg/cluster/cluster.go @@ -30,6 +30,8 @@ const ( DefaultTimeout = 30 * time.Second // AgentLabel is used to give instructions to the Zarf agent AgentLabel = "zarf.dev/agent" + // FieldManagerName is the field manager used during server side apply + FieldManagerName = "zarf" ) // Cluster Zarf specific cluster management functions. diff --git a/src/pkg/cluster/injector.go b/src/pkg/cluster/injector.go index ff9aac1fb9..b459299460 100644 --- a/src/pkg/cluster/injector.go +++ b/src/pkg/cluster/injector.go @@ -19,18 +19,19 @@ import ( kerrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/wait" "github.com/defenseunicorns/pkg/helpers/v2" + "github.com/zarf-dev/zarf/src/api/v1alpha1" "github.com/zarf-dev/zarf/src/config" "github.com/zarf-dev/zarf/src/internal/healthchecks" "github.com/zarf-dev/zarf/src/pkg/logger" "github.com/zarf-dev/zarf/src/pkg/message" "github.com/zarf-dev/zarf/src/pkg/transform" "github.com/zarf-dev/zarf/src/pkg/utils" + v1ac "k8s.io/client-go/applyconfigurations/core/v1" ) // StartInjection initializes a Zarf injection into the cluster. @@ -47,16 +48,15 @@ func (c *Cluster) StartInjection(ctx context.Context, tmpDir, imagesDir string, defer spinner.Stop() l.Info("creating Zarf injector resources") - resReq := corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ + resReq := v1ac.ResourceRequirements(). + WithRequests(corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse(".5"), corev1.ResourceMemory: resource.MustParse("64Mi"), - }, - Limits: corev1.ResourceList{ + }). + WithLimits(corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("1"), corev1.ResourceMemory: resource.MustParse("256Mi"), - }, - } + }) injectorImage, injectorNodeName, err := c.getInjectorImageAndNode(ctx, resReq) if err != nil { return err @@ -71,42 +71,24 @@ func (c *Cluster) StartInjection(ctx context.Context, tmpDir, imagesDir string, if err != nil { return err } - cm := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: ZarfNamespaceName, - Name: "rust-binary", - }, - BinaryData: map[string][]byte{ + cm := v1ac.ConfigMap("rust-binary", ZarfNamespaceName). + WithBinaryData(map[string][]byte{ "zarf-injector": b, - }, - } - _, err = c.Clientset.CoreV1().ConfigMaps(cm.Namespace).Create(ctx, cm, metav1.CreateOptions{}) + }) + _, err = c.Clientset.CoreV1().ConfigMaps(*cm.Namespace).Apply(ctx, cm, metav1.ApplyOptions{Force: true, FieldManager: FieldManagerName}) if err != nil { return err } - svc := &corev1.Service{ - TypeMeta: metav1.TypeMeta{ - APIVersion: corev1.SchemeGroupVersion.String(), - Kind: "Service", - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: ZarfNamespaceName, - Name: "zarf-injector", - }, - Spec: corev1.ServiceSpec{ - Type: corev1.ServiceTypeNodePort, - Ports: []corev1.ServicePort{ - { - Port: int32(5000), - }, - }, - Selector: map[string]string{ - "app": "zarf-injector", - }, - }, - } - svc, err = c.Clientset.CoreV1().Services(svc.Namespace).Create(ctx, svc, metav1.CreateOptions{}) + svcAc := v1ac.Service("zarf-injector", ZarfNamespaceName). + WithSpec(v1ac.ServiceSpec(). + WithType(corev1.ServiceTypeNodePort). + WithPorts( + v1ac.ServicePort().WithPort(int32(5000)), + ).WithSelector(map[string]string{ + "app": "zarf-injector", + })) + svc, err := c.Clientset.CoreV1().Services(*svcAc.Namespace).Apply(ctx, svcAc, metav1.ApplyOptions{Force: true, FieldManager: FieldManagerName}) if err != nil { return err } @@ -114,14 +96,20 @@ func (c *Cluster) StartInjection(ctx context.Context, tmpDir, imagesDir string, config.ZarfSeedPort = fmt.Sprintf("%d", svc.Spec.Ports[0].NodePort) pod := buildInjectionPod(injectorNodeName, injectorImage, payloadCmNames, shasum, resReq) - _, err = c.Clientset.CoreV1().Pods(pod.Namespace).Create(ctx, pod, metav1.CreateOptions{}) + _, err = c.Clientset.CoreV1().Pods(*pod.Namespace).Apply(ctx, pod, metav1.ApplyOptions{Force: true, FieldManager: FieldManagerName}) if err != nil { return fmt.Errorf("error creating pod in cluster: %w", err) } waitCtx, waitCancel := context.WithTimeout(ctx, 60*time.Second) defer waitCancel() - err = healthchecks.WaitForReadyRuntime(waitCtx, c.Watcher, []runtime.Object{pod}) + podRef := v1alpha1.NamespacedObjectKindReference{ + APIVersion: *pod.APIVersion, + Kind: *pod.Kind, + Namespace: *pod.Namespace, + Name: *pod.Name, + } + err = healthchecks.Run(waitCtx, c.Watcher, []v1alpha1.NamespacedObjectKindReference{podRef}) if err != nil { return err } @@ -245,19 +233,14 @@ func (c *Cluster) createPayloadConfigMaps(ctx context.Context, spinner *message. spinner.Updatef("Adding archive binary configmap %d of %d to the cluster", i+1, len(chunks)) - cm := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: ZarfNamespaceName, - Name: fileName, - Labels: map[string]string{ - "zarf-injector": "payload", - }, - }, - BinaryData: map[string][]byte{ + cm := v1ac.ConfigMap(fileName, ZarfNamespaceName). + WithLabels(map[string]string{ + "zarf-injector": "payload", + }). + WithBinaryData(map[string][]byte{ fileName: data, - }, - } - _, err = c.Clientset.CoreV1().ConfigMaps(ZarfNamespaceName).Create(ctx, cm, metav1.CreateOptions{}) + }) + _, err = c.Clientset.CoreV1().ConfigMaps(ZarfNamespaceName).Apply(ctx, cm, metav1.ApplyOptions{Force: true, FieldManager: FieldManagerName}) if err != nil { return nil, "", err } @@ -270,7 +253,7 @@ func (c *Cluster) createPayloadConfigMaps(ctx context.Context, spinner *message. } // getImagesAndNodesForInjection checks for images on schedulable nodes within a cluster. -func (c *Cluster) getInjectorImageAndNode(ctx context.Context, resReq corev1.ResourceRequirements) (string, string, error) { +func (c *Cluster) getInjectorImageAndNode(ctx context.Context, resReq *v1ac.ResourceRequirementsApplyConfiguration) (string, string, error) { // Regex for Zarf seed image zarfImageRegex, err := regexp.Compile(`(?m)^127\.0\.0\.1:`) if err != nil { @@ -288,8 +271,8 @@ func (c *Cluster) getInjectorImageAndNode(ctx context.Context, resReq corev1.Res if err != nil { return "", "", err } - if nodeDetails.Status.Allocatable.Cpu().Cmp(resReq.Requests[corev1.ResourceCPU]) < 0 || - nodeDetails.Status.Allocatable.Memory().Cmp(resReq.Requests[corev1.ResourceMemory]) < 0 { + if nodeDetails.Status.Allocatable.Cpu().Cmp(*resReq.Requests.Cpu()) < 0 || + nodeDetails.Status.Allocatable.Memory().Cmp(*resReq.Requests.Memory()) < 0 { continue } if hasBlockingTaints(nodeDetails.Spec.Taints) { @@ -326,116 +309,95 @@ func hasBlockingTaints(taints []corev1.Taint) bool { return false } -func buildInjectionPod(nodeName, image string, payloadCmNames []string, shasum string, resReq corev1.ResourceRequirements) *corev1.Pod { +func buildInjectionPod(nodeName, image string, payloadCmNames []string, shasum string, resReq *v1ac.ResourceRequirementsApplyConfiguration) *v1ac.PodApplyConfiguration { executeMode := int32(0777) userID := int64(1000) groupID := int64(2000) fsGroupID := int64(2000) + volumes := []*v1ac.VolumeApplyConfiguration{ + v1ac.Volume(). + WithName("init"). + WithConfigMap( + v1ac.ConfigMapVolumeSource(). + WithName("rust-binary"). + WithDefaultMode(executeMode), + ), + v1ac.Volume(). + WithName("seed"). + WithEmptyDir(&v1ac.EmptyDirVolumeSourceApplyConfiguration{})} - pod := &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - APIVersion: corev1.SchemeGroupVersion.String(), - Kind: "Pod", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "injector", - Namespace: ZarfNamespaceName, - Labels: map[string]string{ - "app": "zarf-injector", - AgentLabel: "ignore", - }, - }, - Spec: corev1.PodSpec{ - NodeName: nodeName, - // Do not try to restart the pod as it will be deleted/re-created instead. - RestartPolicy: corev1.RestartPolicyNever, - SecurityContext: &corev1.PodSecurityContext{ - RunAsUser: &userID, - RunAsGroup: &groupID, - FSGroup: &fsGroupID, - SeccompProfile: &corev1.SeccompProfile{Type: corev1.SeccompProfileTypeRuntimeDefault}, - }, - Containers: []corev1.Container{ - { - Name: "injector", - Image: image, - ImagePullPolicy: corev1.PullIfNotPresent, - WorkingDir: "/zarf-init", - Command: []string{"/zarf-init/zarf-injector", shasum}, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "init", - MountPath: "/zarf-init/zarf-injector", - SubPath: "zarf-injector", - }, - { - Name: "seed", - MountPath: "/zarf-seed", - }, - }, - ReadinessProbe: &corev1.Probe{ - PeriodSeconds: 2, - SuccessThreshold: 1, - FailureThreshold: 10, - ProbeHandler: corev1.ProbeHandler{ - HTTPGet: &corev1.HTTPGetAction{ - Path: "/v2/", - Port: intstr.FromInt(5000), - }, - }, - }, - SecurityContext: &corev1.SecurityContext{ - ReadOnlyRootFilesystem: helpers.BoolPtr(true), - AllowPrivilegeEscalation: helpers.BoolPtr(false), - RunAsNonRoot: helpers.BoolPtr(true), - Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{"ALL"}, - }, - }, - Resources: resReq, - }, - }, - Volumes: []corev1.Volume{ - // Contains the rust binary and collection of configmaps from the tarball (seed image). - { - Name: "init", - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "rust-binary", - }, - DefaultMode: &executeMode, - }, - }, - }, - // Empty directory to hold the seed image (new dir to avoid permission issues) - { - Name: "seed", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }, - }, - }, + volumeMounts := []*v1ac.VolumeMountApplyConfiguration{ + v1ac.VolumeMount(). + WithName("init"). + WithMountPath("/zarf-init/zarf-injector"). + WithSubPath("zarf-injector"), + v1ac.VolumeMount(). + WithName("seed"). + WithMountPath("/zarf-seed"), } for _, filename := range payloadCmNames { - pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{ - Name: filename, - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: filename, - }, - }, - }, - }) - pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{ - Name: filename, - MountPath: fmt.Sprintf("/zarf-init/%s", filename), - SubPath: filename, - }) + volumes = append(volumes, v1ac.Volume(). + WithName(filename). + WithConfigMap( + v1ac.ConfigMapVolumeSource(). + WithName(filename), + )) + volumeMounts = append(volumeMounts, v1ac.VolumeMount(). + WithName(filename). + WithMountPath(fmt.Sprintf("/zarf-init/%s", filename)). + WithSubPath(filename)) } + pod := v1ac.Pod("injector", ZarfNamespaceName). + WithLabels(map[string]string{ + "app": "zarf-injector", + AgentLabel: "ignore", + }). + WithSpec( + v1ac.PodSpec(). + WithNodeName(nodeName). + WithRestartPolicy(corev1.RestartPolicyNever). + WithSecurityContext( + v1ac.PodSecurityContext(). + WithRunAsUser(userID). + WithRunAsGroup(groupID). + WithFSGroup(fsGroupID). + WithSeccompProfile( + v1ac.SeccompProfile(). + WithType(corev1.SeccompProfileTypeRuntimeDefault), + ), + ). + WithContainers( + v1ac.Container(). + WithName("injector"). + WithImage(image). + WithImagePullPolicy(corev1.PullIfNotPresent). + WithWorkingDir("/zarf-init"). + WithCommand("/zarf-init/zarf-injector", shasum). + WithVolumeMounts(volumeMounts...). + WithSecurityContext( + v1ac.SecurityContext(). + WithReadOnlyRootFilesystem(true). + WithAllowPrivilegeEscalation(false). + WithRunAsNonRoot(true). + WithCapabilities(v1ac.Capabilities().WithDrop(corev1.Capability("ALL"))), + ). + WithReadinessProbe( + v1ac.Probe(). + WithPeriodSeconds(2). + WithSuccessThreshold(1). + WithFailureThreshold(10). + WithHTTPGet( + v1ac.HTTPGetAction(). + WithPath("/v2/"). + WithPort(intstr.FromInt(5000)), + ), + ). + WithResources(resReq), + ). + WithVolumes(volumes...), + ) + return pod } diff --git a/src/pkg/cluster/injector_test.go b/src/pkg/cluster/injector_test.go index e5acdf3227..dde50f5bab 100644 --- a/src/pkg/cluster/injector_test.go +++ b/src/pkg/cluster/injector_test.go @@ -20,6 +20,7 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + v1ac "k8s.io/client-go/applyconfigurations/core/v1" "k8s.io/client-go/kubernetes/fake" k8stesting "k8s.io/client-go/testing" "sigs.k8s.io/cli-utils/pkg/kstatus/status" @@ -27,7 +28,7 @@ import ( func TestInjector(t *testing.T) { ctx := context.Background() - cs := fake.NewSimpleClientset() + cs := fake.NewClientset() c := &Cluster{ Clientset: cs, Watcher: healthchecks.NewImmediateWatcher(status.CurrentStatus), @@ -117,8 +118,10 @@ func TestInjector(t *testing.T) { expected, err := os.ReadFile("./testdata/expected-injection-service.json") require.NoError(t, err) svc, err := cs.CoreV1().Services(ZarfNamespaceName).Get(ctx, "zarf-injector", metav1.GetOptions{}) + // Managed fields are auto-set and contain timestamps + svc.ManagedFields = nil require.NoError(t, err) - b, err := json.Marshal(svc) + b, err := json.MarshalIndent(svc, "", " ") require.NoError(t, err) require.Equal(t, strings.TrimSpace(string(expected)), string(b)) @@ -147,20 +150,21 @@ func TestInjector(t *testing.T) { func TestBuildInjectionPod(t *testing.T) { t.Parallel() - resReq := corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ + resReq := v1ac.ResourceRequirements(). + WithRequests(corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse(".5"), corev1.ResourceMemory: resource.MustParse("64Mi"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - corev1.ResourceMemory: resource.MustParse("256Mi"), - }, - } + }). + WithLimits( + corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("256Mi"), + }) pod := buildInjectionPod("injection-node", "docker.io/library/ubuntu:latest", []string{"foo", "bar"}, "shasum", resReq) - require.Equal(t, "injector", pod.Name) - b, err := json.Marshal(pod) + require.Equal(t, "injector", *pod.Name) + b, err := json.MarshalIndent(pod, "", " ") require.NoError(t, err) + expected, err := os.ReadFile("./testdata/expected-injection-pod.json") require.NoError(t, err) require.Equal(t, strings.TrimSpace(string(expected)), string(b)) @@ -170,7 +174,7 @@ func TestGetInjectorImageAndNode(t *testing.T) { t.Parallel() ctx := context.Background() - cs := fake.NewSimpleClientset() + cs := fake.NewClientset() c := &Cluster{ Clientset: cs, @@ -270,16 +274,16 @@ func TestGetInjectorImageAndNode(t *testing.T) { require.NoError(t, err) } - resReq := corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ + resReq := v1ac.ResourceRequirements(). + WithRequests(corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse(".5"), corev1.ResourceMemory: resource.MustParse("64Mi"), - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - corev1.ResourceMemory: resource.MustParse("256Mi"), - }, - } + }). + WithLimits( + corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("256Mi"), + }) image, node, err := c.getInjectorImageAndNode(ctx, resReq) require.NoError(t, err) require.Equal(t, "pod-2-container", image) diff --git a/src/pkg/cluster/namespace.go b/src/pkg/cluster/namespace.go index 24525dc6b1..b94c1a3a61 100644 --- a/src/pkg/cluster/namespace.go +++ b/src/pkg/cluster/namespace.go @@ -10,12 +10,13 @@ import ( "time" "github.com/avast/retry-go/v4" - corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/zarf-dev/zarf/src/pkg/logger" "github.com/zarf-dev/zarf/src/pkg/message" + corev1 "k8s.io/api/core/v1" + v1ac "k8s.io/client-go/applyconfigurations/core/v1" ) // DeleteZarfNamespace deletes the Zarf namespace from the connected cluster. @@ -51,6 +52,11 @@ func (c *Cluster) DeleteZarfNamespace(ctx context.Context) error { return nil } +// NewZarfManagedApplyNamespace returns a v1ac.NamespaceApplyConfiguration with Zarf-managed labels +func NewZarfManagedApplyNamespace(name string) *v1ac.NamespaceApplyConfiguration { + return v1ac.Namespace(name).WithLabels(AdoptZarfManagedLabels(nil)) +} + // NewZarfManagedNamespace returns a corev1.Namespace with Zarf-managed labels func NewZarfManagedNamespace(name string) *corev1.Namespace { namespace := &corev1.Namespace{ diff --git a/src/pkg/cluster/pvc_test.go b/src/pkg/cluster/pvc_test.go index 6c267d1500..52347466a0 100644 --- a/src/pkg/cluster/pvc_test.go +++ b/src/pkg/cluster/pvc_test.go @@ -19,7 +19,7 @@ func TestUpdateGiteaPVC(t *testing.T) { ctx := testutil.TestContext(t) c := &Cluster{ - Clientset: fake.NewSimpleClientset(), + Clientset: fake.NewClientset(), } pvc := &corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ diff --git a/src/pkg/cluster/secrets.go b/src/pkg/cluster/secrets.go index aa693c6a44..aa49d00c9e 100644 --- a/src/pkg/cluster/secrets.go +++ b/src/pkg/cluster/secrets.go @@ -5,16 +5,15 @@ package cluster import ( - "bytes" "context" "encoding/base64" "encoding/json" "fmt" - "maps" corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1ac "k8s.io/client-go/applyconfigurations/core/v1" "github.com/zarf-dev/zarf/src/config" "github.com/zarf-dev/zarf/src/pkg/message" @@ -35,7 +34,7 @@ type DockerConfigEntryWithAuth struct { } // GenerateRegistryPullCreds generates a secret containing the registry credentials. -func (c *Cluster) GenerateRegistryPullCreds(ctx context.Context, namespace, name string, registryInfo types.RegistryInfo) (*corev1.Secret, error) { +func (c *Cluster) GenerateRegistryPullCreds(ctx context.Context, namespace, name string, registryInfo types.RegistryInfo) (*v1ac.SecretApplyConfiguration, error) { // Auth field must be username:password and base64 encoded fieldValue := registryInfo.PullUsername + ":" + registryInfo.PullPassword authEncodedValue := base64.StdEncoding.EncodeToString([]byte(fieldValue)) @@ -68,48 +67,28 @@ func (c *Cluster) GenerateRegistryPullCreds(ctx context.Context, namespace, name return nil, fmt.Errorf("unable to marshal the .dockerconfigjson secret data for the image pull secret: %w", err) } - secretDockerConfig := &corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - APIVersion: corev1.SchemeGroupVersion.String(), - Kind: "Secret", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: map[string]string{ - ZarfManagedByLabel: "zarf", - }, - }, - Type: corev1.SecretTypeDockerConfigJson, - Data: map[string][]byte{ + secretDockerConfig := v1ac.Secret(name, namespace). + WithLabels(map[string]string{ + ZarfManagedByLabel: "zarf", + }). + WithType(corev1.SecretTypeDockerConfigJson). + WithData(map[string][]byte{ ".dockerconfigjson": dockerConfigData, - }, - } + }) + return secretDockerConfig, nil } // GenerateGitPullCreds generates a secret containing the git credentials. -func (c *Cluster) GenerateGitPullCreds(namespace, name string, gitServerInfo types.GitServerInfo) *corev1.Secret { - gitServerSecret := &corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - APIVersion: corev1.SchemeGroupVersion.String(), - Kind: "Secret", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: map[string]string{ - ZarfManagedByLabel: "zarf", - }, - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{}, - StringData: map[string]string{ +func (c *Cluster) GenerateGitPullCreds(namespace, name string, gitServerInfo types.GitServerInfo) *v1ac.SecretApplyConfiguration { + return v1ac.Secret(name, namespace). + WithLabels(map[string]string{ + ZarfManagedByLabel: "zarf", + }).WithType(corev1.SecretTypeOpaque). + WithStringData(map[string]string{ "username": gitServerInfo.PullUsername, "password": gitServerInfo.PullPassword, - }, - } - return gitServerSecret + }) } // UpdateZarfManagedImageSecrets updates all Zarf-managed image secrets in all namespaces based on state @@ -138,11 +117,8 @@ func (c *Cluster) UpdateZarfManagedImageSecrets(ctx context.Context, state *type if err != nil { return err } - if maps.EqualFunc(currentRegistrySecret.Data, newRegistrySecret.Data, func(v1, v2 []byte) bool { return bytes.Equal(v1, v2) }) { - continue - } spinner.Updatef("Updating existing Zarf-managed image secret for namespace: '%s'", namespace.Name) - _, err = c.Clientset.CoreV1().Secrets(newRegistrySecret.Namespace).Update(ctx, newRegistrySecret, metav1.UpdateOptions{}) + _, err = c.Clientset.CoreV1().Secrets(*newRegistrySecret.Namespace).Apply(ctx, newRegistrySecret, metav1.ApplyOptions{Force: true, FieldManager: FieldManagerName}) if err != nil { return err } @@ -174,11 +150,8 @@ func (c *Cluster) UpdateZarfManagedGitSecrets(ctx context.Context, state *types. continue } newGitSecret := c.GenerateGitPullCreds(namespace.Name, config.ZarfGitServerSecretName, state.GitServer) - if maps.Equal(currentGitSecret.StringData, newGitSecret.StringData) { - continue - } spinner.Updatef("Updating existing Zarf-managed git secret for namespace: %s", namespace.Name) - _, err = c.Clientset.CoreV1().Secrets(newGitSecret.Namespace).Update(ctx, newGitSecret, metav1.UpdateOptions{}) + _, err = c.Clientset.CoreV1().Secrets(*newGitSecret.Namespace).Apply(ctx, newGitSecret, metav1.ApplyOptions{Force: true, FieldManager: FieldManagerName}) if err != nil { return err } diff --git a/src/pkg/cluster/secrets_test.go b/src/pkg/cluster/secrets_test.go index 0ee731dfe9..40ecce94fa 100644 --- a/src/pkg/cluster/secrets_test.go +++ b/src/pkg/cluster/secrets_test.go @@ -192,7 +192,6 @@ func TestUpdateZarfManagedSecrets(t *testing.T) { }, }, Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{}, StringData: map[string]string{ "username": state.GitServer.PullUsername, "password": state.GitServer.PullPassword, diff --git a/src/pkg/cluster/state.go b/src/pkg/cluster/state.go index 82ba76b28e..551856db25 100644 --- a/src/pkg/cluster/state.go +++ b/src/pkg/cluster/state.go @@ -15,6 +15,7 @@ import ( corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1ac "k8s.io/client-go/applyconfigurations/core/v1" "github.com/avast/retry-go/v4" "github.com/defenseunicorns/pkg/helpers/v2" @@ -93,6 +94,9 @@ func (c *Cluster) InitZarfState(ctx context.Context, initOptions types.ZarfInitO } // Mark existing namespaces as ignored for the zarf agent to prevent mutating resources we don't own. for _, namespace := range namespaceList.Items { + if namespace.Name == "zarf" { + continue + } l.Debug("marking namespace as ignored by Zarf Agent", "name", namespace.Name) if namespace.Labels == nil { @@ -111,23 +115,10 @@ func (c *Cluster) InitZarfState(ctx context.Context, initOptions types.ZarfInitO // Try to create the zarf namespace. spinner.Updatef("Creating the Zarf namespace") l.Debug("creating the Zarf namespace") - zarfNamespace := NewZarfManagedNamespace(ZarfNamespaceName) - err = func() error { - _, err := c.Clientset.CoreV1().Namespaces().Create(ctx, zarfNamespace, metav1.CreateOptions{}) - if err != nil && !kerrors.IsAlreadyExists(err) { - return fmt.Errorf("unable to create the Zarf namespace: %w", err) - } - if err == nil { - return nil - } - _, err = c.Clientset.CoreV1().Namespaces().Update(ctx, zarfNamespace, metav1.UpdateOptions{}) - if err != nil { - return fmt.Errorf("unable to update the Zarf namespace: %w", err) - } - return nil - }() + zarfNamespace := NewZarfManagedApplyNamespace(ZarfNamespaceName) + _, err = c.Clientset.CoreV1().Namespaces().Apply(ctx, zarfNamespace, metav1.ApplyOptions{FieldManager: FieldManagerName, Force: true}) if err != nil { - return err + return fmt.Errorf("unable to apply the Zarf namespace: %w", err) } // Wait up to 2 minutes for the default service account to be created. @@ -263,35 +254,18 @@ func (c *Cluster) SaveZarfState(ctx context.Context, state *types.ZarfState) err if err != nil { return err } - secret := &corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - APIVersion: corev1.SchemeGroupVersion.String(), - Kind: "Secret", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: ZarfStateSecretName, - Namespace: ZarfNamespaceName, - Labels: map[string]string{ - ZarfManagedByLabel: "zarf", - }, - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ + secret := v1ac.Secret(ZarfStateSecretName, ZarfNamespaceName). + WithLabels(map[string]string{ + ZarfManagedByLabel: "zarf", + }). + WithType(corev1.SecretTypeOpaque). + WithData(map[string][]byte{ ZarfStateDataKey: data, - }, - } + }) - // Attempt to create or update the secret and return. - _, err = c.Clientset.CoreV1().Secrets(secret.Namespace).Create(ctx, secret, metav1.CreateOptions{}) - if err != nil && !kerrors.IsAlreadyExists(err) { - return fmt.Errorf("unable to create the zarf state secret: %w", err) - } - if err == nil { - return nil - } - _, err = c.Clientset.CoreV1().Secrets(secret.Namespace).Update(ctx, secret, metav1.UpdateOptions{}) + _, err = c.Clientset.CoreV1().Secrets(*secret.Namespace).Apply(ctx, secret, metav1.ApplyOptions{Force: true, FieldManager: FieldManagerName}) if err != nil { - return fmt.Errorf("unable to update the zarf state secret: %w", err) + return fmt.Errorf("unable to apply the zarf state secret: %w", err) } return nil } diff --git a/src/pkg/cluster/state_test.go b/src/pkg/cluster/state_test.go index 1575528f4a..6d00cf7d28 100644 --- a/src/pkg/cluster/state_test.go +++ b/src/pkg/cluster/state_test.go @@ -165,7 +165,7 @@ func TestInitZarfState(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { ctx := context.Background() - cs := fake.NewSimpleClientset() + cs := fake.NewClientset() for _, node := range tt.nodes { _, err := cs.CoreV1().Nodes().Create(ctx, &node, metav1.CreateOptions{}) require.NoError(t, err) diff --git a/src/pkg/cluster/testdata/expected-injection-pod.json b/src/pkg/cluster/testdata/expected-injection-pod.json index 297a5e28bc..05577add4e 100644 --- a/src/pkg/cluster/testdata/expected-injection-pod.json +++ b/src/pkg/cluster/testdata/expected-injection-pod.json @@ -1 +1,111 @@ -{"kind":"Pod","apiVersion":"v1","metadata":{"name":"injector","namespace":"zarf","creationTimestamp":null,"labels":{"app":"zarf-injector","zarf.dev/agent":"ignore"}},"spec":{"volumes":[{"name":"init","configMap":{"name":"rust-binary","defaultMode":511}},{"name":"seed","emptyDir":{}},{"name":"foo","configMap":{"name":"foo"}},{"name":"bar","configMap":{"name":"bar"}}],"containers":[{"name":"injector","image":"docker.io/library/ubuntu:latest","command":["/zarf-init/zarf-injector","shasum"],"workingDir":"/zarf-init","resources":{"limits":{"cpu":"1","memory":"256Mi"},"requests":{"cpu":"500m","memory":"64Mi"}},"volumeMounts":[{"name":"init","mountPath":"/zarf-init/zarf-injector","subPath":"zarf-injector"},{"name":"seed","mountPath":"/zarf-seed"},{"name":"foo","mountPath":"/zarf-init/foo","subPath":"foo"},{"name":"bar","mountPath":"/zarf-init/bar","subPath":"bar"}],"readinessProbe":{"httpGet":{"path":"/v2/","port":5000},"periodSeconds":2,"successThreshold":1,"failureThreshold":10},"imagePullPolicy":"IfNotPresent","securityContext":{"capabilities":{"drop":["ALL"]},"runAsNonRoot":true,"readOnlyRootFilesystem":true,"allowPrivilegeEscalation":false}}],"restartPolicy":"Never","nodeName":"injection-node","securityContext":{"runAsUser":1000,"runAsGroup":2000,"fsGroup":2000,"seccompProfile":{"type":"RuntimeDefault"}}},"status":{}} +{ + "kind": "Pod", + "apiVersion": "v1", + "metadata": { + "name": "injector", + "namespace": "zarf", + "labels": { + "app": "zarf-injector", + "zarf.dev/agent": "ignore" + } + }, + "spec": { + "volumes": [ + { + "name": "init", + "configMap": { + "name": "rust-binary", + "defaultMode": 511 + } + }, + { + "name": "seed", + "emptyDir": {} + }, + { + "name": "foo", + "configMap": { + "name": "foo" + } + }, + { + "name": "bar", + "configMap": { + "name": "bar" + } + } + ], + "containers": [ + { + "name": "injector", + "image": "docker.io/library/ubuntu:latest", + "command": [ + "/zarf-init/zarf-injector", + "shasum" + ], + "workingDir": "/zarf-init", + "resources": { + "limits": { + "cpu": "1", + "memory": "256Mi" + }, + "requests": { + "cpu": "500m", + "memory": "64Mi" + } + }, + "volumeMounts": [ + { + "name": "init", + "mountPath": "/zarf-init/zarf-injector", + "subPath": "zarf-injector" + }, + { + "name": "seed", + "mountPath": "/zarf-seed" + }, + { + "name": "foo", + "mountPath": "/zarf-init/foo", + "subPath": "foo" + }, + { + "name": "bar", + "mountPath": "/zarf-init/bar", + "subPath": "bar" + } + ], + "readinessProbe": { + "httpGet": { + "path": "/v2/", + "port": 5000 + }, + "periodSeconds": 2, + "successThreshold": 1, + "failureThreshold": 10 + }, + "imagePullPolicy": "IfNotPresent", + "securityContext": { + "capabilities": { + "drop": [ + "ALL" + ] + }, + "runAsNonRoot": true, + "readOnlyRootFilesystem": true, + "allowPrivilegeEscalation": false + } + } + ], + "restartPolicy": "Never", + "nodeName": "injection-node", + "securityContext": { + "runAsUser": 1000, + "runAsGroup": 2000, + "fsGroup": 2000, + "seccompProfile": { + "type": "RuntimeDefault" + } + } + } +} diff --git a/src/pkg/cluster/testdata/expected-injection-service.json b/src/pkg/cluster/testdata/expected-injection-service.json index dd826cbb4c..8e0eeecf9f 100644 --- a/src/pkg/cluster/testdata/expected-injection-service.json +++ b/src/pkg/cluster/testdata/expected-injection-service.json @@ -1 +1,24 @@ -{"kind":"Service","apiVersion":"v1","metadata":{"name":"zarf-injector","namespace":"zarf","creationTimestamp":null},"spec":{"ports":[{"port":5000,"targetPort":0}],"selector":{"app":"zarf-injector"},"type":"NodePort"},"status":{"loadBalancer":{}}} +{ + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "zarf-injector", + "namespace": "zarf", + "creationTimestamp": null + }, + "spec": { + "ports": [ + { + "port": 5000, + "targetPort": 0 + } + ], + "selector": { + "app": "zarf-injector" + }, + "type": "NodePort" + }, + "status": { + "loadBalancer": {} + } +} diff --git a/src/pkg/cluster/tunnel_test.go b/src/pkg/cluster/tunnel_test.go index 9ce09770e9..c41b9312be 100644 --- a/src/pkg/cluster/tunnel_test.go +++ b/src/pkg/cluster/tunnel_test.go @@ -19,7 +19,7 @@ func TestListConnections(t *testing.T) { t.Parallel() c := &Cluster{ - Clientset: fake.NewSimpleClientset(), + Clientset: fake.NewClientset(), } svc := corev1.Service{ ObjectMeta: metav1.ObjectMeta{ diff --git a/src/pkg/cluster/zarf.go b/src/pkg/cluster/zarf.go index 906a7bc166..29e818b140 100644 --- a/src/pkg/cluster/zarf.go +++ b/src/pkg/cluster/zarf.go @@ -16,6 +16,7 @@ import ( corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1ac "k8s.io/client-go/applyconfigurations/core/v1" "github.com/zarf-dev/zarf/src/api/v1alpha1" "github.com/zarf-dev/zarf/src/config" @@ -81,44 +82,16 @@ func (c *Cluster) UpdateDeployedPackage(ctx context.Context, depPkg types.Deploy if err != nil { return err } - packageSecret := &corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - APIVersion: corev1.SchemeGroupVersion.String(), - Kind: "Secret", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: secretName, - Namespace: ZarfNamespaceName, - Labels: map[string]string{ - ZarfManagedByLabel: "zarf", - ZarfPackageInfoLabel: depPkg.Name, - }, - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ - "data": packageSecretData, - }, - } - err = func() error { - _, err := c.Clientset.CoreV1().Secrets(packageSecret.Namespace).Get(ctx, packageSecret.Name, metav1.GetOptions{}) - if err != nil && !kerrors.IsNotFound(err) { - return err - } - if kerrors.IsNotFound(err) { - _, err = c.Clientset.CoreV1().Secrets(packageSecret.Namespace).Create(ctx, packageSecret, metav1.CreateOptions{}) - if err != nil { - return fmt.Errorf("unable to create the deployed package secret: %w", err) - } - return nil - } - _, err = c.Clientset.CoreV1().Secrets(packageSecret.Namespace).Update(ctx, packageSecret, metav1.UpdateOptions{}) - if err != nil { - return fmt.Errorf("unable to update the deployed package secret: %w", err) - } - return nil - }() + packageSecret := v1ac.Secret(secretName, ZarfNamespaceName). + WithLabels(map[string]string{ + ZarfManagedByLabel: "zarf", + ZarfPackageInfoLabel: depPkg.Name, + }).WithData(map[string][]byte{ + "data": packageSecretData, + }).WithType(corev1.SecretTypeOpaque) + _, err = c.Clientset.CoreV1().Secrets(*packageSecret.Namespace).Apply(ctx, packageSecret, metav1.ApplyOptions{Force: true, FieldManager: FieldManagerName}) if err != nil { - return err + return fmt.Errorf("unable to apply the deployed package secret: %w", err) } return nil } @@ -209,41 +182,18 @@ func (c *Cluster) RecordPackageDeployment(ctx context.Context, pkg v1alpha1.Zarf return nil, err } - // Update the package secret - deployedPackageSecret := &corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - APIVersion: corev1.SchemeGroupVersion.String(), - Kind: "Secret", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: config.ZarfPackagePrefix + packageName, - Namespace: ZarfNamespaceName, - Labels: map[string]string{ - ZarfManagedByLabel: "zarf", - ZarfPackageInfoLabel: packageName, - }, - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ + packageSecretName := fmt.Sprintf("%s%s", config.ZarfPackagePrefix, packageName) + deployedPackageSecret := v1ac.Secret(packageSecretName, ZarfNamespaceName). + WithLabels(map[string]string{ + ZarfManagedByLabel: "zarf", + ZarfPackageInfoLabel: packageName, + }).WithType(corev1.SecretTypeOpaque). + WithData(map[string][]byte{ "data": packageData, - }, - } - updatedSecret, err := func() (*corev1.Secret, error) { - secret, err := c.Clientset.CoreV1().Secrets(deployedPackageSecret.Namespace).Create(ctx, deployedPackageSecret, metav1.CreateOptions{}) - if err != nil && !kerrors.IsAlreadyExists(err) { - return nil, err - } - if err == nil { - return secret, nil - } - secret, err = c.Clientset.CoreV1().Secrets(deployedPackageSecret.Namespace).Update(ctx, deployedPackageSecret, metav1.UpdateOptions{}) - if err != nil { - return nil, err - } - return secret, nil - }() + }) + updatedSecret, err := c.Clientset.CoreV1().Secrets(*deployedPackageSecret.Namespace).Apply(ctx, deployedPackageSecret, metav1.ApplyOptions{Force: true, FieldManager: FieldManagerName}) if err != nil { - return nil, fmt.Errorf("failed to record package deployment in secret '%s': %w", deployedPackageSecret.Name, err) + return nil, fmt.Errorf("failed to record package deployment in secret '%s': %w", *deployedPackageSecret.Name, err) } if err := json.Unmarshal(updatedSecret.Data["data"], &deployedPackage); err != nil { return nil, err diff --git a/src/pkg/cluster/zarf_test.go b/src/pkg/cluster/zarf_test.go index a9895bc953..bdfabacb28 100644 --- a/src/pkg/cluster/zarf_test.go +++ b/src/pkg/cluster/zarf_test.go @@ -24,7 +24,7 @@ func TestGetDeployedPackage(t *testing.T) { t.Parallel() ctx := context.Background() c := &Cluster{ - Clientset: fake.NewSimpleClientset(), + Clientset: fake.NewClientset(), } packages := []types.DeployedPackage{ @@ -73,7 +73,7 @@ func TestGetDeployedPackage(t *testing.T) { func TestRegistryHPA(t *testing.T) { ctx := context.Background() - cs := fake.NewSimpleClientset() + cs := fake.NewClientset() hpa := autoscalingv2.HorizontalPodAutoscaler{ ObjectMeta: metav1.ObjectMeta{ Name: "zarf-docker-registry", @@ -127,15 +127,15 @@ func TestInternalGitServerExists(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - cs := fake.NewSimpleClientset() - cluster := &Cluster{Clientset: cs} + cs := fake.NewClientset() + c := &Cluster{Clientset: cs} ctx := context.Background() if tt.svc != nil { _, err := cs.CoreV1().Services(tt.svc.Namespace).Create(ctx, tt.svc, metav1.CreateOptions{}) require.NoError(t, err) } - exists, err := cluster.InternalGitServerExists(ctx) + exists, err := c.InternalGitServerExists(ctx) require.Equal(t, tt.expectedExist, exists) require.Equal(t, tt.expectedErr, err) }) diff --git a/src/pkg/packager/common_test.go b/src/pkg/packager/common_test.go index d2f4a7aa7a..c26b51dc80 100644 --- a/src/pkg/packager/common_test.go +++ b/src/pkg/packager/common_test.go @@ -77,7 +77,7 @@ func TestValidatePackageArchitecture(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() - cs := fake.NewSimpleClientset() + cs := fake.NewClientset() p := &Packager{ cluster: &cluster.Cluster{ diff --git a/src/pkg/packager/deploy.go b/src/pkg/packager/deploy.go index f21ddba5c2..703bdc97ae 100644 --- a/src/pkg/packager/deploy.go +++ b/src/pkg/packager/deploy.go @@ -510,23 +510,10 @@ func (p *Packager) setupState(ctx context.Context) error { spinner.Updatef("Creating the Zarf namespace") l.Info("creating the Zarf namespace") - zarfNamespace := cluster.NewZarfManagedNamespace(cluster.ZarfNamespaceName) - err := func() error { - _, err := p.cluster.Clientset.CoreV1().Namespaces().Create(ctx, zarfNamespace, metav1.CreateOptions{}) - if err != nil && !kerrors.IsAlreadyExists(err) { - return err - } - if err == nil { - return nil - } - _, err = p.cluster.Clientset.CoreV1().Namespaces().Update(ctx, zarfNamespace, metav1.UpdateOptions{}) - if err != nil { - return err - } - return nil - }() + zarfNamespace := cluster.NewZarfManagedApplyNamespace(cluster.ZarfNamespaceName) + _, err = p.cluster.Clientset.CoreV1().Namespaces().Apply(ctx, zarfNamespace, metav1.ApplyOptions{Force: true, FieldManager: cluster.FieldManagerName}) if err != nil { - return fmt.Errorf("unable to create the Zarf namespace: %w", err) + return fmt.Errorf("unable to apply the Zarf namespace: %w", err) } } @@ -768,7 +755,7 @@ func (p *Packager) installChartAndManifests(ctx context.Context, componentPaths connectStrings, installedChartName, err := helmCfg.InstallOrUpgradeChart(ctx) if err != nil { return nil, err - } + } installedCharts = append(installedCharts, types.InstalledChart{Namespace: manifest.Namespace, ChartName: installedChartName, ConnectStrings: connectStrings}) } diff --git a/src/pkg/packager/remove.go b/src/pkg/packager/remove.go index b1149e343d..68e72df76d 100644 --- a/src/pkg/packager/remove.go +++ b/src/pkg/packager/remove.go @@ -15,8 +15,8 @@ import ( "github.com/defenseunicorns/pkg/helpers/v2" "helm.sh/helm/v3/pkg/storage/driver" corev1 "k8s.io/api/core/v1" - kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1ac "k8s.io/client-go/applyconfigurations/core/v1" "github.com/zarf-dev/zarf/src/api/v1alpha1" "github.com/zarf-dev/zarf/src/config" @@ -117,46 +117,19 @@ func (p *Packager) updatePackageSecret(ctx context.Context, deployedPackage type secretName := config.ZarfPackagePrefix + deployedPackage.Name // Save the new secret with the removed components removed from the secret - newPackageSecret := &corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - APIVersion: corev1.SchemeGroupVersion.String(), - Kind: "Secret", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: secretName, - Namespace: cluster.ZarfNamespaceName, - Labels: map[string]string{ - cluster.ZarfManagedByLabel: "zarf", - cluster.ZarfPackageInfoLabel: deployedPackage.Name, - }, - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ + newPackageSecret := v1ac.Secret(secretName, cluster.ZarfNamespaceName). + WithLabels(map[string]string{ + cluster.ZarfManagedByLabel: "zarf", + cluster.ZarfPackageInfoLabel: deployedPackage.Name, + }).WithType(corev1.SecretTypeOpaque). + WithData(map[string][]byte{ "data": newPackageSecretData, - }, - } + }) - err = func() error { - _, err := p.cluster.Clientset.CoreV1().Secrets(newPackageSecret.Namespace).Get(ctx, newPackageSecret.Name, metav1.GetOptions{}) - if err != nil && !kerrors.IsNotFound(err) { - return err - } - if kerrors.IsNotFound(err) { - _, err = p.cluster.Clientset.CoreV1().Secrets(newPackageSecret.Namespace).Create(ctx, newPackageSecret, metav1.CreateOptions{}) - if err != nil { - return fmt.Errorf("unable to create the zarf state secret: %w", err) - } - return nil - } - _, err = p.cluster.Clientset.CoreV1().Secrets(newPackageSecret.Namespace).Update(ctx, newPackageSecret, metav1.UpdateOptions{}) - if err != nil { - return fmt.Errorf("unable to update the zarf state secret: %w", err) - } - return nil - }() + _, err = p.cluster.Clientset.CoreV1().Secrets(*newPackageSecret.Namespace).Apply(ctx, newPackageSecret, metav1.ApplyOptions{Force: true, FieldManager: cluster.FieldManagerName}) // We warn and ignore errors because we may have removed the cluster that this package was inside of if err != nil { - message.Warnf("Unable to update the '%s' package secret: '%s' (this may be normal if the cluster was removed)", secretName, err.Error()) + message.Warnf("Unable to apply the '%s' package secret: '%s' (this may be normal if the cluster was removed)", secretName, err.Error()) } } return nil