diff --git a/test/e2e/packaging/deb_install_test.go b/test/e2e/packaging/deb_install_test.go index baa15d238..672923e29 100644 --- a/test/e2e/packaging/deb_install_test.go +++ b/test/e2e/packaging/deb_install_test.go @@ -8,9 +8,7 @@ import ( "path/filepath" "runtime" "strings" - "time" - "github.com/docker/docker/api/types/container" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) @@ -48,28 +46,8 @@ var _ = Describe("pf9-byohost deb", func() { Expect(strings.TrimSpace(string(archOutput))).To(Equal(runtime.GOARCH)) By("starting a byoh/node:e2e container") - created, err := dockerClient.ContainerCreate(ctx, - &container.Config{Image: debTestImage}, - &container.HostConfig{ - Privileged: true, - Tmpfs: map[string]string{"/run": "", "/run/lock": "", "/tmp": ""}, - }, - nil, nil, "") - Expect(err).NotTo(HaveOccurred()) - containerID := created.ID - defer func() { - _ = dockerClient.ContainerRemove(ctx, containerID, container.RemoveOptions{Force: true}) - }() - - Expect(dockerClient.ContainerStart(ctx, containerID, container.StartOptions{})).To(Succeed()) - - By("waiting for systemd to come up") - Eventually(func() (string, error) { - output, _, execErr := execInContainer(ctx, containerID, []string{"systemctl", "is-system-running"}, nil) - return output, execErr - }, 30*time.Second, time.Second).Should(SatisfyAny( - ContainSubstring("running"), ContainSubstring("degraded"), - )) + containerID, cleanup := startPackagingContainer(ctx, debTestImage) + defer cleanup() By("copying the built deb into the container") Expect(copyFileToContainer(ctx, containerID, debPath, debContainerPath)).To(Succeed()) @@ -95,26 +73,9 @@ var _ = Describe("pf9-byohost deb", func() { "/usr/bin/byohctl", )) - By("asserting byohctl was installed executable and runs") - byohctlOutput, exitCode, err := execInContainer(ctx, containerID, []string{"byohctl", "version"}, nil) - Expect(err).NotTo(HaveOccurred()) - Expect(exitCode).To(Equal(0), "byohctl version failed:\n%s", byohctlOutput) - - By("asserting after-install.sh generated the EnvironmentFile the systemd unit reads BOOTSTRAP_KUBECONFIG from") - confOutput, exitCode, err := execInContainer(ctx, containerID, - []string{"cat", "/etc/pf9-byohost-agent.service.d/pf9-byohost-agent.conf"}, nil) - Expect(err).NotTo(HaveOccurred()) - Expect(exitCode).To(Equal(0)) - Expect(confOutput).To(ContainSubstring("BOOTSTRAP_KUBECONFIG=")) - Expect(confOutput).To(ContainSubstring("NAMESPACE=")) - Expect(confOutput).To(ContainSubstring("REGION=")) - - // Not asserting is-active: same reasoning as the RPM test - no real - // cluster for the agent to reach in this environment. - By("asserting the service is enabled") - enabledOutput, _, err := execInContainer(ctx, containerID, []string{"systemctl", "is-enabled", "pf9-byohost-agent"}, nil) - Expect(err).NotTo(HaveOccurred()) - Expect(strings.TrimSpace(enabledOutput)).To(Equal("enabled")) + assertByohctlRuns(ctx, containerID) + assertEnvironmentFile(ctx, containerID, "after-install.sh") + assertServiceEnabled(ctx, containerID) By("uninstalling the deb") uninstallOutput, exitCode, err := execInContainer(ctx, containerID, []string{"dpkg", "-r", "pf9-byohost-agent"}, nil) @@ -122,16 +83,12 @@ var _ = Describe("pf9-byohost deb", func() { Expect(exitCode).To(Equal(0), "dpkg -r failed:\n%s", uninstallOutput) By("asserting the binary, unit file, byohctl, and generated conf directory are gone") - for _, path := range []string{ + assertPathsRemoved(ctx, containerID, "dpkg -r", []string{ "/binary/pf9-byoh-hostagent", "/etc/systemd/system/pf9-byohost-agent.service", "/etc/pf9-byohost-agent.service.d", "/usr/bin/byohctl", - } { - _, pathExitCode, pathErr := execInContainer(ctx, containerID, []string{"test", "-e", path}, nil) - Expect(pathErr).NotTo(HaveOccurred()) - Expect(pathExitCode).NotTo(Equal(0), "%s should have been removed by dpkg -r", path) - } + }) By("asserting before-remove.sh logged to the same directory every other pf9 log lives in") uninstallLogOutput, exitCode, err := execInContainer(ctx, containerID, diff --git a/test/e2e/packaging/packaging_helpers_test.go b/test/e2e/packaging/packaging_helpers_test.go new file mode 100644 index 000000000..915315f9a --- /dev/null +++ b/test/e2e/packaging/packaging_helpers_test.go @@ -0,0 +1,137 @@ +// Copyright 2026 Platform9, Inc. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package packaging_test + +import ( + "archive/tar" + "bytes" + "context" + "io" + "os" + "strings" + "time" + + "github.com/docker/docker/api/types/container" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func copyFileToContainer(ctx context.Context, containerID, localPath, containerPath string) error { + content, err := os.ReadFile(localPath) + if err != nil { + return err + } + + var buf bytes.Buffer + tw := tar.NewWriter(&buf) + if err := tw.WriteHeader(&tar.Header{ + Name: strings.TrimPrefix(containerPath, "/"), + Mode: 0644, + Size: int64(len(content)), + }); err != nil { + return err + } + if _, err := tw.Write(content); err != nil { + return err + } + if err := tw.Close(); err != nil { + return err + } + + return dockerClient.CopyToContainer(ctx, containerID, "/", &buf, container.CopyToContainerOptions{}) +} + +func execInContainer(ctx context.Context, containerID string, cmd, env []string) (output string, exitCode int, err error) { + created, err := dockerClient.ContainerExecCreate(ctx, containerID, container.ExecOptions{ + Cmd: cmd, + Env: env, + AttachStdout: true, + AttachStderr: true, + Tty: true, + }) + if err != nil { + return "", 0, err + } + + // Tty must match the exec's own creation config: mismatched Tty here can + // leave the daemon multiplexing stdout/stderr with an 8-byte frame header + // per chunk instead of returning the raw stream, corrupting the output. + attached, err := dockerClient.ContainerExecAttach(ctx, created.ID, container.ExecAttachOptions{Tty: true}) + if err != nil { + return "", 0, err + } + defer attached.Close() + + outputBytes, err := io.ReadAll(attached.Reader) + if err != nil { + return "", 0, err + } + + inspected, err := dockerClient.ContainerExecInspect(ctx, created.ID) + if err != nil { + return string(outputBytes), 0, err + } + return string(outputBytes), inspected.ExitCode, nil +} + +func startPackagingContainer(ctx context.Context, image string) (containerID string, cleanup func()) { + created, err := dockerClient.ContainerCreate(ctx, + &container.Config{Image: image}, + &container.HostConfig{ + Privileged: true, + Tmpfs: map[string]string{"/run": "", "/run/lock": "", "/tmp": ""}, + }, + nil, nil, "") + Expect(err).NotTo(HaveOccurred()) + containerID = created.ID + cleanup = func() { + _ = dockerClient.ContainerRemove(ctx, containerID, container.RemoveOptions{Force: true}) + } + + Expect(dockerClient.ContainerStart(ctx, containerID, container.StartOptions{})).To(Succeed()) + + By("waiting for systemd to come up") + Eventually(func() (string, error) { + output, _, execErr := execInContainer(ctx, containerID, []string{"systemctl", "is-system-running"}, nil) + return output, execErr + }, 30*time.Second, time.Second).Should(SatisfyAny( + ContainSubstring("running"), ContainSubstring("degraded"), + )) + + return containerID, cleanup +} + +func assertByohctlRuns(ctx context.Context, containerID string) { + By("asserting byohctl was installed executable and runs") + byohctlOutput, exitCode, err := execInContainer(ctx, containerID, []string{"byohctl", "version"}, nil) + Expect(err).NotTo(HaveOccurred()) + Expect(exitCode).To(Equal(0), "byohctl version failed:\n%s", byohctlOutput) +} + +func assertEnvironmentFile(ctx context.Context, containerID, generatedBy string) { + By("asserting " + generatedBy + " generated the EnvironmentFile the systemd unit reads BOOTSTRAP_KUBECONFIG from") + confOutput, exitCode, err := execInContainer(ctx, containerID, + []string{"cat", "/etc/pf9-byohost-agent.service.d/pf9-byohost-agent.conf"}, nil) + Expect(err).NotTo(HaveOccurred()) + Expect(exitCode).To(Equal(0)) + Expect(confOutput).To(ContainSubstring("BOOTSTRAP_KUBECONFIG=")) + Expect(confOutput).To(ContainSubstring("NAMESPACE=")) + Expect(confOutput).To(ContainSubstring("REGION=")) +} + +// Not asserting is-active: no real cluster for the agent to reach in this environment. +func assertServiceEnabled(ctx context.Context, containerID string) { + By("asserting the service is enabled") + enabledOutput, _, err := execInContainer(ctx, containerID, []string{"systemctl", "is-enabled", "pf9-byohost-agent"}, nil) + Expect(err).NotTo(HaveOccurred()) + Expect(strings.TrimSpace(enabledOutput)).To(Equal("enabled")) +} + +func assertPathsRemoved(ctx context.Context, containerID, removedBy string, paths []string) { + for _, path := range paths { + _, exitCode, err := execInContainer(ctx, containerID, []string{"test", "-e", path}, nil) + Expect(err).NotTo(HaveOccurred()) + Expect(exitCode).NotTo(Equal(0), "%s should have been removed by %s", path, removedBy) + } +} diff --git a/test/e2e/packaging/rpm_install_test.go b/test/e2e/packaging/rpm_install_test.go index 06571231f..13835f096 100644 --- a/test/e2e/packaging/rpm_install_test.go +++ b/test/e2e/packaging/rpm_install_test.go @@ -4,82 +4,17 @@ package packaging_test import ( - "archive/tar" - "bytes" - "context" - "fmt" - "io" "os" "os/exec" "path/filepath" "strings" - "time" - "github.com/docker/docker/api/types/container" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) const rpmContainerPath = "/root/pf9-byohost.rpm" -func copyFileToContainer(ctx context.Context, containerID, localPath, containerPath string) error { - content, err := os.ReadFile(localPath) - if err != nil { - return err - } - - var buf bytes.Buffer - tw := tar.NewWriter(&buf) - if err := tw.WriteHeader(&tar.Header{ - Name: strings.TrimPrefix(containerPath, "/"), - Mode: 0644, - Size: int64(len(content)), - }); err != nil { - return err - } - if _, err := tw.Write(content); err != nil { - return err - } - if err := tw.Close(); err != nil { - return err - } - - return dockerClient.CopyToContainer(ctx, containerID, "/", &buf, container.CopyToContainerOptions{}) -} - -func execInContainer(ctx context.Context, containerID string, cmd, env []string) (output string, exitCode int, err error) { - created, err := dockerClient.ContainerExecCreate(ctx, containerID, container.ExecOptions{ - Cmd: cmd, - Env: env, - AttachStdout: true, - AttachStderr: true, - Tty: true, - }) - if err != nil { - return "", 0, err - } - - // Tty must match the exec's own creation config: mismatched Tty here can - // leave the daemon multiplexing stdout/stderr with an 8-byte frame header - // per chunk instead of returning the raw stream, corrupting the output. - attached, err := dockerClient.ContainerExecAttach(ctx, created.ID, container.ExecAttachOptions{Tty: true}) - if err != nil { - return "", 0, err - } - defer attached.Close() - - outputBytes, err := io.ReadAll(attached.Reader) - if err != nil { - return "", 0, err - } - - inspected, err := dockerClient.ContainerExecInspect(ctx, created.ID) - if err != nil { - return string(outputBytes), 0, err - } - return string(outputBytes), inspected.ExitCode, nil -} - var _ = Describe("pf9-byohost RPM", func() { It("installs cleanly and uninstalls cleanly", func() { repoRootBytes, err := exec.Command("git", "rev-parse", "--show-toplevel").Output() @@ -101,28 +36,8 @@ var _ = Describe("pf9-byohost RPM", func() { rpmPath := matches[0] By("starting a Rocky Linux container") - created, err := dockerClient.ContainerCreate(ctx, - &container.Config{Image: rpmTestImage}, - &container.HostConfig{ - Privileged: true, - Tmpfs: map[string]string{"/run": "", "/run/lock": "", "/tmp": ""}, - }, - nil, nil, "") - Expect(err).NotTo(HaveOccurred()) - containerID := created.ID - defer func() { - _ = dockerClient.ContainerRemove(ctx, containerID, container.RemoveOptions{Force: true}) - }() - - Expect(dockerClient.ContainerStart(ctx, containerID, container.StartOptions{})).To(Succeed()) - - By("waiting for systemd to come up") - Eventually(func() (string, error) { - output, _, execErr := execInContainer(ctx, containerID, []string{"systemctl", "is-system-running"}, nil) - return output, execErr - }, 30*time.Second, time.Second).Should(SatisfyAny( - ContainSubstring("running"), ContainSubstring("degraded"), - )) + containerID, cleanup := startPackagingContainer(ctx, rpmTestImage) + defer cleanup() By("copying the built RPM into the container") Expect(copyFileToContainer(ctx, containerID, rpmPath, rpmContainerPath)).To(Succeed()) @@ -144,26 +59,9 @@ var _ = Describe("pf9-byohost RPM", func() { "/usr/bin/byohctl", )) - By("asserting byohctl was installed executable and runs") - byohctlOutput, exitCode, err := execInContainer(ctx, containerID, []string{"byohctl", "version"}, nil) - Expect(err).NotTo(HaveOccurred()) - Expect(exitCode).To(Equal(0), "byohctl version failed:\n%s", byohctlOutput) - - By("asserting %post generated the EnvironmentFile the systemd unit reads BOOTSTRAP_KUBECONFIG from") - confOutput, exitCode, err := execInContainer(ctx, containerID, - []string{"cat", "/etc/pf9-byohost-agent.service.d/pf9-byohost-agent.conf"}, nil) - Expect(err).NotTo(HaveOccurred()) - Expect(exitCode).To(Equal(0)) - Expect(confOutput).To(ContainSubstring("BOOTSTRAP_KUBECONFIG=")) - Expect(confOutput).To(ContainSubstring("NAMESPACE=")) - Expect(confOutput).To(ContainSubstring("REGION=")) - - // Not asserting is-active: the agent gets a stub, empty kubeconfig - // with no real cluster to register with - By("asserting the service is enabled") - enabledOutput, _, err := execInContainer(ctx, containerID, []string{"systemctl", "is-enabled", "pf9-byohost-agent"}, nil) - Expect(err).NotTo(HaveOccurred()) - Expect(strings.TrimSpace(enabledOutput)).To(Equal("enabled")) + assertByohctlRuns(ctx, containerID) + assertEnvironmentFile(ctx, containerID, "%post") + assertServiceEnabled(ctx, containerID) By("uninstalling the RPM") uninstallOutput, exitCode, err := execInContainer(ctx, containerID, []string{"rpm", "-e", "pf9-byohost"}, nil) @@ -171,14 +69,10 @@ var _ = Describe("pf9-byohost RPM", func() { Expect(exitCode).To(Equal(0), "rpm -e failed:\n%s", uninstallOutput) By("asserting the binary, unit file, and byohctl are gone") - for _, path := range []string{ + assertPathsRemoved(ctx, containerID, "rpm -e", []string{ "/binary/pf9-byoh-hostagent", "/etc/systemd/system/pf9-byohost-agent.service", "/usr/bin/byohctl", - } { - _, exitCode, err := execInContainer(ctx, containerID, []string{"test", "-e", path}, nil) - Expect(err).NotTo(HaveOccurred()) - Expect(exitCode).NotTo(Equal(0), fmt.Sprintf("%s should have been removed by rpm -e", path)) - } + }) }) })