Skip to content

Commit 5279b6e

Browse files
Rajath Agasthyaaramprice
authored andcommitted
Move integration2 tests to integration package
Rewrites those tests in Ginkgo.
1 parent 5d82524 commit 5279b6e

File tree

9 files changed

+242
-280
lines changed

9 files changed

+242
-280
lines changed

src/bpm/integration/integration_suite_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"gopkg.in/yaml.v3"
3434

3535
"bpm/config"
36+
"bpm/integration/sandbox"
3637
)
3738

3839
// This needs to be different from /tmp as /tmp is bind mounted into the
@@ -55,6 +56,7 @@ var _ = SynchronizedBeforeSuite(func() []byte {
5556
return []byte(bpmPath)
5657
}, func(data []byte) {
5758
bpmPath = string(data)
59+
sandbox.BPMPath = bpmPath
5860
SetDefaultEventuallyTimeout(5 * time.Second)
5961
})
6062

src/bpm/integration/run_test.go

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
// Copyright (C) 2017-Present CloudFoundry.org Foundation, Inc. All rights reserved.
2+
//
3+
// This program and the accompanying materials are made available under
4+
// the terms of the under the Apache License, Version 2.0 (the "License”);
5+
// you may not use this file except in compliance with the License.
6+
//
7+
// You may obtain a copy of the License at
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
// License for the specific language governing permissions and limitations
14+
// under the License.
15+
16+
package integration
17+
18+
import (
19+
"bufio"
20+
"bytes"
21+
"fmt"
22+
"math/rand"
23+
"os"
24+
"path/filepath"
25+
"strings"
26+
27+
. "github.com/onsi/ginkgo/v2"
28+
. "github.com/onsi/gomega"
29+
"github.com/onsi/gomega/gbytes"
30+
"github.com/onsi/gomega/gexec"
31+
32+
"bpm/integration/sandbox"
33+
)
34+
35+
var _ = Describe("BPM run command", func() {
36+
var (
37+
s *sandbox.Sandbox
38+
errandJob string
39+
)
40+
41+
BeforeEach(func() {
42+
s = sandbox.New(GinkgoTB())
43+
errandJob = uniqueJobName("errand")
44+
})
45+
46+
AfterEach(func() {
47+
if s != nil {
48+
s.Cleanup()
49+
}
50+
})
51+
52+
Context("bpm run", func() {
53+
const (
54+
stdoutSentinel = "stdout"
55+
stderrSentinel = "stderr"
56+
)
57+
58+
BeforeEach(func() {
59+
s.LoadFixture(errandJob, "testdata/errand.yml")
60+
})
61+
62+
It("executes bpm run, captures stdout/stderr, logs to files and cleans up pid files", func() {
63+
cmd := s.BPMCmd("run", errandJob, "-p", "errand")
64+
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
65+
Expect(err).NotTo(HaveOccurred())
66+
Eventually(session).Should(gexec.Exit(0))
67+
68+
Expect(session.Out).To(gbytes.Say(stdoutSentinel))
69+
Expect(session.Err).To(gbytes.Say(stderrSentinel))
70+
71+
stdoutLogPath := s.Path("sys", "log", errandJob, "errand.stdout.log")
72+
Expect(stdoutLogPath).To(BeAnExistingFile())
73+
stdoutLogContents, err := os.ReadFile(stdoutLogPath)
74+
Expect(err).NotTo(HaveOccurred())
75+
Expect(string(stdoutLogContents)).To(ContainSubstring(stdoutSentinel))
76+
77+
stderrLogPath := s.Path("sys", "log", errandJob, "errand.stderr.log")
78+
Expect(stderrLogPath).To(BeAnExistingFile())
79+
stderrLogContents, err := os.ReadFile(stderrLogPath)
80+
Expect(err).NotTo(HaveOccurred())
81+
Expect(string(stderrLogContents)).To(ContainSubstring(stderrSentinel))
82+
83+
pidfile := s.Path("sys", "run", "bpm", errandJob, "errand.pid")
84+
Expect(pidfile).NotTo(BeAnExistingFile())
85+
})
86+
87+
})
88+
89+
Context("with -e environment variable flags", func() {
90+
const sentinel = "sentinel"
91+
92+
BeforeEach(func() {
93+
s.LoadFixture(errandJob, "testdata/env-flag.yml")
94+
})
95+
96+
It("passes the environment variables to the process", func() {
97+
cmd := s.BPMCmd(
98+
"run",
99+
errandJob,
100+
"-p", "errand",
101+
"-e", fmt.Sprintf("ENVKEY=%s", sentinel),
102+
)
103+
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
104+
Expect(err).NotTo(HaveOccurred())
105+
Eventually(session).Should(gexec.Exit(0))
106+
107+
Expect(session.Out).To(gbytes.Say(sentinel))
108+
})
109+
})
110+
111+
Context("with -v volume flags", func() {
112+
var (
113+
extraVolumeDir string
114+
extraVolumeFile string
115+
sentinel = "success"
116+
)
117+
118+
BeforeEach(func() {
119+
s.LoadFixture(errandJob, "testdata/volume-flag.yml")
120+
extraVolumeDir = s.Path("data", "extra-volume")
121+
extraVolumeFile = filepath.Join(extraVolumeDir, "data.txt")
122+
})
123+
124+
It("mounts the specified volume, verifies mount options and file content", func() {
125+
cmd := s.BPMCmd(
126+
"run",
127+
errandJob,
128+
"-p", "errand",
129+
"-v", fmt.Sprintf("%s:writable,allow_executions", extraVolumeDir),
130+
"-e", fmt.Sprintf("FILE_TO_WRITE_TO=%s", extraVolumeFile),
131+
)
132+
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
133+
Expect(err).NotTo(HaveOccurred())
134+
Eventually(session).Should(gexec.Exit(0))
135+
136+
mounts, err := parseFstab(session.Out.Contents())
137+
Expect(err).NotTo(HaveOccurred())
138+
Expect(mounts).To(HaveLen(1))
139+
140+
firstMount := mounts[0]
141+
Expect(firstMount.MountPoint).To(Equal(extraVolumeDir))
142+
Expect(mountHasOption(firstMount, "ro")).To(BeFalse())
143+
Expect(mountHasOption(firstMount, "noexec")).To(BeFalse())
144+
145+
Expect(extraVolumeFile).To(BeAnExistingFile())
146+
fileContents, err := os.ReadFile(extraVolumeFile)
147+
Expect(err).NotTo(HaveOccurred())
148+
Expect(string(fileContents)).To(ContainSubstring(sentinel))
149+
})
150+
})
151+
152+
Context("when the process fails", func() {
153+
BeforeEach(func() {
154+
s.LoadFixture("oops", "testdata/failure.yml")
155+
})
156+
157+
It("returns a non-zero exit code", func() {
158+
cmd := s.BPMCmd("run", "oops")
159+
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
160+
Expect(err).NotTo(HaveOccurred())
161+
Eventually(session).Should(gexec.Exit())
162+
Expect(session.ExitCode()).ShouldNot(Equal(0))
163+
})
164+
})
165+
166+
Context("when the process exits with an unusual status", func() {
167+
BeforeEach(func() {
168+
// exit status 6
169+
s.LoadFixture("odd", "testdata/odd-status.yml")
170+
})
171+
172+
It("bpm exits with the same status code", func() {
173+
cmd := s.BPMCmd("run", "odd")
174+
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
175+
Expect(err).NotTo(HaveOccurred())
176+
Eventually(session).Should(gexec.Exit(6))
177+
})
178+
})
179+
180+
})
181+
182+
// uniqueJobName appends a random suffix to a job name.
183+
// This is used by tests that run the same job in parallel inorder to not conflict with each other.
184+
func uniqueJobName(prefix string) string {
185+
const charset = "0123456789abcdef"
186+
b := make([]byte, 8)
187+
for i := range b {
188+
b[i] = charset[rand.Intn(len(charset))]
189+
}
190+
return fmt.Sprintf("%s-%s", prefix, string(b))
191+
}
192+
193+
type mount struct {
194+
MountPoint string
195+
Options []string
196+
}
197+
198+
// ParseFstab parses byte slices which contain the contents of files formatted
199+
// as described by fstab(5).
200+
func parseFstab(contents []byte) ([]mount, error) {
201+
var mounts []mount
202+
203+
r := bytes.NewBuffer(contents)
204+
scanner := bufio.NewScanner(r)
205+
for scanner.Scan() {
206+
fields := strings.Fields(scanner.Text())
207+
if len(fields) < 6 {
208+
return nil, fmt.Errorf("invalid mount: %s", scanner.Text())
209+
}
210+
211+
options := strings.Split(fields[3], ",")
212+
mounts = append(mounts, mount{
213+
MountPoint: fields[1],
214+
Options: options,
215+
})
216+
}
217+
218+
return mounts, nil
219+
}
220+
221+
func mountHasOption(m mount, opt string) bool {
222+
for _, o := range m.Options {
223+
if o == opt {
224+
return true
225+
}
226+
}
227+
228+
return false
229+
}

src/bpm/integration2/bpmsandbox/sandbox.go renamed to src/bpm/integration/sandbox/sandbox.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// License for the specific language governing permissions and limitations
1414
// under the License.
1515

16-
package bpmsandbox
16+
package sandbox
1717

1818
import (
1919
"fmt"
@@ -24,19 +24,21 @@ import (
2424
"testing"
2525
)
2626

27-
var (
28-
RuncPath string
29-
BPMPath string
30-
TiniPath string
27+
var BPMPath string
28+
29+
const (
30+
// Binary paths inside the container used to run tests
31+
runcExe = "/var/vcap/packages/bpm/bin/runc"
32+
tiniExe = "/var/vcap/packages/bpm/bin/tini"
3133
)
3234

3335
type Sandbox struct {
34-
t *testing.T
36+
t testing.TB
3537

3638
root string
3739
}
3840

39-
func New(t *testing.T) *Sandbox {
41+
func New(t testing.TB) *Sandbox {
4042
t.Helper()
4143

4244
root, err := os.MkdirTemp("", "bpm_sandbox")
@@ -60,12 +62,12 @@ func New(t *testing.T) *Sandbox {
6062
}
6163

6264
runcSandboxPath := filepath.Join(root, "packages", "bpm", "bin", "runc")
63-
if err := os.Symlink(RuncPath, runcSandboxPath); err != nil {
65+
if err := os.Symlink(runcExe, runcSandboxPath); err != nil {
6466
t.Fatalf("could not link runc executable into sandbox: %v", err)
6567
}
6668

6769
tiniSandboxPath := filepath.Join(root, "packages", "bpm", "bin", "tini")
68-
if err := copyFile(tiniSandboxPath, TiniPath); err != nil {
70+
if err := copyFile(tiniSandboxPath, tiniExe); err != nil {
6971
t.Fatalf("could not copy tini executable into sandbox: %v", err)
7072
}
7173
if err := os.Chown(tiniSandboxPath, 2000, 3000); err != nil {
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)