mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-01-20 05:19:40 +02:00
Rewrite mta IT using docker cli (#1819)
This change addresses some issues of the testcontainer based testing approach (much repeated code, API not on the right abstraction level). It introduces new methods that make use of the docker cli, and rewrites the mta tests using this method. Co-authored-by: Stephan Aßmus <stephan.assmus@sap.com>
This commit is contained in:
parent
94dba13fef
commit
219327a427
129
integration/docker_integration_test_executor.go
Normal file
129
integration/docker_integration_test_executor.go
Normal file
@ -0,0 +1,129 @@
|
||||
// +build integration
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/SAP/jenkins-library/pkg/command"
|
||||
"github.com/SAP/jenkins-library/pkg/log"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// IntegrationTestDockerExecRunner keeps the state of an instance of a docker runner
|
||||
type IntegrationTestDockerExecRunner struct {
|
||||
// Runner is the ExecRunner to which all executions are forwarded in the end.
|
||||
Runner command.Command
|
||||
Image string
|
||||
User string
|
||||
TestDir []string
|
||||
Mounts map[string]string
|
||||
Environment map[string]string
|
||||
Setup []string
|
||||
ContainerName string
|
||||
}
|
||||
|
||||
// IntegrationTestDockerExecRunnerBundle is used to construct an instance of IntegrationTestDockerExecRunner
|
||||
type IntegrationTestDockerExecRunnerBundle struct {
|
||||
Image string
|
||||
User string
|
||||
TestDir []string
|
||||
Mounts map[string]string
|
||||
Environment map[string]string
|
||||
Setup []string
|
||||
}
|
||||
|
||||
func givenThisContainer(t *testing.T, bundle IntegrationTestDockerExecRunnerBundle) IntegrationTestDockerExecRunner {
|
||||
runner := command.Command{}
|
||||
|
||||
// Generate a random container name so we can start a new one for each test method
|
||||
// We don't rely on docker's random name generator for two reasons
|
||||
// First, it is easier to save the name here compared to getting it from stdout
|
||||
// Second, the common prefix allows batch stopping/deleting of containers if so desired
|
||||
// The test code will not automatically delete containers as they might be useful for debugging
|
||||
var seededRand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
containerName := "piper-integration-test-" + strconv.Itoa(seededRand.Int())
|
||||
|
||||
testRunner := IntegrationTestDockerExecRunner{
|
||||
Runner: runner,
|
||||
Image: bundle.Image,
|
||||
User: bundle.User,
|
||||
Mounts: bundle.Mounts,
|
||||
Setup: bundle.Setup,
|
||||
ContainerName: containerName,
|
||||
}
|
||||
|
||||
//todo ensure it is a linux binary
|
||||
wd, _ := os.Getwd()
|
||||
localPiper := path.Join(wd, "..", "piper")
|
||||
if localPiper == "" {
|
||||
t.Fatal("Could not locate piper binary to test")
|
||||
}
|
||||
|
||||
projectDir := path.Join(wd, path.Join(bundle.TestDir...))
|
||||
|
||||
// 1. Copy test files to a temp dir in order to avoid non-repeatable test executions because of changed state
|
||||
// 2. Don't remove the temp dir to allow investigation of failed tests. Maybe add an option for cleaning it later?
|
||||
tempDir, err := ioutil.TempDir("", "piper-integration-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = copyDir(projectDir, tempDir)
|
||||
if err != nil {
|
||||
t.Fatalf("")
|
||||
}
|
||||
|
||||
//todo mounts
|
||||
//todo env (secrets)
|
||||
err = testRunner.Runner.RunExecutable("docker", "run", "-d", "-u="+testRunner.User,
|
||||
"-v", localPiper+":/piper", "-v", tempDir+":/project",
|
||||
"--name="+testRunner.ContainerName,
|
||||
testRunner.Image,
|
||||
"sleep", "2000")
|
||||
if err != nil {
|
||||
t.Fatalf("Starting test container has failed %s", err)
|
||||
}
|
||||
for _, scriptLine := range testRunner.Setup {
|
||||
err := testRunner.Runner.RunExecutable("docker", "exec", testRunner.ContainerName, "/bin/bash", "-c", scriptLine)
|
||||
if err != nil {
|
||||
t.Fatalf("Running setup script in test container has failed %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = testRunner.Runner.RunExecutable("docker", "cp", "piper-command-wrapper.sh", testRunner.ContainerName+":/piper-wrapper")
|
||||
if err != nil {
|
||||
t.Fatalf("Copying command wrapper to container has failed %s", err)
|
||||
}
|
||||
err = testRunner.Runner.RunExecutable("docker", "exec", testRunner.ContainerName, "chmod", "+x", "/piper-wrapper")
|
||||
if err != nil {
|
||||
t.Fatalf("Making command wrapper in container executable has failed %s", err)
|
||||
}
|
||||
return testRunner
|
||||
}
|
||||
|
||||
func (d *IntegrationTestDockerExecRunner) whenRunningPiperCommand(command string, parameters ...string) error {
|
||||
args := []string{"exec", "--workdir", "/project", d.ContainerName, "/bin/bash", "/piper-wrapper", "/piper", command}
|
||||
args = append(args, parameters...)
|
||||
return d.Runner.RunExecutable("docker", args...)
|
||||
}
|
||||
|
||||
func (d *IntegrationTestDockerExecRunner) assertHasOutput(t *testing.T, want string) {
|
||||
buffer := new(bytes.Buffer)
|
||||
d.Runner.Stdout(buffer)
|
||||
err := d.Runner.RunExecutable("docker", "exec", d.ContainerName, "cat", "/tmp/test-log.txt")
|
||||
d.Runner.Stdout(log.Writer())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get log output of container %s", d.ContainerName)
|
||||
}
|
||||
|
||||
if !strings.Contains(buffer.String(), want) {
|
||||
t.Fatalf("Assertion has failed. Expected output %s in command output.\n%s", want, buffer.String())
|
||||
}
|
||||
}
|
@ -4,210 +4,79 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
)
|
||||
|
||||
func TestMavenProject(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("Getting current working directory failed: %v", err)
|
||||
}
|
||||
pwd = filepath.Dir(pwd)
|
||||
|
||||
// using custom createTmpDir function to avoid issues with symlinks on Docker for Mac
|
||||
tempDir, err := createTmpDir("")
|
||||
defer os.RemoveAll(tempDir) // clean up
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error when creating temp dir: %v", err)
|
||||
}
|
||||
|
||||
err = copyDir(filepath.Join(pwd, "integration", "testdata", "TestMtaIntegration", "maven"), tempDir)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to copy test project.")
|
||||
}
|
||||
|
||||
//workaround to use test script util it is possible to set workdir for Exec call
|
||||
testScript := `#!/bin/sh
|
||||
cd /test
|
||||
apt-get -yqq update; apt-get -yqq install make
|
||||
curl -OL https://github.com/SAP/cloud-mta-build-tool/releases/download/v1.0.14/cloud-mta-build-tool_1.0.14_Linux_amd64.tar.gz
|
||||
tar xzf cloud-mta-build-tool_1.0.14_Linux_amd64.tar.gz
|
||||
curl -sL https://deb.nodesource.com/setup_12.x | bash -
|
||||
apt-get install -yqq nodejs
|
||||
mv mbt /usr/bin
|
||||
mkdir mym2
|
||||
/piperbin/piper mtaBuild --installArtifacts --m2Path=mym2 >test-log.txt 2>&1
|
||||
`
|
||||
ioutil.WriteFile(filepath.Join(tempDir, "runPiper.sh"), []byte(testScript), 0700)
|
||||
|
||||
reqNode := testcontainers.ContainerRequest{
|
||||
Image: "maven:3-openjdk-8-slim",
|
||||
Cmd: []string{"tail", "-f"},
|
||||
|
||||
BindMounts: map[string]string{
|
||||
pwd: "/piperbin",
|
||||
tempDir: "/test",
|
||||
container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{
|
||||
Image: "maven:3-openjdk-8-slim",
|
||||
User: "root",
|
||||
TestDir: []string{"testdata", "TestMtaIntegration", "maven"},
|
||||
Mounts: map[string]string{},
|
||||
Setup: []string{
|
||||
"apt-get -yqq update; apt-get -yqq install make",
|
||||
"curl -OL https://github.com/SAP/cloud-mta-build-tool/releases/download/v1.0.14/cloud-mta-build-tool_1.0.14_Linux_amd64.tar.gz",
|
||||
"tar xzf cloud-mta-build-tool_1.0.14_Linux_amd64.tar.gz",
|
||||
"mv mbt /usr/bin",
|
||||
"curl -sL https://deb.nodesource.com/setup_12.x | bash -",
|
||||
"apt-get install -yqq nodejs",
|
||||
},
|
||||
}
|
||||
|
||||
mbtContainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||
ContainerRequest: reqNode,
|
||||
Started: true,
|
||||
})
|
||||
|
||||
code, err := mbtContainer.Exec(ctx, []string{"sh", "/test/runPiper.sh"})
|
||||
|
||||
err := container.whenRunningPiperCommand("mtaBuild", "--installArtifacts", "--m2Path=mym2")
|
||||
if err != nil {
|
||||
t.Fatalf("Script returened error: %v", err)
|
||||
t.Fatalf("Piper command failed %s", err)
|
||||
}
|
||||
assert.Equal(t, 0, code)
|
||||
|
||||
content, err := ioutil.ReadFile(filepath.Join(tempDir, "/test-log.txt"))
|
||||
if err != nil {
|
||||
t.Fatal("Could not read test-log.txt.", err)
|
||||
}
|
||||
output := string(content)
|
||||
assert.Contains(t, output, "Installing /test/.flattened-pom.xml to /test/mym2/mygroup/mymvn/1.0-SNAPSHOT/mymvn-1.0-SNAPSHOT.pom")
|
||||
assert.Contains(t, output, "Installing /test/app/target/mymvn-app-1.0-SNAPSHOT.war to /test/mym2/mygroup/mymvn-app/1.0-SNAPSHOT/mymvn-app-1.0-SNAPSHOT.war")
|
||||
assert.Contains(t, output, "Installing /test/app/target/mymvn-app-1.0-SNAPSHOT-classes.jar to /test/mym2/mygroup/mymvn-app/1.0-SNAPSHOT/mymvn-app-1.0-SNAPSHOT-classes.jar")
|
||||
assert.Contains(t, output, "added 2 packages from 3 contributors and audited 2 packages in")
|
||||
container.assertHasOutput(t, "Installing /project/.flattened-pom.xml to /project/mym2/mygroup/mymvn/1.0-SNAPSHOT/mymvn-1.0-SNAPSHOT.pom")
|
||||
container.assertHasOutput(t, "Installing /project/app/target/mymvn-app-1.0-SNAPSHOT.war to /project/mym2/mygroup/mymvn-app/1.0-SNAPSHOT/mymvn-app-1.0-SNAPSHOT.war")
|
||||
container.assertHasOutput(t, "Installing /project/app/target/mymvn-app-1.0-SNAPSHOT-classes.jar to /project/mym2/mygroup/mymvn-app/1.0-SNAPSHOT/mymvn-app-1.0-SNAPSHOT-classes.jar")
|
||||
container.assertHasOutput(t, "added 2 packages from 3 contributors and audited 2 packages in")
|
||||
}
|
||||
|
||||
func TestNPMProject(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("Getting current working directory failed: %v", err)
|
||||
}
|
||||
pwd = filepath.Dir(pwd)
|
||||
|
||||
// using custom createTmpDir function to avoid issues with symlinks on Docker for Mac
|
||||
tempDir, err := createTmpDir("")
|
||||
defer os.RemoveAll(tempDir) // clean up
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error when creating temp dir: %v", err)
|
||||
}
|
||||
|
||||
err = copyDir(filepath.Join(pwd, "integration", "testdata", "TestMtaIntegration", "npm"), tempDir)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to copy test project.")
|
||||
}
|
||||
|
||||
//workaround to use test script util it is possible to set workdir for Exec call
|
||||
testScript := `#!/bin/sh
|
||||
cd /test
|
||||
apt-get -yqq update; apt-get -yqq install make
|
||||
curl -OL https://github.com/SAP/cloud-mta-build-tool/releases/download/v1.0.14/cloud-mta-build-tool_1.0.14_Linux_amd64.tar.gz
|
||||
tar xzf cloud-mta-build-tool_1.0.14_Linux_amd64.tar.gz
|
||||
mv mbt /usr/bin
|
||||
/piperbin/piper mtaBuild >test-log.txt 2>&1
|
||||
`
|
||||
ioutil.WriteFile(filepath.Join(tempDir, "runPiper.sh"), []byte(testScript), 0700)
|
||||
|
||||
reqNode := testcontainers.ContainerRequest{
|
||||
Image: "node:12",
|
||||
Cmd: []string{"tail", "-f"},
|
||||
|
||||
BindMounts: map[string]string{
|
||||
pwd: "/piperbin",
|
||||
tempDir: "/test",
|
||||
container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{
|
||||
Image: "node:12",
|
||||
User: "root",
|
||||
TestDir: []string{"testdata", "TestMtaIntegration", "npm"},
|
||||
Mounts: map[string]string{},
|
||||
Setup: []string{
|
||||
"apt-get -yqq update; apt-get -yqq install make",
|
||||
"curl -OL https://github.com/SAP/cloud-mta-build-tool/releases/download/v1.0.14/cloud-mta-build-tool_1.0.14_Linux_amd64.tar.gz",
|
||||
"tar xzf cloud-mta-build-tool_1.0.14_Linux_amd64.tar.gz",
|
||||
"mv mbt /usr/bin",
|
||||
},
|
||||
}
|
||||
|
||||
mbtContainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||
ContainerRequest: reqNode,
|
||||
Started: true,
|
||||
})
|
||||
|
||||
code, err := mbtContainer.Exec(ctx, []string{"sh", "/test/runPiper.sh"})
|
||||
|
||||
err := container.whenRunningPiperCommand("mtaBuild", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Script returened error: %v", err)
|
||||
t.Fatalf("Piper command failed %s", err)
|
||||
}
|
||||
assert.Equal(t, 0, code)
|
||||
|
||||
content, err := ioutil.ReadFile(filepath.Join(tempDir, "/test-log.txt"))
|
||||
if err != nil {
|
||||
t.Fatal("Could not read test-log.txt.", err)
|
||||
}
|
||||
output := string(content)
|
||||
assert.Contains(t, output, "INFO the MTA archive generated at: test-mta-js.mtar")
|
||||
container.assertHasOutput(t, "INFO the MTA archive generated at: test-mta-js.mtar")
|
||||
}
|
||||
|
||||
func TestNPMProjectInstallsDevDependencies(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("Getting current working directory failed: %v", err)
|
||||
}
|
||||
pwd = filepath.Dir(pwd)
|
||||
|
||||
// using custom createTmpDir function to avoid issues with symlinks on Docker for Mac
|
||||
tempDir, err := createTmpDir("")
|
||||
defer os.RemoveAll(tempDir) // clean up
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error when creating temp dir: %v", err)
|
||||
}
|
||||
|
||||
err = copyDir(filepath.Join(pwd, "integration", "testdata", "TestMtaIntegration", "npm-install-dev-dependencies"), tempDir)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to copy test project.")
|
||||
}
|
||||
|
||||
//workaround to use test script util it is possible to set workdir for Exec call
|
||||
testScript := `#!/bin/sh
|
||||
cd /test
|
||||
apt-get -yqq update; apt-get -yqq install make
|
||||
curl -OL https://github.com/SAP/cloud-mta-build-tool/releases/download/v1.0.14/cloud-mta-build-tool_1.0.14_Linux_amd64.tar.gz
|
||||
tar xzf cloud-mta-build-tool_1.0.14_Linux_amd64.tar.gz
|
||||
mv mbt /usr/bin
|
||||
/piperbin/piper mtaBuild --installArtifacts >test-log.txt 2>&1
|
||||
`
|
||||
ioutil.WriteFile(filepath.Join(tempDir, "runPiper.sh"), []byte(testScript), 0700)
|
||||
|
||||
reqNode := testcontainers.ContainerRequest{
|
||||
Image: "node:12",
|
||||
Cmd: []string{"tail", "-f"},
|
||||
|
||||
BindMounts: map[string]string{
|
||||
pwd: "/piperbin",
|
||||
tempDir: "/test",
|
||||
container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{
|
||||
Image: "node:12",
|
||||
User: "root",
|
||||
TestDir: []string{"testdata", "TestMtaIntegration", "npm-install-dev-dependencies"},
|
||||
Mounts: map[string]string{},
|
||||
Setup: []string{
|
||||
"apt-get -yqq update; apt-get -yqq install make",
|
||||
"curl -OL https://github.com/SAP/cloud-mta-build-tool/releases/download/v1.0.14/cloud-mta-build-tool_1.0.14_Linux_amd64.tar.gz",
|
||||
"tar xzf cloud-mta-build-tool_1.0.14_Linux_amd64.tar.gz",
|
||||
"mv mbt /usr/bin",
|
||||
},
|
||||
}
|
||||
|
||||
mbtContainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||
ContainerRequest: reqNode,
|
||||
Started: true,
|
||||
})
|
||||
|
||||
code, err := mbtContainer.Exec(ctx, []string{"sh", "/test/runPiper.sh"})
|
||||
|
||||
err := container.whenRunningPiperCommand("mtaBuild", "--installArtifacts")
|
||||
if err != nil {
|
||||
t.Fatalf("Script returened error: %v", err)
|
||||
t.Fatalf("Piper command failed %s", err)
|
||||
}
|
||||
assert.Equal(t, 0, code)
|
||||
|
||||
content, err := ioutil.ReadFile(filepath.Join(tempDir, "/test-log.txt"))
|
||||
if err != nil {
|
||||
t.Fatal("Could not read test-log.txt.", err)
|
||||
}
|
||||
output := string(content)
|
||||
assert.Contains(t, output, "added 2 packages in")
|
||||
container.assertHasOutput(t, "added 2 packages in")
|
||||
}
|
||||
|
6
integration/piper-command-wrapper.sh
Normal file
6
integration/piper-command-wrapper.sh
Normal file
@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
# The purpose of this script is to run the binary inside a test container and to ensure its output is stored for assertions
|
||||
# This is not very elegant, but I have so far not found a better way to save output of a command run via "docker exec"
|
||||
|
||||
"$@" >/tmp/test-log.txt 2>&1
|
Loading…
x
Reference in New Issue
Block a user