From 7988e5d51b43497f19a5052879f9b2878098bc5a Mon Sep 17 00:00:00 2001 From: Lucas Rodriguez Date: Thu, 13 Jun 2024 08:57:37 -0500 Subject: [PATCH] fix: avoid injector pod name collisions --- src/pkg/cluster/injector.go | 52 +++++++++++++++---- .../testdata/expected-injection-pod.json | 2 +- src/pkg/packager/deploy.go | 13 +++-- 3 files changed, 51 insertions(+), 16 deletions(-) diff --git a/src/pkg/cluster/injector.go b/src/pkg/cluster/injector.go index 177c8a25ac..774fb73922 100644 --- a/src/pkg/cluster/injector.go +++ b/src/pkg/cluster/injector.go @@ -6,6 +6,8 @@ package cluster import ( "context" + "crypto/sha256" + "encoding/hex" "fmt" "net/http" "os" @@ -43,7 +45,7 @@ var ( type imageNodeMap map[string][]string // StartInjectionMadness initializes a Zarf injection into the cluster. -func (c *Cluster) StartInjectionMadness(ctx context.Context, tmpDir string, imagesDir string, injectorSeedSrcs []string) { +func (c *Cluster) StartInjectionMadness(ctx context.Context, tmpDir string, imagesDir string, injectorSeedSrcs []string) error { spinner := message.NewProgressSpinner("Attempting to bootstrap the seed image into the cluster") defer spinner.Stop() @@ -115,12 +117,21 @@ func (c *Cluster) StartInjectionMadness(ctx context.Context, tmpDir string, imag GracePeriodSeconds: &deleteGracePeriod, PropagationPolicy: &deletePolicy, } - err := c.Clientset.CoreV1().Pods(ZarfNamespaceName).Delete(ctx, "injector", deleteOpts) + selector, err := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "zarf-injector", + }, + }) if err != nil { - message.Debug("could not delete pod injector:", err) + return err + } + listOpts := metav1.ListOptions{ + LabelSelector: selector.String(), + } + err = c.Clientset.CoreV1().Pods(ZarfNamespaceName).DeleteCollection(ctx, deleteOpts, listOpts) + if err != nil { + return err } - - // Update the podspec image path and use the first node found pod, err := c.buildInjectionPod(node[0], image, payloadConfigmaps, sha256sum) if err != nil { @@ -140,7 +151,7 @@ func (c *Cluster) StartInjectionMadness(ctx context.Context, tmpDir string, imag // if no error, try and wait for a seed image to be present, return if successful if c.injectorIsReady(ctx, seedImages, spinner) { spinner.Success() - return + return nil } // Otherwise just continue to try next image @@ -148,18 +159,30 @@ func (c *Cluster) StartInjectionMadness(ctx context.Context, tmpDir string, imag // All images were exhausted and still no happiness spinner.Fatalf(nil, "Unable to perform the injection") + return nil } // StopInjectionMadness handles cleanup once the seed registry is up. func (c *Cluster) StopInjectionMadness(ctx context.Context) error { // Try to kill the injector pod now - err := c.Clientset.CoreV1().Pods(ZarfNamespaceName).Delete(ctx, "injector", metav1.DeleteOptions{}) + selector, err := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "zarf-injector", + }, + }) + if err != nil { + return err + } + listOpts := metav1.ListOptions{ + LabelSelector: selector.String(), + } + err = c.Clientset.CoreV1().Pods(ZarfNamespaceName).DeleteCollection(ctx, metav1.DeleteOptions{}, listOpts) if err != nil { return err } // Remove the configmaps - selector, err := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{ + selector, err = metav1.LabelSelectorAsSelector(&metav1.LabelSelector{ MatchLabels: map[string]string{ "zarf-injector": "payload", }, @@ -167,7 +190,7 @@ func (c *Cluster) StopInjectionMadness(ctx context.Context) error { if err != nil { return err } - listOpts := metav1.ListOptions{ + listOpts = metav1.ListOptions{ LabelSelector: selector.String(), } err = c.Clientset.CoreV1().ConfigMaps(ZarfNamespaceName).DeleteCollection(ctx, metav1.DeleteOptions{}, listOpts) @@ -384,13 +407,22 @@ func (c *Cluster) createService(ctx context.Context) (*corev1.Service, error) { // buildInjectionPod return a pod for injection with the appropriate containers to perform the injection. func (c *Cluster) buildInjectionPod(node, image string, payloadConfigmaps []string, payloadShasum string) (*corev1.Pod, error) { executeMode := int32(0777) + + // Create a SHA-256 hash of the image name to allow unique injector pod names. + // This prevents collisions where `zarf init` is ran back to back and a previous injector pod still exists. + hasher := sha256.New() + if _, err := hasher.Write([]byte(image)); err != nil { + return nil, err + } + hash := hex.EncodeToString(hasher.Sum(nil))[:8] + pod := &corev1.Pod{ TypeMeta: metav1.TypeMeta{ APIVersion: corev1.SchemeGroupVersion.String(), Kind: "Pod", }, ObjectMeta: metav1.ObjectMeta{ - Name: "injector", + Name: fmt.Sprintf("injector-%s", hash), Namespace: ZarfNamespaceName, Labels: map[string]string{ "app": "zarf-injector", diff --git a/src/pkg/cluster/testdata/expected-injection-pod.json b/src/pkg/cluster/testdata/expected-injection-pod.json index 30f2e5b1f1..3cc8e9af58 100644 --- a/src/pkg/cluster/testdata/expected-injection-pod.json +++ b/src/pkg/cluster/testdata/expected-injection-pod.json @@ -1 +1 @@ -{"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"}],"restartPolicy":"Never","nodeName":"injection-node"},"status":{}} +{"kind":"Pod","apiVersion":"v1","metadata":{"name":"injector-b68304d6","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"}],"restartPolicy":"Never","nodeName":"injection-node"},"status":{}} diff --git a/src/pkg/packager/deploy.go b/src/pkg/packager/deploy.go index 4fe2b770e6..7c63173b42 100644 --- a/src/pkg/packager/deploy.go +++ b/src/pkg/packager/deploy.go @@ -237,13 +237,13 @@ func (p *Packager) deployInitComponent(ctx context.Context, component types.Zarf if component.RequiresCluster() && p.state == nil { err = p.cluster.InitZarfState(ctx, p.cfg.InitOpts) if err != nil { - return charts, fmt.Errorf("unable to initialize Zarf state: %w", err) + return nil, fmt.Errorf("unable to initialize Zarf state: %w", err) } } if hasExternalRegistry && (isSeedRegistry || isInjector || isRegistry) { message.Notef("Not deploying the component (%s) since external registry information was provided during `zarf init`", component.Name) - return charts, nil + return nil, nil } if isRegistry { @@ -253,18 +253,21 @@ func (p *Packager) deployInitComponent(ctx context.Context, component types.Zarf // Before deploying the seed registry, start the injector if isSeedRegistry { - p.cluster.StartInjectionMadness(ctx, p.layout.Base, p.layout.Images.Base, component.Images) + err := p.cluster.StartInjectionMadness(ctx, p.layout.Base, p.layout.Images.Base, component.Images) + if err != nil { + return nil, err + } } charts, err = p.deployComponent(ctx, component, isAgent /* skip img checksum if isAgent */, isSeedRegistry /* skip image push if isSeedRegistry */) if err != nil { - return charts, err + return nil, err } // Do cleanup for when we inject the seed registry during initialization if isSeedRegistry { if err := p.cluster.StopInjectionMadness(ctx); err != nil { - return charts, fmt.Errorf("unable to seed the Zarf Registry: %w", err) + return nil, fmt.Errorf("unable to seed the Zarf Registry: %w", err) } }