Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

misc: add Containerfile #11

Merged
merged 6 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/workflows/container.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Build & Deploy Container

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
merge_group:
types: [ "checks_requested" ]

jobs:
build-base:
name: Build & push image
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Login to Github Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Generate container image metadata
uses: docker/metadata-action@v4
id: meta
with:
images: ghcr.io/osbuild/image-builder-cli
tags: |
type=ref,event=tag,enable=${{ startsWith(github.ref, 'refs/tags/') }}
type=raw,value=latest
type=sha,format=long,enable=${{ github.ref == 'refs/heads/main' }}

- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
platforms: linux/amd64
file: ./Containerfile
push: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }}
cache-from: type=registry,ref=ghcr.io/osbuild/image-builder-cli:latest
cache-to: type=inline
tags: ${{ steps.meta.outputs.tags }}
8 changes: 1 addition & 7 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ jobs:

build:
runs-on: ubuntu-latest
uses: ./.github/workflows/testdeps.yml
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍

steps:
- uses: actions/checkout@v4

Expand All @@ -21,13 +22,6 @@ jobs:
with:
go-version: 'stable'

- name: Apt update
run: sudo apt update

# This is needed for the container resolver dependencies
- name: Install test dependencies
run: sudo apt install -y libgpgme-dev libbtrfs-dev libdevmapper-dev podman

- name: Build
run: go build -v ./...

Expand Down
24 changes: 24 additions & 0 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Integration tests

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
build:
runs-on: ubuntu-latest
uses: ./.github/workflows/testdeps.yml
steps:
- uses: actions/checkout@v4

- name: Install integration test env
run: |
sudo apt update
sudo apt install -y pytest golang

- name: Run integration tests via pytest
run: |
# use "-s" for now for easier debugging
sudo pytest -s -v
13 changes: 13 additions & 0 deletions .github/workflows/testdeps.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: Install common test dependencies

on:
workflow_call:

jobs:
install:
runs-on: ubuntu-latest
steps:
- name: Install common test dependencies
run: |
sudo apt update
sudo apt install -y libgpgme-dev libbtrfs-dev libdevmapper-dev podman
59 changes: 59 additions & 0 deletions Containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
FROM registry.fedoraproject.org/fedora:41 AS builder
RUN dnf install -y git-core golang gpgme-devel libassuan-devel && mkdir -p /build/
ARG GOPROXY=https://proxy.golang.org,direct
RUN go env -w GOPROXY=$GOPROXY
COPY . /build
WORKDIR /build
# keep in sync with:
# https://github.com/containers/podman/blob/2981262215f563461d449b9841741339f4d9a894/Makefile#L51
RUN go build -tags "containers_image_openpgp exclude_graphdriver_btrfs exclude_graphdriver_devicemapper" ./cmd/image-builder

FROM registry.fedoraproject.org/fedora:41

# Fast-track osbuild so we don't depend on the "slow" Fedora release process to implement new features in bib
RUN dnf install -y dnf-plugins-core \
&& dnf copr enable -y @osbuild/osbuild \
&& dnf install -y libxcrypt-compat wget osbuild osbuild-ostree osbuild-depsolve-dnf osbuild-lvm2 \
&& dnf clean all

COPY --from=builder /build/image-builder /usr/bin/

# install repo data from osbuild-composer in an ugly way
# XXX: find a better way
RUN <<EOR
mkdir -p /usr/share/osbuild-composer/repositories
cd /usr/share/osbuild-composer/repositories
# XXX: find a better way to organize the upstream supported repos
# we cannot just checkout the osbuild-composer repo here as it contains a bunch
# of "*-no-aux-key.json" files too, so a naive "cp *.json" will not work.
# Ideally we split the supported repos out of osbuild-composer into
# either "images" (as it generates the code for supported distro it could
# be the place that also defines what is supported) or into its own repo.
# Bonus points if we could make them go:embedable :)
for fname in centos-stream-9 centos-stream-10 \
fedora-40 fedora-41 \
rhel-8 rhel-8.1 rhel-8.2 rhel-8.3 rhel-8.4 rhel-8.5 rhel-8.6 \
rhel-8.7 rhel-8.8 rhel-8.9 \
rhel-9.0 rhel-9.1 rhel-9.2 rhel-9.3 rhel-9.4 rhel-9.5 rhel-9.6 \
rhel-10.0; do
# XXX: if only we had 'go:embed'able repos :(
wget https://raw.githubusercontent.com/osbuild/osbuild-composer/refs/heads/main/repositories/${fname}.json
done
# XXX: find an even better way here, those are symlinks in the upstream repo
cp -a centos-stream-9.json centos-9.json
cp -a centos-stream-10.json centos-10.json
EOR

COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
VOLUME /output
WORKDIR /output
# XXX: add "store" flag like bib
VOLUME /store
VOLUME /var/lib/containers/storage

LABEL description="This tools allows to build and deploy disk-images."
LABEL io.k8s.description="This tools allows to build and deploy disk-images."
LABEL io.k8s.display-name="Image Builder"
LABEL io.openshift.tags="base fedora40"
LABEL summary="A container to create disk-images."
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

Build images from the commandline in a convenient way.

## Run via container

```console
$ sudo podman run --privileged \
-v ./output:/output \
ghcr.io/osbuild/image-builder-cli:latest \
build \
--distro fedora-41 \
minimal-raw
```

## Installation

This project is under development right now and needs to be run via:
Expand Down
3 changes: 1 addition & 2 deletions cmd/image-builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import (
"github.com/osbuild/images/pkg/osbuild"
)

func buildImage(res *imagefilter.Result, osbuildManifest []byte) error {
osbuildStoreDir := ".store"
func buildImage(res *imagefilter.Result, osbuildManifest []byte, osbuildStoreDir string) error {
// XXX: support output dir via commandline
// XXX2: support output filename via commandline (c.f.
// https://github.com/osbuild/images/pull/1039)
Expand Down
9 changes: 7 additions & 2 deletions cmd/image-builder/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,12 @@ func cmdManifest(cmd *cobra.Command, args []string) error {
}

func cmdBuild(cmd *cobra.Command, args []string) error {
var mf bytes.Buffer
storeDir, err := cmd.Flags().GetString("store")
if err != nil {
return err
}

var mf bytes.Buffer
// XXX: check env here, i.e. if user is root and osbuild is installed
res, err := cmdManifestWrapper(cmd, args, &mf, func(archStr string) error {
if archStr != arch.Current().String() {
Expand All @@ -101,7 +105,7 @@ func cmdBuild(cmd *cobra.Command, args []string) error {
return err
}

return buildImage(res, mf.Bytes())
return buildImage(res, mf.Bytes(), storeDir)
}

func run() error {
Expand Down Expand Up @@ -154,6 +158,7 @@ operating sytsems like centos and RHEL with easy customizations support.`,
Args: cobra.RangeArgs(1, 2),
}
buildCmd.Flags().AddFlagSet(manifestCmd.Flags())
buildCmd.Flags().String("store", ".store", `osbuild store directory to cache intermediata build artifacts"`)
rootCmd.AddCommand(buildCmd)

return rootCmd.Execute()
Expand Down
8 changes: 7 additions & 1 deletion cmd/image-builder/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,11 +239,13 @@ func TestBuildIntegrationHappy(t *testing.T) {
restore := main.MockNewRepoRegistry(testrepos.New)
defer restore()

tmpdir := t.TempDir()
restore = main.MockOsArgs([]string{
"build",
"qcow2",
makeTestBlueprint(t, testBlueprint),
"--distro", "centos-9",
"--store", tmpdir,
})
defer restore()

Expand All @@ -256,8 +258,12 @@ func TestBuildIntegrationHappy(t *testing.T) {

// ensure osbuild was run exactly one
assert.Equal(t, 1, len(fakeOsbuildCmd.Calls()))
// and we passed the output dir
osbuildCall := fakeOsbuildCmd.Calls()[0]
// --store is passed correctly to osbuild
storePos := slices.Index(osbuildCall, "--store")
assert.True(t, storePos > -1)
assert.Equal(t, tmpdir, osbuildCall[storePos+1])
// and we passed the output dir
outputDirPos := slices.Index(osbuildCall, "--output-directory")
assert.True(t, outputDirPos > -1)
assert.Equal(t, "centos-9-qcow2-x86_64", osbuildCall[outputDirPos+1])
Expand Down
22 changes: 22 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/sh

set -e

# TODO: share code with bib to do the setup automatically
# see https://github.com/teamsbc/container-for-osbuild/blob/main/entrypoint.bash (thanks simon)
# and https://github.com/osbuild/bootc-image-builder/blob/main/bib/internal/setup/setup.go#L21 (thanks ondrej,achilleas,colin)
mkdir /run/osbuild
mkdir /run/osbuild-store

mount -t tmpfs tmpfs /run/osbuild

cp -p /usr/bin/osbuild /run/osbuild/osbuild

chcon system_u:object_r:install_exec_t:s0 /run/osbuild/osbuild

mount -t devtmpfs devtmpfs /dev
mount --bind /run/osbuild/osbuild /usr/bin/osbuild

# XXX: make this nicer
cd /output
/usr/bin/image-builder --store=/store "$@"
9 changes: 9 additions & 0 deletions internal/manifestgen/manifestgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ import (
// into a common helper in "images" or images should do this on its
// own
func defaultDepsolver(cacheDir string, packageSets map[string][]rpmmd.PackageSet, d distro.Distro, arch string) (map[string][]rpmmd.PackageSpec, map[string][]rpmmd.RepoConfig, error) {
if cacheDir == "" {
var err error
cacheDir, err = os.MkdirTemp("", "manifestgen")
if err != nil {
return nil, nil, fmt.Errorf("cannot create temporary directory: %w", err)
}
defer os.RemoveAll(cacheDir)
}

solver := dnfjson.NewSolver(d.ModulePlatformID(), d.Releasever(), arch, d.Name(), cacheDir)
depsolvedSets := make(map[string][]rpmmd.PackageSpec)
repoSets := make(map[string][]rpmmd.RepoConfig)
Expand Down
23 changes: 23 additions & 0 deletions test/containerbuild.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import os
import platform
import random
import string
import subprocess
import textwrap
from contextlib import contextmanager

import pytest


# XXX: copied from bib
@pytest.fixture(name="build_container", scope="session")
def build_container_fixture():
"""Build a container from the Containerfile and returns the name"""

container_tag = "image-builder-cli-test"
subprocess.check_call([
"podman", "build",
"-f", "Containerfile",
"-t", container_tag,
])
return container_tag
27 changes: 27 additions & 0 deletions test/test_container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import os
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you be willing to use Go for integration tests? Is there any reason to actually use Python?

I can tell you why I prefer Go:

  • developers don't need to switch their language mindset when writing integration tests
  • we don't need an extra linter and formatter
  • sharing code between integration tests and the SUT
  • pytest fixtures are cool... until they start being overwhelming. I think I have a preference for the lightweight nature of Go.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I will give this a "go" (see what I did here ;)

import subprocess

import pytest

from containerbuild import build_container_fixture


@pytest.mark.skipif(os.getuid() != 0, reason="needs root")
def test_container_builds_image(tmp_path, build_container):
output_dir = tmp_path / "output"
output_dir.mkdir()
subprocess.check_call([
"podman", "run",
"--privileged",
"-v", f"{output_dir}:/output",
build_container,
"build",
"minimal-raw",
"--distro", "fedora-41"
])
arch = "x86_64"
assert (output_dir / f"fedora-41-minimal-raw-{arch}/xz/disk.raw.xz").exists()
# XXX: ensure no other leftover dirs
dents = os.listdir(output_dir)
assert len(dents) == 1, f"too many dentries in output dir: {dents}"

Loading