osbuild-playground: rework slightly
Invoke osbuild, rather than output the manifest. Make it easier to include several image types.
This commit is contained in:
parent
1b924ae30c
commit
33fe2da25c
3 changed files with 163 additions and 75 deletions
|
|
@ -10,12 +10,25 @@ import (
|
|||
"path"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/distroregistry"
|
||||
"github.com/osbuild/osbuild-composer/internal/dnfjson"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
var ImageTypes = make(map[string]ImageType)
|
||||
|
||||
type ImageType interface {
|
||||
Name() string
|
||||
InstantiateManifest(m *manifest.Manifest, repos []rpmmd.RepoConfig, runner string) error
|
||||
GetExports() []string
|
||||
GetCheckpoints() []string
|
||||
}
|
||||
|
||||
func AddImageType(img ImageType) {
|
||||
ImageTypes[img.Name()] = img
|
||||
}
|
||||
|
||||
// osbuild-playground is a utility command and is often run from within the
|
||||
// source tree. Find the dnf-json binary in case the osbuild-composer package
|
||||
// isn't installed. This prioritises the local source version over the system
|
||||
|
|
@ -34,17 +47,25 @@ func findDnfJsonBin() string {
|
|||
}
|
||||
|
||||
func main() {
|
||||
// Path to MyOptions or '-' for stdin
|
||||
myOptionsArg := flag.Arg(0)
|
||||
var distroArg string
|
||||
flag.StringVar(&distroArg, "distro", "", "distro to build from")
|
||||
var archArg string
|
||||
flag.StringVar(&archArg, "arch", common.CurrentArch(), "architecture to build for")
|
||||
var imageTypeArg string
|
||||
flag.StringVar(&imageTypeArg, "type", "my-image", "image type to build")
|
||||
flag.Parse()
|
||||
|
||||
myOptions := &MyOptions{}
|
||||
if myOptionsArg != "" {
|
||||
// Path to options or '-' for stdin
|
||||
optionsArg := flag.Arg(0)
|
||||
|
||||
img := ImageTypes[imageTypeArg]
|
||||
if optionsArg != "" {
|
||||
var reader io.Reader
|
||||
if myOptionsArg == "-" {
|
||||
if optionsArg == "-" {
|
||||
reader = os.Stdin
|
||||
} else {
|
||||
var err error
|
||||
reader, err = os.Open(myOptionsArg)
|
||||
reader, err = os.Open(optionsArg)
|
||||
if err != nil {
|
||||
panic("Could not open path to image options: " + err.Error())
|
||||
}
|
||||
|
|
@ -53,21 +74,29 @@ func main() {
|
|||
if err != nil {
|
||||
panic("Could not read image options: " + err.Error())
|
||||
}
|
||||
err = json.Unmarshal(file, &myOptions)
|
||||
err = json.Unmarshal(file, img)
|
||||
if err != nil {
|
||||
panic("Could not parse image options: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
distros := distroregistry.NewDefault()
|
||||
d := distros.FromHost()
|
||||
if d == nil {
|
||||
panic("host distro not supported")
|
||||
var d distro.Distro
|
||||
if distroArg != "" {
|
||||
d = distros.GetDistro(distroArg)
|
||||
if d == nil {
|
||||
panic(fmt.Sprintf("distro '%s' not supported\n", distroArg))
|
||||
}
|
||||
} else {
|
||||
d = distros.FromHost()
|
||||
if d == nil {
|
||||
panic("host distro not supported")
|
||||
}
|
||||
}
|
||||
|
||||
arch, err := d.GetArch(common.CurrentArch())
|
||||
arch, err := d.GetArch(archArg)
|
||||
if err != nil {
|
||||
panic("host arch not supported")
|
||||
panic(fmt.Sprintf("arch '%s' not supported\n", archArg))
|
||||
}
|
||||
|
||||
repos, err := rpmmd.LoadRepositories([]string{"./"}, d.Name())
|
||||
|
|
@ -80,37 +109,7 @@ func main() {
|
|||
panic("os.UserHomeDir(): " + err.Error())
|
||||
}
|
||||
|
||||
solver := dnfjson.NewSolver(d.ModulePlatformID(), d.Releasever(), arch.Name(), path.Join(home, ".cache/osbuild-playground/rpmmd"))
|
||||
solver.SetDNFJSONPath(findDnfJsonBin())
|
||||
state_dir := path.Join(home, ".local/share/osbuild-playground/")
|
||||
|
||||
// Set cache size to 3 GiB
|
||||
solver.SetMaxCacheSize(1 * 1024 * 1024 * 1024)
|
||||
|
||||
manifest := manifest.New()
|
||||
|
||||
// TODO: figure out the runner situation
|
||||
err = MyManifest(&manifest, myOptions, repos[arch.Name()], "org.osbuild.fedora36")
|
||||
if err != nil {
|
||||
panic("MyManifest() failed: " + err.Error())
|
||||
}
|
||||
|
||||
packageSpecs := make(map[string][]rpmmd.PackageSpec)
|
||||
for name, chain := range manifest.GetPackageSetChains() {
|
||||
packages, err := solver.Depsolve(chain)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to depsolve for pipeline %s: %s\n", name, err.Error()))
|
||||
}
|
||||
packageSpecs[name] = packages
|
||||
}
|
||||
|
||||
bytes, err := manifest.Serialize(packageSpecs)
|
||||
if err != nil {
|
||||
panic("failed to serialize manifest: " + err.Error())
|
||||
}
|
||||
|
||||
os.Stdout.Write(bytes)
|
||||
if err := solver.CleanCache(); err != nil {
|
||||
// print to stderr but don't exit with error
|
||||
fmt.Fprintf(os.Stderr, "could not clean dnf cache: %s", err.Error())
|
||||
}
|
||||
RunPlayground(img, d, arch, repos, state_dir)
|
||||
}
|
||||
|
|
|
|||
77
cmd/osbuild-playground/my-image.go
Normal file
77
cmd/osbuild-playground/my-image.go
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
func init() {
|
||||
AddImageType(&MyImage{})
|
||||
}
|
||||
|
||||
// MyImage contains the arguments passed in as a JSON blob.
|
||||
// You can replace them with whatever you want to use to
|
||||
// configure your image. In the current example they are
|
||||
// unused.
|
||||
type MyImage struct {
|
||||
MyOption string `json:"my_option"`
|
||||
Filename string `json:"filename"`
|
||||
}
|
||||
|
||||
// Name returns the name of the image type, used to select what kind
|
||||
// of image to build.
|
||||
func (img *MyImage) Name() string {
|
||||
return "my-image"
|
||||
}
|
||||
|
||||
// Build your manifest by attaching pipelines to it
|
||||
//
|
||||
// @m is the manifest you are constructing
|
||||
// @options are what was passed in on the commandline
|
||||
// @repos are the default repositories for the host OS/arch
|
||||
// @runner is needed by any build pipelines
|
||||
//
|
||||
// Return nil when you are done, or an error if something
|
||||
// went wrong. Your manifest will be streamed to osbuild
|
||||
// for building.
|
||||
func (img *MyImage) InstantiateManifest(m *manifest.Manifest, repos []rpmmd.RepoConfig, runner string) error {
|
||||
// Let's create a simple OCI container!
|
||||
|
||||
// configure a build pipeline
|
||||
build := manifest.NewBuildPipeline(m, runner, repos)
|
||||
|
||||
// create a non-bootable OS tree containing the `core` comps group
|
||||
os := manifest.NewOSPipeline(m, build, &platform.X86{}, repos)
|
||||
os.ExtraBasePackages = []string{
|
||||
"@core",
|
||||
}
|
||||
|
||||
filename := "my-container.tar"
|
||||
if img.Filename != "" {
|
||||
filename = img.Filename
|
||||
}
|
||||
// create an OCI container containing the OS tree created above
|
||||
manifest.NewOCIContainerPipeline(m, build, &os.BasePipeline, "x86_64", filename)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetExports returns a list of the pipelines osbuild should export.
|
||||
// These are the pipelines containing the artefact you want returned.
|
||||
//
|
||||
// TODO: Move this to be implemented in terms ofthe Manifest package.
|
||||
// We should not need to know the pipeline names.
|
||||
func (img *MyImage) GetExports() []string {
|
||||
return []string{"container"}
|
||||
}
|
||||
|
||||
// GetCheckpoints returns a list of the pipelines osbuild should
|
||||
// checkpoint. These are the pipelines likely to be reusable in
|
||||
// future runs.
|
||||
//
|
||||
// TODO: Move this to be implemented in terms ofthe Manifest package.
|
||||
// We should not need to know the pipeline names.
|
||||
func (img *MyImage) GetCheckpoints() []string {
|
||||
return []string{"build"}
|
||||
}
|
||||
|
|
@ -1,44 +1,56 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/dnfjson"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild2"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
// MyOptions contains the arguments passed in as a JSON blob.
|
||||
// You can replace them with whatever you want to use to
|
||||
// configure your image. In the current example they are
|
||||
// unused.
|
||||
type MyOptions struct {
|
||||
MyOption string `json:"my_option"`
|
||||
}
|
||||
func RunPlayground(img ImageType, d distro.Distro, arch distro.Arch, repos map[string][]rpmmd.RepoConfig, state_dir string) {
|
||||
|
||||
// Build your manifest by attaching pipelines to it
|
||||
//
|
||||
// @m is the manifest you are constructing
|
||||
// @options are what was passed in on the commandline
|
||||
// @repos are the default repositories for the host OS/arch
|
||||
// @runner is needed by any build pipelines
|
||||
//
|
||||
// Return nil when you are done, or an error if something
|
||||
// went wrong. Your manifest will be streamed to stdout and
|
||||
// can be piped directly to either jq for inspection or
|
||||
// osbuild for building.
|
||||
func MyManifest(m *manifest.Manifest, options *MyOptions, repos []rpmmd.RepoConfig, runner string) error {
|
||||
// Let's create a simple OCI container!
|
||||
solver := dnfjson.NewSolver(d.ModulePlatformID(), d.Releasever(), arch.Name(), path.Join(state_dir, "rpmmd"))
|
||||
solver.SetDNFJSONPath(findDnfJsonBin())
|
||||
|
||||
// configure a build pipeline
|
||||
build := manifest.NewBuildPipeline(m, runner, repos)
|
||||
// Set cache size to 3 GiB
|
||||
solver.SetMaxCacheSize(1 * 1024 * 1024 * 1024)
|
||||
|
||||
// create a non-bootable OS tree containing the `core` comps group
|
||||
os := manifest.NewOSPipeline(m, build, &platform.X86{}, repos)
|
||||
os.ExtraBasePackages = []string{
|
||||
"@core",
|
||||
manifest := manifest.New()
|
||||
|
||||
// TODO: figure out the runner situation
|
||||
err := img.InstantiateManifest(&manifest, repos[arch.Name()], "org.osbuild.fedora36")
|
||||
if err != nil {
|
||||
panic("InstantiateManifest() failed: " + err.Error())
|
||||
}
|
||||
|
||||
// create an OCI container containing the OS tree created above
|
||||
manifest.NewOCIContainerPipeline(m, build, &os.BasePipeline, "x86_64", "my-container.tar")
|
||||
packageSpecs := make(map[string][]rpmmd.PackageSpec)
|
||||
for name, chain := range manifest.GetPackageSetChains() {
|
||||
packages, err := solver.Depsolve(chain)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to depsolve for pipeline %s: %s\n", name, err.Error()))
|
||||
}
|
||||
packageSpecs[name] = packages
|
||||
}
|
||||
|
||||
return nil
|
||||
if err := solver.CleanCache(); err != nil {
|
||||
// print to stderr but don't exit with error
|
||||
fmt.Fprintf(os.Stderr, "could not clean dnf cache: %s", err.Error())
|
||||
}
|
||||
|
||||
bytes, err := manifest.Serialize(packageSpecs)
|
||||
if err != nil {
|
||||
panic("failed to serialize manifest: " + err.Error())
|
||||
}
|
||||
|
||||
store := path.Join(state_dir, "osbuild-store")
|
||||
|
||||
_, err = osbuild2.RunOSBuild(bytes, store, "./", img.GetExports(), []string{"build"}, false, os.Stdout)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "could not run osbuild: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue