cmd: add new build command
This commit adds the `build` command. It takes the same flags as `manifest` and will build the specified image.
This commit is contained in:
parent
ce8dd45821
commit
e5b3ccd6ed
5 changed files with 193 additions and 16 deletions
24
cmd/image-builder/build.go
Normal file
24
cmd/image-builder/build.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/osbuild/images/pkg/imagefilter"
|
||||
"github.com/osbuild/images/pkg/osbuild"
|
||||
)
|
||||
|
||||
func buildImage(res *imagefilter.Result, osbuildManifest []byte) error {
|
||||
osbuildStoreDir := ".store"
|
||||
// XXX: support output dir via commandline
|
||||
// XXX2: support output filename via commandline (c.f.
|
||||
// https://github.com/osbuild/images/pull/1039)
|
||||
outputDir := "."
|
||||
buildName := fmt.Sprintf("%s-%s-%s", res.Distro.Name(), res.ImgType.Name(), res.Arch.Name())
|
||||
jobOutputDir := filepath.Join(outputDir, buildName)
|
||||
|
||||
// XXX: support stremaing via images/pkg/osbuild/monitor.go
|
||||
_, err := osbuild.RunOSBuild(osbuildManifest, osbuildStoreDir, jobOutputDir, res.ImgType.Exports(), nil, nil, false, osStderr)
|
||||
return err
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
|
@ -9,9 +10,9 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/osbuild/images/pkg/arch"
|
||||
"github.com/osbuild/images/pkg/imagefilter"
|
||||
|
||||
"github.com/osbuild/image-builder-cli/internal/blueprintload"
|
||||
"github.com/osbuild/image-builder-cli/internal/manifestgen"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -36,21 +37,21 @@ func cmdListImages(cmd *cobra.Command, args []string) error {
|
|||
return listImages(dataDir, output, filter)
|
||||
}
|
||||
|
||||
func cmdManifest(cmd *cobra.Command, args []string) error {
|
||||
func cmdManifestWrapper(cmd *cobra.Command, args []string, w io.Writer, archChecker func(string) error) (*imagefilter.Result, error) {
|
||||
dataDir, err := cmd.Flags().GetString("datadir")
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
archStr, err := cmd.Flags().GetString("arch")
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if archStr == "" {
|
||||
archStr = arch.Current().String()
|
||||
}
|
||||
distroStr, err := cmd.Flags().GetString("distro")
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var blueprintPath string
|
||||
|
|
@ -60,30 +61,47 @@ func cmdManifest(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
bp, err := blueprintload.Load(blueprintPath)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
distroStr, err = findDistro(distroStr, bp.Distro)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := getOneImage(dataDir, distroStr, imgTypeStr, archStr)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
repos, err := newRepoRegistry(dataDir)
|
||||
if err != nil {
|
||||
return err
|
||||
if archChecker != nil {
|
||||
if err := archChecker(res.Arch.Name()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// XXX: add --rpmmd/cachedir option like bib
|
||||
mg, err := manifestgen.New(repos, &manifestgen.Options{
|
||||
Output: osStdout,
|
||||
|
||||
err = generateManifest(dataDir, blueprintPath, res, w)
|
||||
return res, err
|
||||
}
|
||||
|
||||
func cmdManifest(cmd *cobra.Command, args []string) error {
|
||||
_, err := cmdManifestWrapper(cmd, args, osStdout, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func cmdBuild(cmd *cobra.Command, args []string) error {
|
||||
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() {
|
||||
return fmt.Errorf("cannot build for arch %q from %q", archStr, arch.Current().String())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return mg.Generate(bp, res.Distro, res.ImgType, res.Arch, nil)
|
||||
return buildImage(res, mf.Bytes())
|
||||
}
|
||||
|
||||
func run() error {
|
||||
|
|
@ -128,6 +146,16 @@ operating sytsems like centos and RHEL with easy customizations support.`,
|
|||
manifestCmd.Flags().String("distro", "", `build manifest for a different distroname (e.g. centos-9)`)
|
||||
rootCmd.AddCommand(manifestCmd)
|
||||
|
||||
buildCmd := &cobra.Command{
|
||||
Use: "build <image-type> [blueprint]",
|
||||
Short: "Build the given distro/image-type, e.g. centos-9 qcow2",
|
||||
RunE: cmdBuild,
|
||||
SilenceUsage: true,
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
}
|
||||
buildCmd.Flags().AddFlagSet(manifestCmd.Flags())
|
||||
rootCmd.AddCommand(buildCmd)
|
||||
|
||||
return rootCmd.Execute()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
|
@ -14,6 +15,7 @@ import (
|
|||
|
||||
"github.com/osbuild/image-builder-cli/cmd/image-builder"
|
||||
"github.com/osbuild/image-builder-cli/internal/manifesttest"
|
||||
"github.com/osbuild/image-builder-cli/internal/testutil"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
@ -225,3 +227,87 @@ func TestManifestIntegrationCrossArch(t *testing.T) {
|
|||
// XXX: provide helpers in manifesttest to extract this in a nicer way
|
||||
assert.Contains(t, fakeStdout.String(), `.el9.s390x.rpm`)
|
||||
}
|
||||
|
||||
func TestBuildIntegrationHappy(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("manifest generation takes a while")
|
||||
}
|
||||
if !hasDepsolveDnf() {
|
||||
t.Skip("no osbuild-depsolve-dnf binary found")
|
||||
}
|
||||
|
||||
restore := main.MockNewRepoRegistry(testrepos.New)
|
||||
defer restore()
|
||||
|
||||
restore = main.MockOsArgs([]string{
|
||||
"build",
|
||||
"qcow2",
|
||||
makeTestBlueprint(t, testBlueprint),
|
||||
"--distro", "centos-9",
|
||||
})
|
||||
defer restore()
|
||||
|
||||
script := `cat - > "$0".stdin`
|
||||
fakeOsbuildCmd := testutil.MockCommand(t, "osbuild", script)
|
||||
defer fakeOsbuildCmd.Restore()
|
||||
|
||||
err := main.Run()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// ensure osbuild was run exactly one
|
||||
assert.Equal(t, 1, len(fakeOsbuildCmd.Calls()))
|
||||
// and we passed the output dir
|
||||
osbuildCall := fakeOsbuildCmd.Calls()[0]
|
||||
outputDirPos := slices.Index(osbuildCall, "--output-directory")
|
||||
assert.True(t, outputDirPos > -1)
|
||||
assert.Equal(t, "centos-9-qcow2-x86_64", osbuildCall[outputDirPos+1])
|
||||
|
||||
// ... and that the manifest passed to osbuild
|
||||
manifest, err := os.ReadFile(fakeOsbuildCmd.Path() + ".stdin")
|
||||
assert.NoError(t, err)
|
||||
// XXX: provide helpers in manifesttest to extract this in a nicer way
|
||||
assert.Contains(t, string(manifest), `{"type":"org.osbuild.users","options":{"users":{"alice":{}}}}`)
|
||||
assert.Contains(t, string(manifest), `"image":{"name":"registry.gitlab.com/redhat/services/products/image-builder/ci/osbuild-composer/fedora-minimal"`)
|
||||
}
|
||||
|
||||
func TestBuildIntegrationErrors(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("manifest generation takes a while")
|
||||
}
|
||||
if !hasDepsolveDnf() {
|
||||
t.Skip("no osbuild-depsolve-dnf binary found")
|
||||
}
|
||||
|
||||
restore := main.MockNewRepoRegistry(testrepos.New)
|
||||
defer restore()
|
||||
|
||||
var fakeStdout, fakeStderr bytes.Buffer
|
||||
restore = main.MockOsStdout(&fakeStdout)
|
||||
defer restore()
|
||||
restore = main.MockOsStderr(&fakeStderr)
|
||||
defer restore()
|
||||
|
||||
restore = main.MockOsArgs([]string{
|
||||
"build",
|
||||
"qcow2",
|
||||
makeTestBlueprint(t, testBlueprint),
|
||||
"--distro", "centos-9",
|
||||
})
|
||||
defer restore()
|
||||
|
||||
script := `
|
||||
cat - > "$0".stdin
|
||||
>&2 echo "error on stderr"
|
||||
exit 1
|
||||
`
|
||||
fakeOsbuildCmd := testutil.MockCommand(t, "osbuild", script)
|
||||
defer fakeOsbuildCmd.Restore()
|
||||
|
||||
err := main.Run()
|
||||
assert.EqualError(t, err, "running osbuild failed: exit status 1")
|
||||
// ensure errors from osbuild are passed to the user
|
||||
// XXX: once the osbuild.Status is used, also check that stdout
|
||||
// is available (but that cannot be done with the existing
|
||||
// osbuild-exec.go)
|
||||
assert.Equal(t, "error on stderr\n", fakeStderr.String())
|
||||
}
|
||||
|
|
|
|||
30
cmd/image-builder/manifest.go
Normal file
30
cmd/image-builder/manifest.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/osbuild/images/pkg/imagefilter"
|
||||
|
||||
"github.com/osbuild/image-builder-cli/internal/blueprintload"
|
||||
"github.com/osbuild/image-builder-cli/internal/manifestgen"
|
||||
)
|
||||
|
||||
func generateManifest(dataDir, blueprintPath string, res *imagefilter.Result, output io.Writer) error {
|
||||
repos, err := newRepoRegistry(dataDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// XXX: add --rpmmd/cachedir option like bib
|
||||
mg, err := manifestgen.New(repos, &manifestgen.Options{
|
||||
Output: output,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bp, err := blueprintload.Load(blueprintPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return mg.Generate(bp, res.Distro, res.ImgType, res.Arch, nil)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue