Skip to content

Commit

Permalink
bib: implemnt bootc-image-builder manifest
Browse files Browse the repository at this point in the history
Now that we have a `bootc-image-builder build` verb we can have
a `manifest` one as well that just generates the manifest but
does not do the full building. This is mostly useful for testing
so it's not exposed by default in the entrypoint (for now).
  • Loading branch information
mvo5 authored and achilleas-k committed Jan 18, 2024
1 parent 220a974 commit 1b3b653
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 54 deletions.
87 changes: 56 additions & 31 deletions bib/cmd/bootc-image-builder/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,22 +155,49 @@ func saveManifest(ms manifest.OSBuildManifest, fpath string) error {
return nil
}

func build(cmd *cobra.Command, args []string) {
err := setup.Validate()
check(err)
err = setup.EnsureEnvironment()
check(err)

func manifestFromCobra(cmd *cobra.Command, args []string) ([]byte, error) {
hostArch := arch.Current()
repos := loadRepos(hostArch.String())

imgref := args[0]
outputDir, _ := cmd.Flags().GetString("output")
osbuildStore, _ := cmd.Flags().GetString("store")
rpmCacheRoot, _ := cmd.Flags().GetString("rpmmd")
configFile, _ := cmd.Flags().GetString("config")
imgType, _ := cmd.Flags().GetString("type")
tlsVerify, _ := cmd.Flags().GetBool("tls-verify")
imgType, _ := cmd.Flags().GetString("type")

config := BuildConfig{}
if configFile != "" {
config = loadConfig(configFile)
}

manifestConfig := &ManifestConfig{
Imgref: imgref,
ImgType: imgType,
Config: &config,
Repos: repos,
Architecture: hostArch,
TLSVerify: tlsVerify,
}
return makeManifest(manifestConfig, rpmCacheRoot)
}

func cmdManifest(cmd *cobra.Command, args []string) {
mf, err := manifestFromCobra(cmd, args)
if err != nil {
panic(err)
}
fmt.Print(string(mf))
}

func cmdBuild(cmd *cobra.Command, args []string) {
outputDir, _ := cmd.Flags().GetString("output")
osbuildStore, _ := cmd.Flags().GetString("store")
imgType, _ := cmd.Flags().GetString("type")

err := setup.Validate()
check(err)
err = setup.EnsureEnvironment()
check(err)

if err := os.MkdirAll(outputDir, 0777); err != nil {
fail(fmt.Sprintf("failed to create target directory: %s", err.Error()))
Expand All @@ -195,10 +222,13 @@ func build(cmd *cobra.Command, args []string) {
canChown, err := canChownInPath(outputDir)
check(err)

config := BuildConfig{}
if configFile != "" {
config = loadConfig(configFile)
manifest_fname := fmt.Sprintf("manifest-%s.json", imgType)
fmt.Printf("Generating %s ... ", manifest_fname)
mf, err := manifestFromCobra(cmd, args)
if err != nil {
panic(err)
}
fmt.Print("DONE\n")

var exports []string
switch imgType {
Expand All @@ -212,20 +242,6 @@ func build(cmd *cobra.Command, args []string) {
fail(fmt.Sprintf("valid types are 'qcow2', 'ami', 'raw', 'iso', not: '%s'", imgType))
}

manifest_fname := fmt.Sprintf("manifest-%s.json", imgType)
fmt.Printf("Generating %s ... ", manifest_fname)
manifestConfig := &ManifestConfig{
Imgref: imgref,
ImgType: imgType,
Config: &config,
Repos: repos,
Architecture: hostArch,
TLSVerify: tlsVerify,
}
mf, err := makeManifest(manifestConfig, rpmCacheRoot)
check(err)
fmt.Print("DONE\n")

manifestPath := filepath.Join(outputDir, manifest_fname)
check(saveManifest(mf, manifestPath))

Expand Down Expand Up @@ -265,17 +281,26 @@ func main() {
Long: rootCmd.Long,
Args: cobra.ExactArgs(1),
DisableFlagsInUseLine: true,
Run: build,
Run: cmdBuild,
}
rootCmd.AddCommand(buildCmd)
manifestCmd := &cobra.Command{
Use: "manifest",
Long: rootCmd.Long,
Args: cobra.ExactArgs(1),
DisableFlagsInUseLine: true,
Run: cmdManifest,
}
rootCmd.AddCommand(manifestCmd)
manifestCmd.Flags().String("rpmmd", "/var/cache/osbuild/rpmmd", "rpm metadata cache directory")
manifestCmd.Flags().String("config", "", "build config file")
manifestCmd.Flags().String("type", "qcow2", "image type to build [qcow2, ami]")
manifestCmd.Flags().Bool("tls-verify", true, "require HTTPS and verify certificates when contacting registries")

logrus.SetLevel(logrus.ErrorLevel)
buildCmd.Flags().AddFlagSet(manifestCmd.Flags())
buildCmd.Flags().String("output", ".", "artifact output directory")
buildCmd.Flags().String("store", ".osbuild", "osbuild store for intermediate pipeline trees")
buildCmd.Flags().String("rpmmd", "/var/cache/osbuild/rpmmd", "rpm metadata cache directory")
buildCmd.Flags().String("config", "", "build config file")
buildCmd.Flags().String("type", "qcow2", "image type to build [qcow2, ami, iso]")
buildCmd.Flags().Bool("tls-verify", true, "require HTTPS and verify certificates when contacting registries")
buildCmd.Flags().String("aws-region", "", "target region for AWS uploads (only for type=ami)")
buildCmd.Flags().String("aws-bucket", "", "target S3 bucket name for intermediate storage when creating AMI (only for type=ami)")
buildCmd.Flags().String("aws-ami-name", "", "name for the AMI in AWS (only for type=ami)")
Expand Down
28 changes: 28 additions & 0 deletions test/containerbuild.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import os
import subprocess

import pytest


@pytest.fixture(name="build_container", scope="session")
def build_container_fixture():
"""Build a container from the Containerfile and returns the name"""
if tag_from_env := os.getenv("BIB_TEST_BUILD_CONTAINER_TAG"):
return tag_from_env

container_tag = "bootc-image-builder-test"
subprocess.check_call([
"podman", "build",
"-f", "Containerfile",
"-t", container_tag,
])
return container_tag


def container_to_build_ref():
# TODO: make this another indirect fixture input, e.g. by making
# making "image_type" an "image" tuple (type, container_ref_to_test)
return os.getenv(
"BIB_TEST_BOOTC_CONTAINER_TAG",
"quay.io/centos-bootc/fedora-bootc:eln",
)
25 changes: 2 additions & 23 deletions test/test_smoke.py → test/test_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

# local test utils
import testutil
from containerbuild import build_container_fixture, container_to_build_ref # noqa: F401
from vm import AWS, QEMU

if not testutil.has_executable("podman"):
Expand All @@ -26,21 +27,6 @@
pytest.skip("need x86_64-v3 capable CPU", allow_module_level=True)


@pytest.fixture(name="build_container", scope="session")
def build_container_fixture():
"""Build a container from the Containerfile and returns the name"""
if tag_from_env := os.getenv("BIB_TEST_BUILD_CONTAINER_TAG"):
return tag_from_env

container_tag = "bootc-image-builder-test"
subprocess.check_call([
"podman", "build",
"-f", "Containerfile",
"-t", container_tag,
])
return container_tag


# image types to test
SUPPORTED_IMAGE_TYPES = ["qcow2", "ami", "raw"]

Expand All @@ -60,13 +46,6 @@ def image_type_fixture(tmpdir_factory, build_container, request, force_aws_uploa
Build an image inside the passed build_container and return an
ImageBuildResult with the resulting image path and user/password
"""
# TODO: make this another indirect fixture input, e.g. by making
# making "image_type" an "image" tuple (type, container_ref_to_test)
container_to_build_ref = os.getenv(
"BIB_TEST_BOOTC_CONTAINER_TAG",
"quay.io/centos-bootc/fedora-bootc:eln",
)

# image_type is passed via special pytest parameter fixture
image_type = request.param

Expand Down Expand Up @@ -139,7 +118,7 @@ def image_type_fixture(tmpdir_factory, build_container, request, force_aws_uploa
"-v", "/store", # share the cache between builds
*creds_args,
build_container,
container_to_build_ref,
container_to_build_ref(),
"--config", "/output/config.json",
"--type", image_type,
*upload_args,
Expand Down
24 changes: 24 additions & 0 deletions test/test_manifest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import json
import subprocess

import pytest

import testutil


if not testutil.has_executable("podman"):
pytest.skip("no podman, skipping integration tests that required podman", allow_module_level=True)

from containerbuild import build_container_fixture, container_to_build_ref # noqa: F401


def test_manifest_smoke(build_container):
output = subprocess.check_output([
"podman", "run", "--rm",
f'--entrypoint=["/usr/bin/bootc-image-builder", "manifest", "{container_to_build_ref()}"]',
build_container,
])
manifest = json.loads(output)
# just some basic validation
assert manifest["version"] == "2"
assert manifest["pipelines"][0]["name"] == "build"

0 comments on commit 1b3b653

Please sign in to comment.