|
Packit |
63bb0d |
// +build integration
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
package main
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
import (
|
|
Packit |
63bb0d |
"bytes"
|
|
Packit |
63bb0d |
"context"
|
|
Packit |
63bb0d |
"encoding/json"
|
|
Packit |
63bb0d |
"flag"
|
|
Packit |
63bb0d |
"fmt"
|
|
Packit |
63bb0d |
"io"
|
|
Packit |
63bb0d |
"io/ioutil"
|
|
Packit |
63bb0d |
"log"
|
|
Packit |
63bb0d |
"os"
|
|
Packit |
63bb0d |
"os/exec"
|
|
Packit |
63bb0d |
"path"
|
|
Packit Service |
15f37d |
"path/filepath"
|
|
Packit |
63bb0d |
"strings"
|
|
Packit |
63bb0d |
"testing"
|
|
Packit |
63bb0d |
"time"
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
"github.com/gophercloud/gophercloud"
|
|
Packit |
63bb0d |
"github.com/gophercloud/gophercloud/openstack"
|
|
Packit |
63bb0d |
"github.com/stretchr/testify/assert"
|
|
Packit |
63bb0d |
"github.com/stretchr/testify/require"
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
"github.com/osbuild/osbuild-composer/cmd/osbuild-image-tests/constants"
|
|
Packit Service |
509fd4 |
"github.com/osbuild/osbuild-composer/internal/boot"
|
|
Packit Service |
509fd4 |
"github.com/osbuild/osbuild-composer/internal/boot/azuretest"
|
|
Packit Service |
509fd4 |
"github.com/osbuild/osbuild-composer/internal/boot/openstacktest"
|
|
Packit Service |
509fd4 |
"github.com/osbuild/osbuild-composer/internal/boot/vmwaretest"
|
|
Packit |
63bb0d |
"github.com/osbuild/osbuild-composer/internal/common"
|
|
Packit Service |
509fd4 |
"github.com/osbuild/osbuild-composer/internal/test"
|
|
Packit Service |
509fd4 |
"github.com/osbuild/osbuild-composer/internal/upload/vmware"
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
type testcaseStruct struct {
|
|
Packit |
63bb0d |
ComposeRequest struct {
|
|
Packit |
63bb0d |
Distro string
|
|
Packit |
63bb0d |
Arch string
|
|
Packit |
63bb0d |
Filename string
|
|
Packit |
63bb0d |
} `json:"compose-request"`
|
|
Packit |
63bb0d |
Manifest json.RawMessage
|
|
Packit |
63bb0d |
ImageInfo json.RawMessage `json:"image-info"`
|
|
Packit |
63bb0d |
Boot *struct {
|
|
Packit |
63bb0d |
Type string
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
var disableLocalBoot = flag.Bool("disable-local-boot", false, "when this flag is given, no images are booted locally using qemu (this does not affect testing in clouds)")
|
|
Packit Service |
509fd4 |
var failLocalBoot = flag.Bool("fail-local-boot", true, "when this flag is on (default), local boot will fail. Usually indicates missing cloud credentials")
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// runOsbuild runs osbuild with the specified manifest and output-directory.
|
|
Packit Service |
15f37d |
func runOsbuild(manifest []byte, store, outputDirectory string, exports []string) error {
|
|
Packit Service |
15f37d |
cmd := constants.GetOsbuildCommand(store, outputDirectory, exports)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
cmd.Stdin = bytes.NewReader(manifest)
|
|
Packit |
63bb0d |
var outBuffer bytes.Buffer
|
|
Packit |
63bb0d |
cmd.Stdout = &outBuffer
|
|
Packit |
63bb0d |
cmd.Stderr = &outBuffer
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
err := cmd.Run()
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
// Pretty print the osbuild error output.
|
|
Packit |
63bb0d |
buf := new(bytes.Buffer)
|
|
Packit |
63bb0d |
_ = json.Indent(buf, outBuffer.Bytes(), "", " ")
|
|
Packit |
63bb0d |
fmt.Println(buf)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return fmt.Errorf("running osbuild failed: %v", err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return nil
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// testImageInfo runs image-info on image specified by imageImage and
|
|
Packit |
63bb0d |
// compares the result with expected image info
|
|
Packit |
63bb0d |
func testImageInfo(t *testing.T, imagePath string, rawImageInfoExpected []byte) {
|
|
Packit |
63bb0d |
var imageInfoExpected interface{}
|
|
Packit |
63bb0d |
err := json.Unmarshal(rawImageInfoExpected, &imageInfoExpected)
|
|
Packit Service |
509fd4 |
require.NoErrorf(t, err, "cannot decode expected image info: %v", err)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
cmd := constants.GetImageInfoCommand(imagePath)
|
|
Packit |
63bb0d |
cmd.Stderr = os.Stderr
|
|
Packit |
63bb0d |
reader, writer := io.Pipe()
|
|
Packit |
63bb0d |
cmd.Stdout = writer
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
err = cmd.Start()
|
|
Packit Service |
509fd4 |
require.NoErrorf(t, err, "image-info cannot start: %v", err)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
var imageInfoGot interface{}
|
|
Packit |
63bb0d |
err = json.NewDecoder(reader).Decode(&imageInfoGot)
|
|
Packit Service |
509fd4 |
require.NoErrorf(t, err, "decoding image-info output failed: %v", err)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
err = cmd.Wait()
|
|
Packit Service |
509fd4 |
require.NoErrorf(t, err, "running image-info failed: %v", err)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
assert.Equal(t, imageInfoExpected, imageInfoGot)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
type timeoutError struct{}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func (*timeoutError) Error() string { return "" }
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// trySSHOnce tries to test the running image using ssh once
|
|
Packit |
63bb0d |
// It returns timeoutError if ssh command returns 255, if it runs for more
|
|
Packit |
63bb0d |
// that 10 seconds or if systemd-is-running returns starting.
|
|
Packit |
63bb0d |
// It returns nil if systemd-is-running returns running or degraded.
|
|
Packit |
63bb0d |
// It can also return other errors in other error cases.
|
|
Packit Service |
509fd4 |
func trySSHOnce(address string, privateKey string, ns *boot.NetNS) error {
|
|
Packit |
63bb0d |
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
Packit |
63bb0d |
defer cancel()
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
cmdName := "ssh"
|
|
Packit |
63bb0d |
cmdArgs := []string{
|
|
Packit |
63bb0d |
"-p", "22",
|
|
Packit |
63bb0d |
"-i", privateKey,
|
|
Packit |
63bb0d |
"-o", "StrictHostKeyChecking=no",
|
|
Packit |
63bb0d |
"-o", "UserKnownHostsFile=/dev/null",
|
|
Packit |
63bb0d |
"redhat@" + address,
|
|
Packit |
63bb0d |
"systemctl --wait is-system-running",
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
var cmd *exec.Cmd
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if ns != nil {
|
|
Packit |
63bb0d |
cmd = ns.NamespacedCommandContext(ctx, cmdName, cmdArgs...)
|
|
Packit |
63bb0d |
} else {
|
|
Packit |
63bb0d |
cmd = exec.CommandContext(ctx, cmdName, cmdArgs...)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
output, err := cmd.Output()
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if ctx.Err() == context.DeadlineExceeded {
|
|
Packit |
63bb0d |
return &timeoutError{}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
if exitError, ok := err.(*exec.ExitError); ok {
|
|
Packit |
63bb0d |
if exitError.ExitCode() == 255 {
|
|
Packit |
63bb0d |
return &timeoutError{}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
} else {
|
|
Packit Service |
509fd4 |
return fmt.Errorf("ssh command failed from unknown reason: %v", err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
outputString := strings.TrimSpace(string(output))
|
|
Packit |
63bb0d |
switch outputString {
|
|
Packit |
63bb0d |
case "running":
|
|
Packit |
63bb0d |
return nil
|
|
Packit |
63bb0d |
case "degraded":
|
|
Packit |
63bb0d |
log.Print("ssh test passed, but the system is degraded")
|
|
Packit |
63bb0d |
return nil
|
|
Packit |
63bb0d |
case "starting":
|
|
Packit |
63bb0d |
return &timeoutError{}
|
|
Packit |
63bb0d |
default:
|
|
Packit |
63bb0d |
return fmt.Errorf("ssh test failed, system status is: %s", outputString)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// testSSH tests the running image using ssh.
|
|
Packit |
63bb0d |
// It tries 20 attempts before giving up. If a major error occurs, it might
|
|
Packit |
63bb0d |
// return earlier.
|
|
Packit Service |
509fd4 |
func testSSH(t *testing.T, address string, privateKey string, ns *boot.NetNS) {
|
|
Packit |
63bb0d |
const attempts = 20
|
|
Packit |
63bb0d |
for i := 0; i < attempts; i++ {
|
|
Packit |
63bb0d |
err := trySSHOnce(address, privateKey, ns)
|
|
Packit |
63bb0d |
if err == nil {
|
|
Packit |
63bb0d |
// pass the test
|
|
Packit |
63bb0d |
return
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// if any other error than the timeout one happened, fail the test immediately
|
|
Packit |
63bb0d |
if _, ok := err.(*timeoutError); !ok {
|
|
Packit |
63bb0d |
t.Fatal(err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
fmt.Println(err)
|
|
Packit |
63bb0d |
time.Sleep(10 * time.Second)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
t.Errorf("ssh test failure, %d attempts were made", attempts)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func testBootUsingQemu(t *testing.T, imagePath string) {
|
|
Packit Service |
509fd4 |
if *failLocalBoot {
|
|
Packit Service |
509fd4 |
t.Fatal("-fail-local-boot specified. Check missing cloud credentials!")
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
bootWithQemu(t, imagePath)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// will not fail even if -fail-local-boot is specified
|
|
Packit Service |
509fd4 |
func bootWithQemu(t *testing.T, imagePath string) {
|
|
Packit |
63bb0d |
if *disableLocalBoot {
|
|
Packit |
63bb0d |
t.Skip("local booting was disabled by -disable-local-boot, skipping")
|
|
Packit |
63bb0d |
}
|
|
Packit Service |
509fd4 |
err := boot.WithNetworkNamespace(func(ns boot.NetNS) error {
|
|
Packit Service |
509fd4 |
return boot.WithBootedQemuImage(imagePath, ns, func() error {
|
|
Packit |
63bb0d |
testSSH(t, "localhost", constants.TestPaths.PrivateKey, &ns)
|
|
Packit |
63bb0d |
return nil
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func testBootUsingNspawnImage(t *testing.T, imagePath string) {
|
|
Packit Service |
509fd4 |
err := boot.WithNetworkNamespace(func(ns boot.NetNS) error {
|
|
Packit Service |
509fd4 |
return boot.WithBootedNspawnImage(imagePath, ns, func() error {
|
|
Packit |
63bb0d |
testSSH(t, "localhost", constants.TestPaths.PrivateKey, &ns)
|
|
Packit |
63bb0d |
return nil
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func testBootUsingNspawnDirectory(t *testing.T, imagePath string) {
|
|
Packit Service |
509fd4 |
err := boot.WithNetworkNamespace(func(ns boot.NetNS) error {
|
|
Packit Service |
509fd4 |
return boot.WithExtractedTarArchive(imagePath, func(dir string) error {
|
|
Packit Service |
509fd4 |
return boot.WithBootedNspawnDirectory(dir, ns, func() error {
|
|
Packit |
63bb0d |
testSSH(t, "localhost", constants.TestPaths.PrivateKey, &ns)
|
|
Packit |
63bb0d |
return nil
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func testBootUsingAWS(t *testing.T, imagePath string) {
|
|
Packit Service |
509fd4 |
creds, err := boot.GetAWSCredentialsFromEnv()
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// if no credentials are given, fall back to qemu
|
|
Packit |
63bb0d |
if creds == nil {
|
|
Packit |
63bb0d |
log.Print("no AWS credentials given, falling back to booting using qemu")
|
|
Packit |
63bb0d |
testBootUsingQemu(t, imagePath)
|
|
Packit |
63bb0d |
return
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
imageName, err := test.GenerateCIArtifactName("osbuild-image-tests-image-")
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
e, err := boot.NewEC2(creds)
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// the following line should be done by osbuild-composer at some point
|
|
Packit Service |
509fd4 |
err = boot.UploadImageToAWS(creds, imagePath, imageName)
|
|
Packit |
63bb0d |
require.NoErrorf(t, err, "upload to amazon failed, resources could have been leaked")
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
imageDesc, err := boot.DescribeEC2Image(e, imageName)
|
|
Packit |
63bb0d |
require.NoErrorf(t, err, "cannot describe the ec2 image")
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// delete the image after the test is over
|
|
Packit |
63bb0d |
defer func() {
|
|
Packit Service |
509fd4 |
err = boot.DeleteEC2Image(e, imageDesc)
|
|
Packit |
63bb0d |
require.NoErrorf(t, err, "cannot delete the ec2 image, resources could have been leaked")
|
|
Packit |
63bb0d |
}()
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
securityGroupName, err := test.GenerateCIArtifactName("osbuild-image-tests-security-group-")
|
|
Packit Service |
509fd4 |
require.NoError(t, err)
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
instanceTypeForArch := map[string]string{
|
|
Packit Service |
509fd4 |
"x86_64": "t3.micro",
|
|
Packit Service |
509fd4 |
"aarch64": "t4g.micro",
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
instanceType, exists := instanceTypeForArch[common.CurrentArch()]
|
|
Packit Service |
509fd4 |
if !exists {
|
|
Packit Service |
509fd4 |
panic("unsupported AWS arch")
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit |
63bb0d |
// boot the uploaded image and try to connect to it
|
|
Packit Service |
509fd4 |
err = boot.WithSSHKeyPair(func(privateKey, publicKey string) error {
|
|
Packit Service |
509fd4 |
return boot.WithBootedImageInEC2(e, securityGroupName, imageDesc, publicKey, instanceType, func(address string) error {
|
|
Packit |
63bb0d |
testSSH(t, address, privateKey, nil)
|
|
Packit |
63bb0d |
return nil
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func testBootUsingAzure(t *testing.T, imagePath string) {
|
|
Packit |
63bb0d |
creds, err := azuretest.GetAzureCredentialsFromEnv()
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// if no credentials are given, fall back to qemu
|
|
Packit |
63bb0d |
if creds == nil {
|
|
Packit |
63bb0d |
log.Print("no Azure credentials given, falling back to booting using qemu")
|
|
Packit |
63bb0d |
testBootUsingQemu(t, imagePath)
|
|
Packit |
63bb0d |
return
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// create a random test id to name all the resources used in this test
|
|
Packit Service |
509fd4 |
testId, err := test.GenerateCIArtifactName("")
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
imageName := "image-" + testId + ".vhd"
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// the following line should be done by osbuild-composer at some point
|
|
Packit |
63bb0d |
err = azuretest.UploadImageToAzure(creds, imagePath, imageName)
|
|
Packit |
63bb0d |
require.NoErrorf(t, err, "upload to azure failed, resources could have been leaked")
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// delete the image after the test is over
|
|
Packit |
63bb0d |
defer func() {
|
|
Packit |
63bb0d |
err = azuretest.DeleteImageFromAzure(creds, imageName)
|
|
Packit |
63bb0d |
require.NoErrorf(t, err, "cannot delete the azure image, resources could have been leaked")
|
|
Packit |
63bb0d |
}()
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// boot the uploaded image and try to connect to it
|
|
Packit Service |
509fd4 |
err = boot.WithSSHKeyPair(func(privateKey, publicKey string) error {
|
|
Packit |
63bb0d |
return azuretest.WithBootedImageInAzure(creds, imageName, testId, publicKey, func(address string) error {
|
|
Packit |
63bb0d |
testSSH(t, address, privateKey, nil)
|
|
Packit |
63bb0d |
return nil
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func testBootUsingOpenStack(t *testing.T, imagePath string) {
|
|
Packit |
63bb0d |
creds, err := openstack.AuthOptionsFromEnv()
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// if no credentials are given, fall back to qemu
|
|
Packit |
63bb0d |
if (creds == gophercloud.AuthOptions{}) {
|
|
Packit |
63bb0d |
log.Print("No OpenStack credentials given, falling back to booting using qemu")
|
|
Packit |
63bb0d |
testBootUsingQemu(t, imagePath)
|
|
Packit |
63bb0d |
return
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// provider is the top-level client that all OpenStack services derive from
|
|
Packit |
63bb0d |
provider, err := openstack.AuthenticatedClient(creds)
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// create a random test id to name all the resources used in this test
|
|
Packit Service |
509fd4 |
imageName, err := test.GenerateCIArtifactName("osbuild-image-tests-openstack-image-")
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// the following line should be done by osbuild-composer at some point
|
|
Packit |
63bb0d |
image, err := openstacktest.UploadImageToOpenStack(provider, imagePath, imageName)
|
|
Packit |
63bb0d |
require.NoErrorf(t, err, "Upload to OpenStack failed, resources could have been leaked")
|
|
Packit |
63bb0d |
require.NotNil(t, image)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// delete the image after the test is over
|
|
Packit |
63bb0d |
defer func() {
|
|
Packit |
63bb0d |
err = openstacktest.DeleteImageFromOpenStack(provider, image.ID)
|
|
Packit |
63bb0d |
require.NoErrorf(t, err, "Cannot delete OpenStack image, resources could have been leaked")
|
|
Packit |
63bb0d |
}()
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// boot the uploaded image and try to connect to it
|
|
Packit Service |
509fd4 |
err = boot.WithSSHKeyPair(func(privateKey, publicKey string) error {
|
|
Packit Service |
509fd4 |
userData, err := boot.CreateUserData(publicKey)
|
|
Packit |
63bb0d |
require.NoErrorf(t, err, "Creating user data failed: %v", err)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return openstacktest.WithBootedImageInOpenStack(provider, image.ID, userData, func(address string) error {
|
|
Packit |
63bb0d |
testSSH(t, address, privateKey, nil)
|
|
Packit |
63bb0d |
return nil
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func testBootUsingVMware(t *testing.T, imagePath string) {
|
|
Packit |
63bb0d |
creds, err := vmwaretest.AuthOptionsFromEnv()
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// if no credentials are given, fall back to qemu
|
|
Packit Service |
509fd4 |
if creds == nil {
|
|
Packit |
63bb0d |
log.Print("No vCenter credentials given, falling back to booting using qemu")
|
|
Packit |
63bb0d |
log.Printf("Error=%v", err)
|
|
Packit |
63bb0d |
testBootUsingQemu(t, imagePath)
|
|
Packit |
63bb0d |
return
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// convert to streamOptimized vmdk
|
|
Packit Service |
509fd4 |
imageF, err := vmware.OpenAsStreamOptimizedVmdk(imagePath)
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit Service |
509fd4 |
// we don't need the file descriptor to be opened b/c import.vmdk operates
|
|
Packit Service |
509fd4 |
// on the file path
|
|
Packit Service |
509fd4 |
imageF.Close()
|
|
Packit Service |
509fd4 |
imagePath = imageF.Name()
|
|
Packit |
63bb0d |
require.NotEqual(t, "", imagePath)
|
|
Packit |
63bb0d |
defer os.Remove(imagePath)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// create a random test id to name all the resources used in this test
|
|
Packit Service |
509fd4 |
imageName, err := test.GenerateCIArtifactName("osbuild-image-tests-vmware-image-")
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// the following line should be done by osbuild-composer at some point
|
|
Packit |
63bb0d |
err = vmwaretest.ImportImage(creds, imagePath, imageName)
|
|
Packit |
63bb0d |
require.NoErrorf(t, err, "Upload to vCenter failed, resources could have been leaked")
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// delete the image after the test is over
|
|
Packit |
63bb0d |
defer func() {
|
|
Packit |
63bb0d |
err = vmwaretest.DeleteImage(creds, imageName)
|
|
Packit |
63bb0d |
require.NoErrorf(t, err, "Cannot delete image from vCenter, resources could have been leaked")
|
|
Packit |
63bb0d |
}()
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// boot the uploaded image and try to connect to it
|
|
Packit |
63bb0d |
err = vmwaretest.WithSSHKeyPair(func(privateKey, publicKey string) error {
|
|
Packit |
63bb0d |
return vmwaretest.WithBootedImage(creds, imagePath, imageName, publicKey, func(address string) error {
|
|
Packit |
63bb0d |
testSSH(t, address, privateKey, nil)
|
|
Packit |
63bb0d |
return nil
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// testBoot tests if the image is able to successfully boot
|
|
Packit |
63bb0d |
// Before the test it boots the image respecting the specified bootType.
|
|
Packit |
63bb0d |
// The test passes if the function is able to connect to the image via ssh
|
|
Packit |
63bb0d |
// in defined number of attempts and systemd-is-running returns running
|
|
Packit |
63bb0d |
// or degraded status.
|
|
Packit |
63bb0d |
func testBoot(t *testing.T, imagePath string, bootType string) {
|
|
Packit |
63bb0d |
switch bootType {
|
|
Packit |
63bb0d |
case "qemu":
|
|
Packit Service |
509fd4 |
bootWithQemu(t, imagePath)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
case "nspawn":
|
|
Packit |
63bb0d |
testBootUsingNspawnImage(t, imagePath)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
case "nspawn-extract":
|
|
Packit |
63bb0d |
testBootUsingNspawnDirectory(t, imagePath)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
case "aws":
|
|
Packit |
63bb0d |
testBootUsingAWS(t, imagePath)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
case "azure":
|
|
Packit |
63bb0d |
testBootUsingAzure(t, imagePath)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
case "openstack":
|
|
Packit |
63bb0d |
testBootUsingOpenStack(t, imagePath)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
case "vmware":
|
|
Packit |
63bb0d |
testBootUsingVMware(t, imagePath)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
default:
|
|
Packit |
63bb0d |
panic("unknown boot type!")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// testImage performs a series of tests specified in the testcase
|
|
Packit |
63bb0d |
// on an image
|
|
Packit |
63bb0d |
func testImage(t *testing.T, testcase testcaseStruct, imagePath string) {
|
|
Packit |
63bb0d |
if testcase.ImageInfo != nil {
|
|
Packit |
63bb0d |
t.Run("image info", func(t *testing.T) {
|
|
Packit |
63bb0d |
testImageInfo(t, imagePath, testcase.ImageInfo)
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if testcase.Boot != nil {
|
|
Packit |
63bb0d |
t.Run("boot", func(t *testing.T) {
|
|
Packit |
63bb0d |
testBoot(t, imagePath, testcase.Boot.Type)
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// runTestcase builds the pipeline specified in the testcase and then it
|
|
Packit |
63bb0d |
// tests the result
|
|
Packit |
63bb0d |
func runTestcase(t *testing.T, testcase testcaseStruct, store string) {
|
|
Packit |
63bb0d |
_ = os.Mkdir("/var/lib/osbuild-composer-tests", 0755)
|
|
Packit |
63bb0d |
outputDirectory, err := ioutil.TempDir("/var/lib/osbuild-composer-tests", "osbuild-image-tests-*")
|
|
Packit |
63bb0d |
require.NoError(t, err, "error creating temporary output directory")
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
defer func() {
|
|
Packit |
63bb0d |
err := os.RemoveAll(outputDirectory)
|
|
Packit |
63bb0d |
require.NoError(t, err, "error removing temporary output directory")
|
|
Packit |
63bb0d |
}()
|
|
Packit |
63bb0d |
|
|
Packit Service |
15f37d |
// NOTE(akoutsou) 1to2t: new v2 manifests name their last pipeline
|
|
Packit Service |
15f37d |
// "assembler" for compatibility with v1
|
|
Packit Service |
15f37d |
exports := []string{"assembler"}
|
|
Packit Service |
15f37d |
err = runOsbuild(testcase.Manifest, store, outputDirectory, exports)
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
|
|
Packit Service |
15f37d |
for _, export := range exports {
|
|
Packit Service |
15f37d |
imagePath := filepath.Join(outputDirectory, export, testcase.ComposeRequest.Filename)
|
|
Packit Service |
15f37d |
testImage(t, testcase, imagePath)
|
|
Packit Service |
15f37d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// getAllCases returns paths to all testcases in the testcase directory
|
|
Packit |
63bb0d |
func getAllCases() ([]string, error) {
|
|
Packit |
63bb0d |
cases, err := ioutil.ReadDir(constants.TestPaths.TestCasesDirectory)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit Service |
509fd4 |
return nil, fmt.Errorf("cannot list test cases: %v", err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
casesPaths := []string{}
|
|
Packit |
63bb0d |
for _, c := range cases {
|
|
Packit |
63bb0d |
if c.IsDir() {
|
|
Packit |
63bb0d |
continue
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
casePath := fmt.Sprintf("%s/%s", constants.TestPaths.TestCasesDirectory, c.Name())
|
|
Packit |
63bb0d |
casesPaths = append(casesPaths, casePath)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return casesPaths, nil
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// runTests opens, parses and runs all the specified testcases
|
|
Packit |
63bb0d |
func runTests(t *testing.T, cases []string) {
|
|
Packit |
63bb0d |
_ = os.Mkdir("/var/lib/osbuild-composer-tests", 0755)
|
|
Packit |
63bb0d |
store, err := ioutil.TempDir("/var/lib/osbuild-composer-tests", "osbuild-image-tests-*")
|
|
Packit |
63bb0d |
require.NoError(t, err, "error creating temporary store")
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
defer func() {
|
|
Packit |
63bb0d |
err := os.RemoveAll(store)
|
|
Packit |
63bb0d |
require.NoError(t, err, "error removing temporary store")
|
|
Packit |
63bb0d |
}()
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
for _, p := range cases {
|
|
Packit |
63bb0d |
t.Run(path.Base(p), func(t *testing.T) {
|
|
Packit |
63bb0d |
f, err := os.Open(p)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit Service |
509fd4 |
t.Skipf("%s: cannot open test case: %v", p, err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
var testcase testcaseStruct
|
|
Packit |
63bb0d |
err = json.NewDecoder(f).Decode(&testcase)
|
|
Packit |
63bb0d |
require.NoErrorf(t, err, "%s: cannot decode test case", p)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
currentArch := common.CurrentArch()
|
|
Packit |
63bb0d |
if testcase.ComposeRequest.Arch != currentArch {
|
|
Packit |
63bb0d |
t.Skipf("the required arch is %s, the current arch is %s", testcase.ComposeRequest.Arch, currentArch)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
runTestcase(t, testcase, store)
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func TestImages(t *testing.T) {
|
|
Packit |
63bb0d |
cases := flag.Args()
|
|
Packit |
63bb0d |
// if no cases were specified, run the default set
|
|
Packit |
63bb0d |
if len(cases) == 0 {
|
|
Packit |
63bb0d |
var err error
|
|
Packit |
63bb0d |
cases, err = getAllCases()
|
|
Packit |
63bb0d |
require.NoError(t, err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
runTests(t, cases)
|
|
Packit |
63bb0d |
}
|