|
5 | 5 | package e2e_test |
6 | 6 |
|
7 | 7 | import ( |
| 8 | + "bufio" |
8 | 9 | "bytes" |
9 | 10 | "fmt" |
| 11 | + "io" |
| 12 | + "os" |
10 | 13 | "os/exec" |
| 14 | + "strings" |
| 15 | + "sync" |
11 | 16 |
|
12 | 17 | . "github.com/onsi/ginkgo/v2" |
13 | 18 | . "github.com/onsi/gomega" |
14 | 19 | ) |
15 | 20 |
|
16 | | -// Define the mgradm binary path |
17 | | -const mgradmPath = "/workspace/bin/mgradm" |
| 21 | +const mgradmPath = "./../../bin/mgradm" |
| 22 | +const backend = "podman" |
18 | 23 |
|
19 | | -// runMgradmCommand is a helper function to execute mgradm and capture its output. |
20 | | -// It returns the combined stdout+stderr and an error if the command fails to run. |
21 | | -func runMgradmCommand(args []string) (string, error) { |
| 24 | +// interaction defines a prompt to look for and the corresponding response to send. |
| 25 | +type interaction struct { |
| 26 | + Prompt string |
| 27 | + Response string |
| 28 | +} |
| 29 | + |
| 30 | +// runMgradmCommand executes mgradm, handling an ordered sequence of interactive prompts. |
| 31 | +func runMgradmCommand(args []string, interactions []interaction) (string, error) { |
22 | 32 | cmd := exec.Command(mgradmPath, args...) |
23 | 33 |
|
24 | | - var stdoutBuffer bytes.Buffer |
25 | | - var stderrBuffer bytes.Buffer |
26 | | - cmd.Stdout = &stdoutBuffer |
27 | | - cmd.Stderr = &stderrBuffer |
| 34 | + var combinedOutputBuffer bytes.Buffer |
| 35 | + var wg sync.WaitGroup |
| 36 | + |
| 37 | + stdoutPipe, err := cmd.StdoutPipe() |
| 38 | + if err != nil { |
| 39 | + return "", fmt.Errorf("error creating stdout pipe: %w", err) |
| 40 | + } |
| 41 | + |
| 42 | + stderrPipe, err := cmd.StderrPipe() |
| 43 | + if err != nil { |
| 44 | + return "", fmt.Errorf("error creating stderr pipe: %w", err) |
| 45 | + } |
| 46 | + |
| 47 | + stdinPipe, err := cmd.StdinPipe() |
| 48 | + if err != nil { |
| 49 | + return "", fmt.Errorf("error creating stdin pipe: %w", err) |
| 50 | + } |
| 51 | + |
| 52 | + interactionReader, interactionWriter := io.Pipe() |
| 53 | + mainWriter := io.MultiWriter(&combinedOutputBuffer, GinkgoWriter, interactionWriter) |
28 | 54 |
|
29 | 55 | By(fmt.Sprintf("Running command: %s %v", mgradmPath, args)) |
| 56 | + if err := cmd.Start(); err != nil { |
| 57 | + err := interactionWriter.Close() |
| 58 | + if err != nil { |
| 59 | + return "", err |
| 60 | + } |
| 61 | + return "", fmt.Errorf("error starting command: %w", err) |
| 62 | + } |
| 63 | + |
| 64 | + // This goroutine handles the ordered sequence of interactions. |
| 65 | + wg.Add(1) |
| 66 | + go func() { |
| 67 | + defer wg.Done() |
| 68 | + defer stdinPipe.Close() |
30 | 69 |
|
31 | | - err := cmd.Run() |
32 | | - output := stdoutBuffer.String() + stderrBuffer.String() |
| 70 | + interactionIndex := 0 |
| 71 | + scanner := bufio.NewScanner(interactionReader) |
| 72 | + for scanner.Scan() { |
| 73 | + line := scanner.Text() |
| 74 | + // Check if there are still interactions left to perform. |
| 75 | + if interactionIndex < len(interactions) { |
| 76 | + currentInteraction := interactions[interactionIndex] |
| 77 | + if strings.Contains(line, currentInteraction.Prompt) { |
| 78 | + By(fmt.Sprintf("Answering prompt '%s' with '%s'", currentInteraction.Prompt, currentInteraction.Response)) |
| 79 | + _, err := io.WriteString(stdinPipe, currentInteraction.Response+"\n") |
| 80 | + if err != nil { |
| 81 | + GinkgoWriter.Printf("Error writing to stdin: %v", err) |
| 82 | + } |
| 83 | + // Move to the next expected interaction. |
| 84 | + interactionIndex++ |
| 85 | + } |
| 86 | + } |
| 87 | + } |
| 88 | + }() |
33 | 89 |
|
34 | | - return output, err |
| 90 | + // Goroutines to copy stdout and stderr to the main writer. |
| 91 | + wg.Add(2) |
| 92 | + go func() { |
| 93 | + defer wg.Done() |
| 94 | + _, err := io.Copy(mainWriter, stdoutPipe) |
| 95 | + if err != nil { |
| 96 | + return |
| 97 | + } |
| 98 | + }() |
| 99 | + go func() { |
| 100 | + defer wg.Done() |
| 101 | + _, err := io.Copy(mainWriter, stderrPipe) |
| 102 | + if err != nil { |
| 103 | + return |
| 104 | + } |
| 105 | + }() |
| 106 | + |
| 107 | + err = cmd.Wait() |
| 108 | + interactionWriter.Close() |
| 109 | + wg.Wait() |
| 110 | + |
| 111 | + return combinedOutputBuffer.String(), err |
35 | 112 | } |
36 | 113 |
|
| 114 | +var _ = BeforeSuite(func() { |
| 115 | + _, err := os.Stat(mgradmPath) |
| 116 | + Expect(err).To(Succeed(), "mgradm binary not found at %s. Error: %v", mgradmPath, err) |
| 117 | + |
| 118 | + GinkgoWriter.Printf("Using mgradm: %s\n", mgradmPath) |
| 119 | +}) |
| 120 | + |
37 | 121 | var _ = Describe("mgradm tests", func() { |
| 122 | + // Ensure that the environment is clean before each test. |
| 123 | + BeforeEach(func() { |
| 124 | + By(fmt.Sprintf("Running mgradm cleanup: uninstall --backend %s --force --purge-volumes --purge-images", backend)) |
| 125 | + cleanupArgs := []string{"uninstall", "--backend", backend, "--force", "--purge-volumes", "--purge-images"} |
| 126 | + output, err := runMgradmCommand(cleanupArgs, nil) |
| 127 | + Expect(err).To(Succeed(), "Failed to run mgradm cleanup command. Error: %v\nOutput: %s", err, output) |
| 128 | + }) |
38 | 129 |
|
39 | 130 | Context("install command", func() { |
40 | | - It("should run without error with default options", func() { |
41 | | - args := []string{"install", "podman", "--logLevel", "debug"} |
42 | | - output, err := runMgradmCommand(args) |
| 131 | + It("should run without error and handle repeated prompts", func() { |
| 132 | + args := []string{"install", backend, "--logLevel", "debug"} |
| 133 | + // Define the ordered sequence of interactions. |
| 134 | + interactions := []interaction{ |
| 135 | + {Prompt: "Password for the CA key to generate:", Response: "uyuni"}, |
| 136 | + {Prompt: "Confirm the password:", Response: "uyuni"}, |
| 137 | + {Prompt: "Administrator password:", Response: "admin"}, |
| 138 | + {Prompt: "Confirm the password:", Response: "admin"}, |
| 139 | + } |
| 140 | + output, err := runMgradmCommand(args, interactions) |
43 | 141 |
|
44 | 142 | Expect(err).To(Succeed(), "Command failed with error: %v\nOutput: %s", err, output) |
45 | | - Expect(output).To(ContainSubstring("Starting /workspace/bin/mgradm install podman --logLevel debug")) |
46 | | - Expect(output).To(ContainSubstring("Use of this software implies acceptance of the End User License Agreement.")) |
47 | | - Expect(output).To(ContainSubstring("application log level=debug")) |
| 143 | + Expect(output).Should(ContainSubstring("mgradm install %s --logLevel debug", backend)) |
| 144 | + Expect(output).Should(ContainSubstring("Setting up uyuni network")) |
| 145 | + Expect(output).Should(ContainSubstring("Enabling system service")) |
| 146 | + Expect(output).Should(ContainSubstring("Waiting for the server to start")) |
| 147 | + Expect(output).Should(ContainSubstring("Run setup command in the container")) |
| 148 | + Expect(output).Should(ContainSubstring("Populating the database")) |
| 149 | + Expect(output).ShouldNot(MatchRegexp(`(?i)\berr(or)?\b`)) |
| 150 | + Expect(output).Should(ContainSubstring("Server configuration finished")) |
48 | 151 | }) |
49 | 152 |
|
50 | 153 | It("should show help for install command", func() { |
51 | 154 | args := []string{"install", "--help"} |
52 | | - output, err := runMgradmCommand(args) |
| 155 | + output, err := runMgradmCommand(args, nil) |
53 | 156 | Expect(err).To(Succeed()) |
54 | 157 | Expect(output).To(ContainSubstring("Install a new server")) |
55 | 158 | }) |
|
0 commit comments