Blame cmd/osbuild-tests/main_test.go

Packit 63bb0d
// +build integration
Packit 63bb0d
Packit 63bb0d
package main
Packit 63bb0d
Packit 63bb0d
import (
Packit 63bb0d
	"bytes"
Packit 63bb0d
	"encoding/json"
Packit 63bb0d
	"io/ioutil"
Packit 63bb0d
	"log"
Packit 63bb0d
	"os"
Packit 63bb0d
	"os/exec"
Packit 63bb0d
	"time"
Packit 63bb0d
Packit 63bb0d
	"github.com/BurntSushi/toml"
Packit 63bb0d
	"github.com/google/uuid"
Packit 63bb0d
	"github.com/stretchr/testify/assert"
Packit 63bb0d
	"github.com/stretchr/testify/require"
Packit 63bb0d
Packit 63bb0d
	"github.com/osbuild/osbuild-composer/internal/blueprint"
Packit 63bb0d
	"github.com/osbuild/osbuild-composer/internal/weldr"
Packit 63bb0d
Packit 63bb0d
	"testing"
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
func TestComposeCommands(t *testing.T) {
Packit 63bb0d
	// common setup
Packit 63bb0d
	tmpdir := NewTemporaryWorkDir(t, "osbuild-tests-")
Packit 63bb0d
	defer tmpdir.Close(t)
Packit 63bb0d
Packit 63bb0d
	bp := blueprint.Blueprint{
Packit 63bb0d
		Name:        "empty",
Packit 63bb0d
		Description: "Test blueprint in toml format",
Packit 63bb0d
		Packages: []blueprint.Package{{Name: "bash", Version: "*"}},
Packit 63bb0d
	}
Packit 63bb0d
	pushBlueprint(t, &bp)
Packit 63bb0d
	defer deleteBlueprint(t, &bp)
Packit 63bb0d
Packit 63bb0d
	runComposer(t, "blueprints", "save", "empty")
Packit 63bb0d
	_, err := os.Stat("empty.toml")
Packit 63bb0d
	require.NoError(t, err, "Error accessing 'empty.toml: %v'", err)
Packit 63bb0d
Packit 63bb0d
	runComposer(t, "compose", "types")
Packit 63bb0d
	runComposer(t, "compose", "status")
Packit 63bb0d
	runComposer(t, "compose", "list")
Packit 63bb0d
	runComposer(t, "compose", "list", "waiting")
Packit 63bb0d
	runComposer(t, "compose", "list", "running")
Packit 63bb0d
	runComposer(t, "compose", "list", "finished")
Packit 63bb0d
	runComposer(t, "compose", "list", "failed")
Packit 63bb0d
Packit 63bb0d
	// Full integration tests
Packit 63bb0d
	uuid := buildCompose(t, "empty", "qcow2")
Packit 63bb0d
	defer deleteCompose(t, uuid)
Packit 63bb0d
Packit 63bb0d
	runComposer(t, "compose", "info", uuid.String())
Packit 63bb0d
Packit 63bb0d
	runComposer(t, "compose", "metadata", uuid.String())
Packit 63bb0d
	_, err = os.Stat(uuid.String() + "-metadata.tar")
Packit 63bb0d
	require.NoError(t, err, "'%s-metadata.tar' not found", uuid.String())
Packit 63bb0d
	defer os.Remove(uuid.String() + "-metadata.tar")
Packit 63bb0d
Packit 63bb0d
	runComposer(t, "compose", "results", uuid.String())
Packit 63bb0d
	_, err = os.Stat(uuid.String() + ".tar")
Packit 63bb0d
	require.NoError(t, err, "'%s.tar' not found", uuid.String())
Packit 63bb0d
	defer os.Remove(uuid.String() + ".tar")
Packit 63bb0d
Packit 63bb0d
	// Just assert that result wasn't empty
Packit 63bb0d
	result := runComposer(t, "compose", "log", uuid.String())
Packit 63bb0d
	require.NotNil(t, result)
Packit 63bb0d
	result = runComposer(t, "compose", "log", uuid.String(), "1024")
Packit 63bb0d
	require.NotNil(t, result)
Packit 63bb0d
Packit 63bb0d
	runComposer(t, "compose", "logs", uuid.String())
Packit 63bb0d
	_, err = os.Stat(uuid.String() + "-logs.tar")
Packit 63bb0d
	require.NoError(t, err, "'%s-logs.tar' not found", uuid.String())
Packit 63bb0d
	defer os.Remove(uuid.String() + "-logs.tar")
Packit 63bb0d
Packit 63bb0d
	runComposer(t, "compose", "image", uuid.String())
Packit 63bb0d
	_, err = os.Stat(uuid.String() + "-disk.qcow2")
Packit 63bb0d
	require.NoError(t, err, "'%s-disk.qcow2' not found", uuid.String())
Packit 63bb0d
	defer os.Remove(uuid.String() + "-disk.qcow2")
Packit 63bb0d
Packit 63bb0d
	// workers ask the composer every 15 seconds if a compose was canceled.
Packit 63bb0d
	// Use 20 seconds here to be sure this is hit.
Packit 63bb0d
	uuid = startCompose(t, "empty", "qcow2")
Packit 63bb0d
	defer deleteCompose(t, uuid)
Packit 63bb0d
	runComposer(t, "compose", "cancel", uuid.String())
Packit 63bb0d
	time.Sleep(20 * time.Second)
Packit 63bb0d
	status := waitForCompose(t, uuid)
Packit 63bb0d
	assert.Equal(t, "FAILED", status)
Packit 63bb0d
Packit 63bb0d
	// Check that reusing the cache works ok
Packit 63bb0d
	uuid = buildCompose(t, "empty", "qcow2")
Packit 63bb0d
	defer deleteCompose(t, uuid)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func TestBlueprintCommands(t *testing.T) {
Packit 63bb0d
	// common setup
Packit 63bb0d
	tmpdir := NewTemporaryWorkDir(t, "osbuild-tests-")
Packit 63bb0d
	defer tmpdir.Close(t)
Packit 63bb0d
Packit 63bb0d
	bp := blueprint.Blueprint{
Packit 63bb0d
		Name:        "empty",
Packit 63bb0d
		Description: "Test empty blueprint in toml format",
Packit 63bb0d
	}
Packit 63bb0d
	pushBlueprint(t, &bp)
Packit 63bb0d
	defer deleteBlueprint(t, &bp)
Packit 63bb0d
Packit 63bb0d
	runComposer(t, "blueprints", "list")
Packit 63bb0d
	runComposer(t, "blueprints", "show", "empty")
Packit 63bb0d
Packit 63bb0d
	runComposer(t, "blueprints", "changes", "empty")
Packit 63bb0d
	runComposer(t, "blueprints", "diff", "empty", "NEWEST", "WORKSPACE")
Packit 63bb0d
Packit 63bb0d
	runComposer(t, "blueprints", "save", "empty")
Packit 63bb0d
	_, err := os.Stat("empty.toml")
Packit 63bb0d
	require.NoError(t, err, "'empty.toml' not found")
Packit 63bb0d
	defer os.Remove("empty.toml")
Packit 63bb0d
Packit 63bb0d
	runComposer(t, "blueprints", "depsolve", "empty")
Packit 63bb0d
	runComposer(t, "blueprints", "freeze", "empty")
Packit 63bb0d
	runComposer(t, "blueprints", "freeze", "show", "empty")
Packit 63bb0d
	runComposer(t, "blueprints", "freeze", "save", "empty")
Packit 63bb0d
	_, err = os.Stat("empty.frozen.toml")
Packit 63bb0d
	require.NoError(t, err, "'empty.frozen.toml' not found")
Packit 63bb0d
	defer os.Remove("empty.frozen.toml")
Packit 63bb0d
Packit 63bb0d
	runComposer(t, "blueprints", "tag", "empty")
Packit 63bb0d
Packit 63bb0d
	// undo the latest commit we can find
Packit 63bb0d
	var changes weldr.BlueprintsChangesV0
Packit 63bb0d
	rawReply := runComposerJSON(t, "blueprints", "changes", "empty")
Packit 63bb0d
	err = json.Unmarshal(rawReply, &changes)
Packit 63bb0d
	require.Nilf(t, err, "Error searching for commits to undo: %v", err)
Packit 63bb0d
	runComposer(t, "blueprints", "undo", "empty", changes.BlueprintsChanges[0].Changes[0].Commit)
Packit 63bb0d
Packit 63bb0d
	runComposer(t, "blueprints", "workspace", "empty")
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func TestModulesCommands(t *testing.T) {
Packit 63bb0d
	runComposer(t, "modules", "list")
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func TestProjectsCommands(t *testing.T) {
Packit 63bb0d
	runComposer(t, "projects", "list")
Packit 63bb0d
	runComposer(t, "projects", "info", "filesystem")
Packit 63bb0d
	runComposer(t, "projects", "info", "filesystem", "kernel")
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func TestStatusCommands(t *testing.T) {
Packit 63bb0d
	runComposer(t, "status", "show")
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func TestSourcesCommands(t *testing.T) {
Packit 63bb0d
	sources_toml, err := ioutil.TempFile("", "SOURCES-*.TOML")
Packit 63bb0d
	require.NoErrorf(t, err, "Could not create temporary file: %v", err)
Packit 63bb0d
	defer os.Remove(sources_toml.Name())
Packit 63bb0d
Packit 63bb0d
	_, err = sources_toml.Write([]byte(`id = "osbuild-test-addon-source"
Packit 63bb0d
name = "Testing sources add command"
Packit 63bb0d
url = "file://REPO-PATH"
Packit 63bb0d
type = "yum-baseurl"
Packit 63bb0d
proxy = "https://proxy-url/"
Packit 63bb0d
check_ssl = true
Packit 63bb0d
check_gpg = true
Packit 63bb0d
gpgkey_urls = ["https://url/path/to/gpg-key"]
Packit 63bb0d
`))
Packit 63bb0d
	require.NoError(t, err)
Packit 63bb0d
Packit 63bb0d
	runComposer(t, "sources", "list")
Packit 63bb0d
	runComposer(t, "sources", "add", sources_toml.Name())
Packit 63bb0d
	runComposer(t, "sources", "info", "osbuild-test-addon-source")
Packit 63bb0d
	runComposer(t, "sources", "change", sources_toml.Name())
Packit 63bb0d
	runComposer(t, "sources", "delete", "osbuild-test-addon-source")
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func buildCompose(t *testing.T, bpName string, outputType string) uuid.UUID {
Packit 63bb0d
	uuid := startCompose(t, bpName, outputType)
Packit 63bb0d
	status := waitForCompose(t, uuid)
Packit 63bb0d
	logs := getLogs(t, uuid)
Packit 63bb0d
	assert.NotEmpty(t, logs, "logs are empty after the build is finished/failed")
Packit 63bb0d
Packit 63bb0d
	if !assert.Equalf(t, "FINISHED", status, "Unexpected compose result: %s", status) {
Packit 63bb0d
		log.Print("logs from the build: ", logs)
Packit 63bb0d
		t.FailNow()
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return uuid
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func startCompose(t *testing.T, name, outputType string) uuid.UUID {
Packit 63bb0d
	var reply struct {
Packit 63bb0d
		BuildID uuid.UUID `json:"build_id"`
Packit 63bb0d
		Status  bool      `json:"status"`
Packit 63bb0d
	}
Packit 63bb0d
	rawReply := runComposerJSON(t, "compose", "start", name, outputType)
Packit 63bb0d
	err := json.Unmarshal(rawReply, &reply)
Packit 63bb0d
	require.Nilf(t, err, "Unexpected reply: %v", err)
Packit 63bb0d
	require.Truef(t, reply.Status, "Unexpected status %v", reply.Status)
Packit 63bb0d
Packit 63bb0d
	return reply.BuildID
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func deleteCompose(t *testing.T, id uuid.UUID) {
Packit 63bb0d
	type deleteUUID struct {
Packit 63bb0d
		ID     uuid.UUID `json:"uuid"`
Packit 63bb0d
		Status bool      `json:"status"`
Packit 63bb0d
	}
Packit 63bb0d
	var reply struct {
Packit 63bb0d
		IDs    []deleteUUID  `json:"uuids"`
Packit 63bb0d
		Errors []interface{} `json:"errors"`
Packit 63bb0d
	}
Packit 63bb0d
	rawReply := runComposerJSON(t, "compose", "delete", id.String())
Packit 63bb0d
	err := json.Unmarshal(rawReply, &reply)
Packit 63bb0d
	require.Nilf(t, err, "Unexpected reply: %v", err)
Packit 63bb0d
	require.Zerof(t, len(reply.Errors), "Unexpected errors")
Packit 63bb0d
	require.Equalf(t, 1, len(reply.IDs), "Unexpected number of UUIDs returned: %d", len(reply.IDs))
Packit 63bb0d
	require.Truef(t, reply.IDs[0].Status, "Unexpected status %v", reply.IDs[0].Status)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func waitForCompose(t *testing.T, uuid uuid.UUID) string {
Packit 63bb0d
	for {
Packit 63bb0d
		status := getComposeStatus(t, uuid)
Packit 63bb0d
		if status == "FINISHED" || status == "FAILED" {
Packit 63bb0d
			return status
Packit 63bb0d
		}
Packit 63bb0d
		time.Sleep(time.Second)
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func getComposeStatus(t *testing.T, uuid uuid.UUID) string {
Packit 63bb0d
	var reply struct {
Packit 63bb0d
		QueueStatus string `json:"queue_status"`
Packit 63bb0d
	}
Packit 63bb0d
	rawReply := runComposerJSON(t, "compose", "info", uuid.String())
Packit 63bb0d
	err := json.Unmarshal(rawReply, &reply)
Packit 63bb0d
	require.Nilf(t, err, "Unexpected reply: %v", err)
Packit 63bb0d
Packit 63bb0d
	return reply.QueueStatus
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func getLogs(t *testing.T, uuid uuid.UUID) string {
Packit 63bb0d
	cmd := exec.Command("composer-cli", "compose", "log", uuid.String())
Packit 63bb0d
	cmd.Stderr = os.Stderr
Packit 63bb0d
	stdoutReader, err := cmd.StdoutPipe()
Packit 63bb0d
	require.NoError(t, err)
Packit 63bb0d
Packit 63bb0d
	err = cmd.Start()
Packit 63bb0d
	require.NoError(t, err)
Packit 63bb0d
Packit 63bb0d
	var buffer bytes.Buffer
Packit 63bb0d
	_, err = buffer.ReadFrom(stdoutReader)
Packit 63bb0d
	require.NoError(t, err)
Packit 63bb0d
Packit 63bb0d
	err = cmd.Wait()
Packit 63bb0d
	require.NoError(t, err)
Packit 63bb0d
Packit 63bb0d
	return buffer.String()
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func pushBlueprint(t *testing.T, bp *blueprint.Blueprint) {
Packit 63bb0d
	tmpfile, err := ioutil.TempFile("", "osbuild-test-")
Packit 63bb0d
	require.Nilf(t, err, "Could not create temporary file: %v", err)
Packit 63bb0d
	defer os.Remove(tmpfile.Name())
Packit 63bb0d
Packit 63bb0d
	err = toml.NewEncoder(tmpfile).Encode(bp)
Packit 63bb0d
	require.Nilf(t, err, "Could not marshal blueprint TOML: %v", err)
Packit 63bb0d
	err = tmpfile.Close()
Packit 63bb0d
	require.Nilf(t, err, "Could not close toml file: %v", err)
Packit 63bb0d
Packit 63bb0d
	var reply struct {
Packit 63bb0d
		Status bool `json:"status"`
Packit 63bb0d
	}
Packit 63bb0d
	rawReply := runComposerJSON(t, "blueprints", "push", tmpfile.Name())
Packit 63bb0d
	err = json.Unmarshal(rawReply, &reply)
Packit 63bb0d
	require.Nilf(t, err, "Unexpected reply: %v", err)
Packit 63bb0d
	require.Truef(t, reply.Status, "Unexpected status %v", reply.Status)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func deleteBlueprint(t *testing.T, bp *blueprint.Blueprint) {
Packit 63bb0d
	var reply struct {
Packit 63bb0d
		Status bool `json:"status"`
Packit 63bb0d
	}
Packit 63bb0d
	rawReply := runComposerJSON(t, "blueprints", "delete", bp.Name)
Packit 63bb0d
	err := json.Unmarshal(rawReply, &reply)
Packit 63bb0d
	require.Nilf(t, err, "Unexpected reply: %v", err)
Packit 63bb0d
	require.Truef(t, reply.Status, "Unexpected status %v", reply.Status)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func runComposer(t *testing.T, command ...string) []byte {
Packit 63bb0d
	cmd := exec.Command("composer-cli", command...)
Packit 63bb0d
	stdout, err := cmd.StdoutPipe()
Packit 63bb0d
	require.Nilf(t, err, "Could not create command: %v", err)
Packit 63bb0d
Packit 63bb0d
	err = cmd.Start()
Packit 63bb0d
	require.Nilf(t, err, "Could not start command: %v", err)
Packit 63bb0d
Packit 63bb0d
	contents, err := ioutil.ReadAll(stdout)
Packit 63bb0d
	require.NoError(t, err, "Could not read stdout from command")
Packit 63bb0d
Packit 63bb0d
	err = cmd.Wait()
Packit 63bb0d
	require.NoErrorf(t, err, "Command failed: %v", err)
Packit 63bb0d
Packit 63bb0d
	return contents
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func runComposerJSON(t *testing.T, command ...string) json.RawMessage {
Packit 63bb0d
	command = append([]string{"--json"}, command...)
Packit 63bb0d
	contents := runComposer(t, command...)
Packit 63bb0d
Packit 63bb0d
	var result json.RawMessage
Packit 63bb0d
Packit 63bb0d
	if len(contents) != 0 {
Packit 63bb0d
		err := json.Unmarshal(contents, &result)
Packit 63bb0d
		if err != nil {
Packit 63bb0d
			// We did not get JSON, try interpreting it as TOML
Packit 63bb0d
			var data interface{}
Packit 63bb0d
			err = toml.Unmarshal(contents, &data)
Packit 63bb0d
			require.Nilf(t, err, "Could not parse output: %v", err)
Packit 63bb0d
			buffer := bytes.Buffer{}
Packit 63bb0d
			err = json.NewEncoder(&buffer).Encode(data)
Packit 63bb0d
			require.Nilf(t, err, "Could not remarshal TOML to JSON: %v", err)
Packit 63bb0d
			err = json.NewDecoder(&buffer).Decode(&result)
Packit 63bb0d
			require.Nilf(t, err, "Could not decode the remarshalled JSON: %v", err)
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	buffer := bytes.Buffer{}
Packit 63bb0d
	encoder := json.NewEncoder(&buffer)
Packit 63bb0d
	encoder.SetIndent("", "  ")
Packit 63bb0d
	err := encoder.Encode(result)
Packit 63bb0d
	require.Nilf(t, err, "Could not remarshal the recevied JSON: %v", err)
Packit 63bb0d
Packit 63bb0d
	return result
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
type TemporaryWorkDir struct {
Packit 63bb0d
	OldWorkDir string
Packit 63bb0d
	Path       string
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Creates a new temporary directory based on pattern and changes the current
Packit 63bb0d
// working directory to it.
Packit 63bb0d
//
Packit 63bb0d
// Example:
Packit 63bb0d
//   d := NewTemporaryWorkDir(t, "foo-*")
Packit 63bb0d
//   defer d.Close(t)
Packit 63bb0d
func NewTemporaryWorkDir(t *testing.T, pattern string) TemporaryWorkDir {
Packit 63bb0d
	var d TemporaryWorkDir
Packit 63bb0d
	var err error
Packit 63bb0d
Packit 63bb0d
	d.OldWorkDir, err = os.Getwd()
Packit 63bb0d
	require.Nilf(t, err, "os.GetWd: %v", err)
Packit 63bb0d
Packit 63bb0d
	d.Path, err = ioutil.TempDir("", pattern)
Packit 63bb0d
	require.Nilf(t, err, "ioutil.TempDir: %v", err)
Packit 63bb0d
Packit 63bb0d
	err = os.Chdir(d.Path)
Packit 63bb0d
	require.Nilf(t, err, "os.ChDir: %v", err)
Packit 63bb0d
Packit 63bb0d
	return d
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Change back to the previous working directory and removes the temporary one.
Packit 63bb0d
func (d *TemporaryWorkDir) Close(t *testing.T) {
Packit 63bb0d
	var err error
Packit 63bb0d
Packit 63bb0d
	err = os.Chdir(d.OldWorkDir)
Packit 63bb0d
	require.Nilf(t, err, "os.ChDir: %v", err)
Packit 63bb0d
Packit 63bb0d
	err = os.RemoveAll(d.Path)
Packit 63bb0d
	require.Nilf(t, err, "os.RemoveAll: %v", err)
Packit 63bb0d
}