deps: update osbuild/images to 246b718310ea

Current main.
246b718310
This commit is contained in:
Achilleas Koutsou 2023-07-19 17:22:28 +02:00 committed by Ondřej Budai
parent 326f0cfa2f
commit 5c292c61c6
1437 changed files with 208886 additions and 87131 deletions

View file

@ -1,623 +0,0 @@
package distro_test_common
import (
"encoding/json"
"fmt"
"os"
"path"
"path/filepath"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/osbuild/images/internal/common"
"github.com/osbuild/images/internal/dnfjson"
"github.com/osbuild/images/pkg/blueprint"
"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/distro"
"github.com/osbuild/images/pkg/distroregistry"
"github.com/osbuild/images/pkg/manifest"
"github.com/osbuild/images/pkg/ostree"
"github.com/osbuild/images/pkg/rhsm/facts"
"github.com/osbuild/images/pkg/rpmmd"
)
const RandomTestSeed = 0
func TestDistro_Manifest(t *testing.T, pipelinePath string, prefix string, registry *distroregistry.Registry, depsolvePkgSets bool, dnfCacheDir, dnfJsonPath string) {
assert := assert.New(t)
fileNames, err := filepath.Glob(filepath.Join(pipelinePath, prefix))
assert.NoErrorf(err, "Could not read pipelines directory '%s': %v", pipelinePath, err)
require.Greaterf(t, len(fileNames), 0, "No pipelines found in %s for %s", pipelinePath, prefix)
for _, fileName := range fileNames {
type repository struct {
BaseURL string `json:"baseurl,omitempty"`
Metalink string `json:"metalink,omitempty"`
MirrorList string `json:"mirrorlist,omitempty"`
GPGKey string `json:"gpgkey,omitempty"`
CheckGPG bool `json:"check_gpg,omitempty"`
PackageSets []string `json:"package-sets,omitempty"`
}
type ostreeOptions struct {
Ref string `json:"ref"`
URL string `json:"url"`
Parent string `json:"parent"`
RHSM bool `json:"rhsm"`
}
type composeRequest struct {
Distro string `json:"distro"`
Arch string `json:"arch"`
ImageType string `json:"image-type"`
Repositories []repository `json:"repositories"`
Blueprint *blueprint.Blueprint `json:"blueprint"`
OSTree ostreeOptions `json:"ostree"`
}
var tt struct {
ComposeRequest *composeRequest `json:"compose-request"`
PackageSpecSets map[string][]rpmmd.PackageSpec `json:"rpmmd"`
Manifest manifest.OSBuildManifest `json:"manifest,omitempty"`
Containers map[string][]container.Spec `json:"containers,omitempty"`
OSTreeCommits map[string][]ostree.CommitSpec `json:"ostree-commits,omitempty"`
}
file, err := os.ReadFile(fileName)
assert.NoErrorf(err, "Could not read test-case '%s': %v", fileName, err)
err = json.Unmarshal([]byte(file), &tt)
assert.NoErrorf(err, "Could not parse test-case '%s': %v", fileName, err)
if tt.ComposeRequest == nil || tt.ComposeRequest.Blueprint == nil {
t.Logf("Skipping '%s'.", fileName)
continue
}
repos := make([]rpmmd.RepoConfig, len(tt.ComposeRequest.Repositories))
for i, repo := range tt.ComposeRequest.Repositories {
var urls []string
if repo.BaseURL != "" {
urls = []string{repo.BaseURL}
}
var keys []string
if repo.GPGKey != "" {
keys = []string{repo.GPGKey}
}
repos[i] = rpmmd.RepoConfig{
Name: fmt.Sprintf("repo-%d", i),
BaseURLs: urls,
Metalink: repo.Metalink,
MirrorList: repo.MirrorList,
GPGKeys: keys,
CheckGPG: common.ToPtr(repo.CheckGPG),
PackageSets: repo.PackageSets,
}
}
t.Run(path.Base(fileName), func(t *testing.T) {
require.NoError(t, err)
d := registry.GetDistro(tt.ComposeRequest.Distro)
if d == nil {
t.Errorf("unknown distro: %v", tt.ComposeRequest.Distro)
return
}
arch, err := d.GetArch(tt.ComposeRequest.Arch)
if err != nil {
t.Errorf("unknown arch: %v", tt.ComposeRequest.Arch)
return
}
imageType, err := arch.GetImageType(tt.ComposeRequest.ImageType)
if err != nil {
t.Errorf("unknown image type: %v", tt.ComposeRequest.ImageType)
return
}
ostreeOptions := &ostree.ImageOptions{
ImageRef: tt.ComposeRequest.OSTree.Ref,
ParentRef: tt.ComposeRequest.OSTree.Parent,
URL: tt.ComposeRequest.OSTree.URL,
RHSM: tt.ComposeRequest.OSTree.RHSM,
}
options := distro.ImageOptions{
Size: imageType.Size(0),
OSTree: ostreeOptions,
Facts: &facts.ImageOptions{
APIType: facts.TEST_APITYPE,
},
}
var imgPackageSpecSets map[string][]rpmmd.PackageSpec
// depsolve the image's package set to catch changes in the image's default package set.
// downside is that this takes long time
if depsolvePkgSets {
require.NotEmptyf(t, dnfCacheDir, "DNF cache directory path must be provided when chosen to depsolve image package sets")
require.NotEmptyf(t, dnfJsonPath, "path to 'dnf-json' must be provided when chosen to depsolve image package sets")
imgPackageSpecSets = getImageTypePkgSpecSets(
imageType,
*tt.ComposeRequest.Blueprint,
options,
repos,
dnfCacheDir,
dnfJsonPath,
)
} else {
imgPackageSpecSets = tt.PackageSpecSets
}
manifest, _, err := imageType.Manifest(tt.ComposeRequest.Blueprint, options, repos, RandomTestSeed)
if err != nil {
t.Errorf("distro.Manifest() error = %v", err)
return
}
got, err := manifest.Serialize(imgPackageSpecSets, tt.Containers, tt.OSTreeCommits)
if (err == nil && tt.Manifest == nil) || (err != nil && tt.Manifest != nil) {
t.Errorf("distro.Manifest() error = %v", err)
return
}
if tt.Manifest != nil {
var expected, actual interface{}
err = json.Unmarshal(tt.Manifest, &expected)
require.NoError(t, err)
err = json.Unmarshal(got, &actual)
require.NoError(t, err)
diff := cmp.Diff(expected, actual)
require.Emptyf(t, diff, "Distro: %s\nArch: %s\nImage type: %s\nTest case file: %s\n", d.Name(), arch.Name(), imageType.Name(), fileName)
}
})
}
}
func getImageTypePkgSpecSets(imageType distro.ImageType, bp blueprint.Blueprint, options distro.ImageOptions, repos []rpmmd.RepoConfig, cacheDir, dnfJsonPath string) map[string][]rpmmd.PackageSpec {
manifest, _, err := imageType.Manifest(&bp, options, repos, 0)
if err != nil {
panic("Could not generate manifest for package sets: " + err.Error())
}
imgPackageSets := manifest.GetPackageSetChains()
solver := dnfjson.NewSolver(imageType.Arch().Distro().ModulePlatformID(),
imageType.Arch().Distro().Releasever(),
imageType.Arch().Name(),
imageType.Arch().Distro().Name(),
cacheDir)
solver.SetDNFJSONPath(dnfJsonPath)
depsolvedSets := make(map[string][]rpmmd.PackageSpec)
for name, packages := range imgPackageSets {
res, err := solver.Depsolve(packages)
if err != nil {
panic("Could not depsolve: " + err.Error())
}
depsolvedSets[name] = res
}
return depsolvedSets
}
func isOSTree(imgType distro.ImageType) bool {
return imgType.OSTreeRef() != ""
}
var knownKernels = []string{"kernel", "kernel-debug", "kernel-rt"}
// Returns the number of known kernels in the package list
func kernelCount(imgType distro.ImageType, bp blueprint.Blueprint) int {
ostreeOptions := &ostree.ImageOptions{
URL: "https://example.com", // required by some image types
}
manifest, _, err := imgType.Manifest(&bp, distro.ImageOptions{OSTree: ostreeOptions}, nil, 0)
if err != nil {
panic(err)
}
sets := manifest.GetPackageSetChains()
// Use a map to count unique kernels in a package set. If the same kernel
// name appears twice, it will only be installed once, so we only count it
// once.
kernels := make(map[string]bool)
for _, name := range []string{
// payload package set names
"os", "ostree-tree", "anaconda-tree",
"packages", "installer",
} {
for _, pset := range sets[name] {
for _, pkg := range pset.Include {
for _, kernel := range knownKernels {
if kernel == pkg {
kernels[kernel] = true
}
}
}
if len(kernels) > 0 {
// BUG: some RHEL image types contain both 'packages'
// and 'installer' even though only 'installer' is used
// this counts the kernel package twice. None of these
// sets should appear more than once, so return the count
// for the first package set that has at least one kernel.
return len(kernels)
}
}
}
return len(kernels)
}
func TestDistro_KernelOption(t *testing.T, d distro.Distro) {
skipList := map[string]bool{
// Ostree installers and raw images download a payload to embed or
// deploy. The kernel is part of the payload so it doesn't appear in
// the image type's package lists.
"iot-installer": true,
"edge-installer": true,
"edge-simplified-installer": true,
"iot-raw-image": true,
"edge-raw-image": true,
"edge-ami": true,
// the tar image type is a minimal image type which is not expected to
// be usable without a blueprint (see commit 83a63aaf172f556f6176e6099ffaa2b5357b58f5).
"tar": true,
// containers don't have kernels
"container": true,
// image installer on Fedora doesn't support kernel customizations
// on RHEL we support kernel name
// TODO: Remove when we unify the allowed options
"image-installer": true,
"live-installer": true,
}
{ // empty blueprint: all image types should just have the default kernel
for _, archName := range d.ListArches() {
arch, err := d.GetArch(archName)
assert.NoError(t, err)
for _, typeName := range arch.ListImageTypes() {
if skipList[typeName] {
continue
}
imgType, err := arch.GetImageType(typeName)
assert.NoError(t, err)
nk := kernelCount(imgType, blueprint.Blueprint{})
if nk != 1 {
assert.Fail(t, fmt.Sprintf("%s Kernel count", d.Name()),
"Image type %s (arch %s) specifies %d Kernel packages", typeName, archName, nk)
}
}
}
}
{ // kernel in blueprint: the specified kernel replaces the default
for _, kernelName := range []string{"kernel", "kernel-debug"} {
bp := blueprint.Blueprint{
Customizations: &blueprint.Customizations{
Kernel: &blueprint.KernelCustomization{
Name: kernelName,
},
},
}
for _, archName := range d.ListArches() {
arch, err := d.GetArch(archName)
assert.NoError(t, err)
for _, typeName := range arch.ListImageTypes() {
if typeName != "image-installer" {
continue
}
if typeName != "live-installer" {
continue
}
if skipList[typeName] {
continue
}
imgType, err := arch.GetImageType(typeName)
assert.NoError(t, err)
nk := kernelCount(imgType, bp)
// ostree image types should have only one kernel
// other image types should have at least 1
if nk < 1 || (nk != 1 && isOSTree(imgType)) {
assert.Fail(t, fmt.Sprintf("%s Kernel count", d.Name()),
"Image type %s (arch %s) specifies %d Kernel packages", typeName, archName, nk)
}
}
}
}
}
}
func TestDistro_OSTreeOptions(t *testing.T, d distro.Distro) {
// test that ostree parameters are properly resolved by image functions that should support them
typesWithParent := map[string]bool{ // image types that support specifying a parent commit
"edge-commit": true,
"edge-container": true,
"iot-commit": true,
"iot-container": true,
}
typesWithPayload := map[string]bool{
"edge-ami": true,
"edge-installer": true,
"edge-raw-image": true,
"edge-simplified-installer": true,
"iot-ami": true,
"iot-installer": true,
"iot-raw-image": true,
"iot-simplified-installer": true,
}
assert := assert.New(t)
{ // empty options: payload ref should equal default
for _, archName := range d.ListArches() {
arch, err := d.GetArch(archName)
assert.NoError(err)
for _, typeName := range arch.ListImageTypes() {
bp := &blueprint.Blueprint{}
if strings.HasSuffix(typeName, "simplified-installer") {
// simplified installers require installation device
bp = &blueprint.Blueprint{
Customizations: &blueprint.Customizations{
InstallationDevice: "/dev/sda42",
},
}
}
imgType, err := arch.GetImageType(typeName)
assert.NoError(err)
ostreeOptions := ostree.ImageOptions{}
if typesWithPayload[typeName] {
// payload types require URL
ostreeOptions.URL = "https://example.com/repo"
}
options := distro.ImageOptions{OSTree: &ostreeOptions}
m, _, err := imgType.Manifest(bp, options, nil, 0)
assert.NoError(err)
nrefs := 0
// If a manifest returns an ostree source spec, the ref should
// match the default.
for _, commits := range m.GetOSTreeSourceSpecs() {
for _, commit := range commits {
assert.Equal(options.OSTree.URL, commit.URL, "url does not match expected for image type %q\n", typeName)
assert.Equal(imgType.OSTreeRef(), commit.Ref, "ref does not match expected for image type %q\n", typeName)
nrefs++
}
}
nexpected := 0
if typesWithPayload[typeName] {
// image types with payload should return a ref
nexpected = 1
}
assert.Equal(nexpected, nrefs, "incorrect ref count for image type %q\n", typeName)
}
}
}
{ // ImageRef set: should be returned as payload ref - no parent for commits and containers
for _, archName := range d.ListArches() {
arch, err := d.GetArch(archName)
assert.NoError(err)
for _, typeName := range arch.ListImageTypes() {
bp := &blueprint.Blueprint{}
if strings.HasSuffix(typeName, "simplified-installer") {
// simplified installers require installation device
bp = &blueprint.Blueprint{
Customizations: &blueprint.Customizations{
InstallationDevice: "/dev/sda42",
},
}
}
imgType, err := arch.GetImageType(typeName)
assert.NoError(err)
ostreeOptions := ostree.ImageOptions{
ImageRef: "test/x86_64/01",
}
if typesWithPayload[typeName] {
// payload types require URL
ostreeOptions.URL = "https://example.com/repo"
}
options := distro.ImageOptions{OSTree: &ostreeOptions}
m, _, err := imgType.Manifest(bp, options, nil, 0)
assert.NoError(err)
nrefs := 0
// if a manifest returns an ostree source spec, the ref should
// match the default
for _, commits := range m.GetOSTreeSourceSpecs() {
for _, commit := range commits {
assert.Equal(options.OSTree.URL, commit.URL, "url does not match expected for image type %q\n", typeName)
assert.Equal(options.OSTree.ImageRef, commit.Ref, "ref does not match expected for image type %q\n", typeName)
nrefs++
}
}
nexpected := 0
if typesWithPayload[typeName] {
// image types with payload should return a ref
nexpected = 1
}
assert.Equal(nexpected, nrefs, "incorrect ref count for image type %q\n", typeName)
}
}
}
{ // URL always specified: should add a parent to image types that support it and the ref should match the option
for _, archName := range d.ListArches() {
arch, err := d.GetArch(archName)
assert.NoError(err)
for _, typeName := range arch.ListImageTypes() {
bp := &blueprint.Blueprint{}
if strings.HasSuffix(typeName, "simplified-installer") {
// simplified installers require installation device
bp = &blueprint.Blueprint{
Customizations: &blueprint.Customizations{
InstallationDevice: "/dev/sda42",
},
}
}
imgType, err := arch.GetImageType(typeName)
assert.NoError(err)
ostreeOptions := ostree.ImageOptions{
ImageRef: "test/x86_64/01",
URL: "https://example.com/repo",
}
options := distro.ImageOptions{OSTree: &ostreeOptions}
m, _, err := imgType.Manifest(bp, options, nil, 0)
assert.NoError(err)
nrefs := 0
for _, commits := range m.GetOSTreeSourceSpecs() {
for _, commit := range commits {
assert.Equal(options.OSTree.URL, commit.URL, "url does not match expected for image type %q\n", typeName)
assert.Equal(options.OSTree.ImageRef, commit.Ref, "ref does not match expected for image type %q\n", typeName)
nrefs++
}
}
nexpected := 0
if typesWithPayload[typeName] || typesWithParent[typeName] {
// image types with payload or parent should return a ref
nexpected = 1
}
assert.Equal(nexpected, nrefs, "incorrect ref count for image type %q\n", typeName)
}
}
}
{ // URL and parent ref always specified: payload ref should be default - parent ref should match option
for _, archName := range d.ListArches() {
arch, err := d.GetArch(archName)
assert.NoError(err)
for _, typeName := range arch.ListImageTypes() {
bp := &blueprint.Blueprint{}
if strings.HasSuffix(typeName, "simplified-installer") {
// simplified installers require installation device
bp = &blueprint.Blueprint{
Customizations: &blueprint.Customizations{
InstallationDevice: "/dev/sda42",
},
}
}
imgType, err := arch.GetImageType(typeName)
assert.NoError(err)
ostreeOptions := ostree.ImageOptions{
ParentRef: "test/x86_64/01",
URL: "https://example.com/repo",
}
options := distro.ImageOptions{OSTree: &ostreeOptions}
m, _, err := imgType.Manifest(bp, options, nil, 0)
assert.NoError(err)
nrefs := 0
for _, commits := range m.GetOSTreeSourceSpecs() {
for _, commit := range commits {
assert.Equal(options.OSTree.URL, commit.URL, "url does not match expected for image type %q\n", typeName)
if typesWithPayload[typeName] {
// payload ref should fall back to default
assert.Equal(imgType.OSTreeRef(), commit.Ref, "ref does not match expected for image type %q\n", typeName)
} else if typesWithParent[typeName] {
// parent ref should match option
assert.Equal(options.OSTree.ParentRef, commit.Ref, "ref does not match expected for image type %q\n", typeName)
} else {
// image type requires ostree commit but isn't specified: this shouldn't happen
panic(fmt.Sprintf("image type %q requires ostree commit but is not covered by test", typeName))
}
nrefs++
}
}
nexpected := 0
if typesWithPayload[typeName] || typesWithParent[typeName] {
// image types with payload or parent should return a ref
nexpected = 1
}
assert.Equal(nexpected, nrefs, "incorrect ref count for image type %q\n", typeName)
}
}
}
{ // All options set: all refs should match the corresponding option
for _, archName := range d.ListArches() {
arch, err := d.GetArch(archName)
assert.NoError(err)
for _, typeName := range arch.ListImageTypes() {
bp := &blueprint.Blueprint{}
if strings.HasSuffix(typeName, "simplified-installer") {
// simplified installers require installation device
bp = &blueprint.Blueprint{
Customizations: &blueprint.Customizations{
InstallationDevice: "/dev/sda42",
},
}
}
imgType, err := arch.GetImageType(typeName)
assert.NoError(err)
ostreeOptions := ostree.ImageOptions{
ImageRef: "test/x86_64/01",
ParentRef: "test/x86_64/02",
URL: "https://example.com/repo",
}
options := distro.ImageOptions{OSTree: &ostreeOptions}
m, _, err := imgType.Manifest(bp, options, nil, 0)
assert.NoError(err)
nrefs := 0
for _, commits := range m.GetOSTreeSourceSpecs() {
for _, commit := range commits {
assert.Equal(options.OSTree.URL, commit.URL, "url does not match expected for image type %q\n", typeName)
if typesWithPayload[typeName] {
// payload ref should match image ref
assert.Equal(options.OSTree.ImageRef, commit.Ref, "ref does not match expected for image type %q\n", typeName)
} else if typesWithParent[typeName] {
// parent ref should match option
assert.Equal(options.OSTree.ParentRef, commit.Ref, "ref does not match expected for image type %q\n", typeName)
} else {
// image type requires ostree commit but isn't specified: this shouldn't happen
panic(fmt.Sprintf("image type %q requires ostree commit but is not covered by test", typeName))
}
nrefs++
}
}
nexpected := 0
if typesWithPayload[typeName] || typesWithParent[typeName] {
// image types with payload or parent should return a ref
nexpected = 1
}
assert.Equal(nexpected, nrefs, "incorrect ref count for image type %q\n", typeName)
}
}
}
{ // Parent set without URL: always causes error
for _, archName := range d.ListArches() {
arch, err := d.GetArch(archName)
assert.NoError(err)
for _, typeName := range arch.ListImageTypes() {
bp := &blueprint.Blueprint{}
if strings.HasSuffix(typeName, "simplified-installer") {
// simplified installers require installation device
bp = &blueprint.Blueprint{
Customizations: &blueprint.Customizations{
InstallationDevice: "/dev/sda42",
},
}
}
imgType, err := arch.GetImageType(typeName)
assert.NoError(err)
ostreeOptions := ostree.ImageOptions{
ParentRef: "test/x86_64/02",
}
options := distro.ImageOptions{OSTree: &ostreeOptions}
_, _, err = imgType.Manifest(bp, options, nil, 0)
assert.Error(err)
}
}
}
}

View file

@ -10,6 +10,7 @@ import (
"github.com/osbuild/images/internal/environment"
"github.com/osbuild/images/internal/oscap"
"github.com/osbuild/images/pkg/distro"
"github.com/osbuild/images/pkg/osbuild"
"github.com/osbuild/images/pkg/platform"
"github.com/osbuild/images/pkg/rpmmd"
"github.com/osbuild/images/pkg/runner"
@ -303,10 +304,36 @@ var (
exports: []string{"container"},
}
wslImgType = imageType{
name: "wsl",
filename: "wsl.tar",
mimeType: "application/x-tar",
packageSets: map[string]packageSetFunc{
osPkgsKey: containerPackageSet,
},
defaultImageConfig: &distro.ImageConfig{
NoSElinux: common.ToPtr(true),
ExcludeDocs: common.ToPtr(true),
Locale: common.ToPtr("C.UTF-8"),
Timezone: common.ToPtr("Etc/UTC"),
WSLConfig: &osbuild.WSLConfStageOptions{
Boot: osbuild.WSLConfBootOptions{
Systemd: true,
},
},
},
image: containerImage,
bootable: false,
buildPipelines: []string{"build"},
payloadPipelines: []string{"os", "container"},
exports: []string{"container"},
}
minimalrawImgType = imageType{
name: "minimal-raw",
filename: "raw.img",
mimeType: "application/disk",
name: "minimal-raw",
filename: "raw.img.xz",
compression: "xz",
mimeType: "application/xz",
packageSets: map[string]packageSetFunc{
osPkgsKey: minimalrpmPackageSet,
},
@ -316,8 +343,8 @@ var (
defaultSize: 2 * common.GibiByte,
image: liveImage,
buildPipelines: []string{"build"},
payloadPipelines: []string{"os", "image"},
exports: []string{"image"},
payloadPipelines: []string{"os", "image", "xz"},
exports: []string{"xz"},
basePartitionTables: defaultBasePartitionTables,
}
)
@ -569,6 +596,7 @@ func newDistro(version int) distro.Distro {
x86_64.addImageTypes(
&platform.X86{},
containerImgType,
wslImgType,
)
x86_64.addImageTypes(
&platform.X86{

View file

@ -193,6 +193,7 @@ func osCustomizations(
osc.SshdConfig = imageConfig.SshdConfig
osc.AuthConfig = imageConfig.Authconfig
osc.PwQuality = imageConfig.PwQuality
osc.WSLConfig = imageConfig.WSLConfig
return osc
}
@ -212,6 +213,7 @@ func liveImage(workload workload.Workload,
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], containers, customizations)
img.Environment = t.environment
img.Workload = workload
img.Compression = t.compression
// TODO: move generation into LiveImage
pt, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng)
if err != nil {

View file

@ -61,6 +61,7 @@ type ImageConfig struct {
Firewall *osbuild.FirewallStageOptions
UdevRules *osbuild.UdevRulesStageOptions
GCPGuestAgentConfig *osbuild.GcpGuestAgentConfigOptions
WSLConfig *osbuild.WSLConfStageOptions
}
// InheritFrom inherits unset values from the provided parent configuration and

View file

@ -316,6 +316,7 @@ func newDistro(name string, minor int) *distribution {
x86_64.addImageTypes(
&platform.X86{},
tarImgType(),
wslImgType(),
)
aarch64.addImageTypes(
@ -342,6 +343,7 @@ func newDistro(name string, minor int) *distribution {
aarch64.addImageTypes(
&platform.Aarch64{},
tarImgType(),
wslImgType(),
)
bareMetalAarch64Platform := &platform.Aarch64{
@ -367,6 +369,7 @@ func newDistro(name string, minor int) *distribution {
aarch64.addImageTypes(
rawAarch64Platform,
amiImgTypeAarch64(rd),
minimalRawImgType(rd),
)
ppc64le.addImageTypes(
@ -424,6 +427,11 @@ func newDistro(name string, minor int) *distribution {
UEFIVendor: rd.vendor,
}
x86_64.addImageTypes(
rawUEFIx86Platform,
minimalRawImgType(rd),
)
if rd.isRHEL() {
if !common.VersionLessThan(rd.osVersion, "8.6") {
// image types only available on 8.6 and later on RHEL

View file

@ -139,6 +139,28 @@ func edgeSimplifiedInstallerImgType(rd distribution) imageType {
return it
}
func minimalRawImgType(rd distribution) imageType {
it := imageType{
name: "minimal-raw",
filename: "raw.img.xz",
compression: "xz",
mimeType: "application/xz",
packageSets: map[string]packageSetFunc{
osPkgsKey: minimalrpmPackageSet,
},
rpmOstree: false,
kernelOptions: "ro no_timer_check console=ttyS0,115200n8 biosdevname=0 net.ifnames=0",
bootable: true,
defaultSize: 2 * common.GibiByte,
image: liveImage,
buildPipelines: []string{"build"},
payloadPipelines: []string{"os", "image", "xz"},
exports: []string{"xz"},
basePartitionTables: defaultBasePartitionTables,
}
return it
}
// edge commit OS package set
func edgeCommitPackageSet(t *imageType) rpmmd.PackageSet {
ps := rpmmd.PackageSet{

View file

@ -222,6 +222,7 @@ func osCustomizations(
osc.WAAgentConfig = imageConfig.WAAgentConfig
osc.UdevRules = imageConfig.UdevRules
osc.GCPGuestAgentConfig = imageConfig.GCPGuestAgentConfig
osc.WSLConfig = imageConfig.WSLConfig
return osc
}

View file

@ -73,3 +73,11 @@ func distroSpecificPackageSet(t *imageType) rpmmd.PackageSet {
}
return rpmmd.PackageSet{}
}
func minimalrpmPackageSet(t *imageType) rpmmd.PackageSet {
return rpmmd.PackageSet{
Include: []string{
"@core",
},
}
}

View file

@ -0,0 +1,144 @@
package rhel8
import (
"github.com/osbuild/images/internal/common"
"github.com/osbuild/images/pkg/distro"
"github.com/osbuild/images/pkg/osbuild"
"github.com/osbuild/images/pkg/rpmmd"
)
func wslImgType() imageType {
return imageType{
name: "wsl",
filename: "disk.tar.gz",
mimeType: "application/x-tar",
packageSets: map[string]packageSetFunc{
osPkgsKey: ubiCommonPackageSet,
},
defaultImageConfig: &distro.ImageConfig{
Locale: common.ToPtr("en_US.UTF-8"),
NoSElinux: common.ToPtr(true),
WSLConfig: &osbuild.WSLConfStageOptions{
Boot: osbuild.WSLConfBootOptions{
Systemd: true,
},
},
},
bootable: false,
image: tarImage,
buildPipelines: []string{"build"},
payloadPipelines: []string{"os", "archive"},
exports: []string{"archive"},
}
}
func ubiCommonPackageSet(t *imageType) rpmmd.PackageSet {
ps := rpmmd.PackageSet{
Include: []string{
"alternatives",
"audit-libs",
"basesystem",
"bash",
"brotli",
"ca-certificates",
"coreutils-single",
"crypto-policies-scripts",
"curl",
"libcurl",
"dnf",
"filesystem",
"findutils",
"gdb-gdbserver",
// Differs from official UBI, as we don't include CRB repos
// "gdbm",
"glibc-minimal-langpack",
"gmp",
"gnupg2",
"gobject-introspection",
"hostname",
"langpacks-en",
"pam",
"passwd",
"python3",
"python3-inotify",
"python3-systemd",
"redhat-release",
"rootfiles",
"rpm",
"sed",
"setup",
"shadow-utils",
"subscription-manager",
"systemd",
"tar",
"tpm2-tss",
"tzdata",
"util-linux",
"vim-minimal",
"yum",
},
Exclude: []string{
"aic94xx-firmware",
"alsa-firmware",
"alsa-lib",
"alsa-tools-firmware",
"biosdevname",
"cpio",
"diffutils",
"dnf-plugin-spacewalk",
"dracut",
"elfutils-debuginfod-client",
"fedora-release",
"fedora-repos",
"file",
"fontpackages-filesystem",
"gawk-all-langpacks",
"gettext",
"glibc-gconv-extra",
"glibc-langpack-en",
"gnupg2-smime",
"grub2-common",
"hardlink",
"iprutils",
"ivtv-firmware",
"kbd",
"kmod",
"kpartx",
"libcroco",
"libcrypt-compat",
"libevent",
"libkcapi",
"libkcapi-hmaccalc",
"libsecret",
"libselinux-utils",
"libxkbcommon",
"libertas-sd8787-firmware",
"memstrack",
"nss",
"openssl",
"openssl-pkcs11",
"os-prober",
"pigz",
"pinentry",
"plymouth",
"policycoreutils",
"python3-unbound",
"redhat-release-eula",
"rng-tools",
"rpm-plugin-selinux",
"rpm-plugin-systemd-inhibit",
"selinux-policy",
"selinux",
"selinux-policy-targeted",
"shared-mime-info",
"systemd-udev",
"trousers",
"udisks2",
"unbound-libs",
"xkeyboard-config",
"xz",
},
}
return ps
}

View file

@ -321,6 +321,17 @@ func newDistro(name string, minor int) *distribution {
edgeAMIImgType,
)
x86_64.addImageTypes(
&platform.X86{
BasePlatform: platform.BasePlatform{
ImageFormat: platform.FORMAT_VMDK,
},
BIOS: true,
UEFIVendor: rd.vendor,
},
edgeVsphereImgType,
)
x86_64.addImageTypes(
&platform.X86{
BasePlatform: platform.BasePlatform{
@ -330,11 +341,13 @@ func newDistro(name string, minor int) *distribution {
UEFIVendor: rd.vendor,
},
edgeSimplifiedInstallerImgType,
minimalrawImgType,
)
x86_64.addImageTypes(
&platform.X86{},
tarImgType,
wslImgType,
)
aarch64.addImageTypes(
@ -350,6 +363,7 @@ func newDistro(name string, minor int) *distribution {
aarch64.addImageTypes(
&platform.Aarch64{},
tarImgType,
wslImgType,
)
aarch64.addImageTypes(
@ -364,6 +378,17 @@ func newDistro(name string, minor int) *distribution {
imageInstaller,
edgeAMIImgType,
)
aarch64.addImageTypes(
&platform.Aarch64{
BasePlatform: platform.BasePlatform{
ImageFormat: platform.FORMAT_VMDK,
},
UEFIVendor: rd.vendor,
},
edgeVsphereImgType,
)
aarch64.addImageTypes(
&platform.Aarch64{
BasePlatform: platform.BasePlatform{
@ -372,6 +397,7 @@ func newDistro(name string, minor int) *distribution {
UEFIVendor: rd.vendor,
},
edgeRawImgType,
minimalrawImgType,
)
aarch64.addImageTypes(

View file

@ -153,6 +153,44 @@ var (
environment: &environment.EC2{},
}
edgeVsphereImgType = imageType{
name: "edge-vsphere",
filename: "image.vmdk",
mimeType: "application/x-vmdk",
packageSets: nil,
defaultImageConfig: &distro.ImageConfig{
Locale: common.ToPtr("en_US.UTF-8"),
},
defaultSize: 10 * common.GibiByte,
rpmOstree: true,
bootable: true,
bootISO: false,
image: edgeRawImage,
buildPipelines: []string{"build"},
payloadPipelines: []string{"ostree-deployment", "image", "vmdk"},
exports: []string{"vmdk"},
basePartitionTables: edgeBasePartitionTables,
}
minimalrawImgType = imageType{
name: "minimal-raw",
filename: "raw.img.xz",
compression: "xz",
mimeType: "application/xz",
packageSets: map[string]packageSetFunc{
osPkgsKey: minimalrpmPackageSet,
},
rpmOstree: false,
kernelOptions: "ro no_timer_check console=ttyS0,115200n8 biosdevname=0 net.ifnames=0",
bootable: true,
defaultSize: 2 * common.GibiByte,
image: liveImage,
buildPipelines: []string{"build"},
payloadPipelines: []string{"os", "image", "xz"},
exports: []string{"xz"},
basePartitionTables: defaultBasePartitionTables,
}
// Shared Services
edgeServices = []string{
// TODO(runcom): move fdo-client-linuxapp.service to presets?

View file

@ -219,6 +219,7 @@ func osCustomizations(
osc.WAAgentConfig = imageConfig.WAAgentConfig
osc.UdevRules = imageConfig.UdevRules
osc.GCPGuestAgentConfig = imageConfig.GCPGuestAgentConfig
osc.WSLConfig = imageConfig.WSLConfig
return osc
}

View file

@ -343,7 +343,7 @@ func (t *imageType) checkOptions(bp *blueprint.Blueprint, options distro.ImageOp
}
}
if t.name == "edge-raw-image" || t.name == "edge-ami" {
if t.name == "edge-raw-image" || t.name == "edge-ami" || t.name == "edge-vsphere" {
// ostree-based bootable images require a URL from which to pull a payload commit
if ostreeURL == "" {
return warnings, fmt.Errorf("%q images require specifying a URL from which to retrieve the OSTree commit", t.name)

View file

@ -245,3 +245,11 @@ func distroSpecificPackageSet(t *imageType) rpmmd.PackageSet {
}
return rpmmd.PackageSet{}
}
func minimalrpmPackageSet(t *imageType) rpmmd.PackageSet {
return rpmmd.PackageSet{
Include: []string{
"@core",
},
}
}

View file

@ -0,0 +1,91 @@
package rhel9
import (
"github.com/osbuild/images/internal/common"
"github.com/osbuild/images/pkg/distro"
"github.com/osbuild/images/pkg/osbuild"
"github.com/osbuild/images/pkg/rpmmd"
)
var wslImgType = imageType{
name: "wsl",
filename: "disk.tar.gz",
mimeType: "application/x-tar",
packageSets: map[string]packageSetFunc{
osPkgsKey: ubiCommonPackageSet,
},
defaultImageConfig: &distro.ImageConfig{
Locale: common.ToPtr("en_US.UTF-8"),
NoSElinux: common.ToPtr(true),
WSLConfig: &osbuild.WSLConfStageOptions{
Boot: osbuild.WSLConfBootOptions{
Systemd: true,
},
},
},
bootable: false,
image: tarImage,
buildPipelines: []string{"build"},
payloadPipelines: []string{"os", "archive"},
exports: []string{"archive"},
}
func ubiCommonPackageSet(t *imageType) rpmmd.PackageSet {
ps := rpmmd.PackageSet{
Include: []string{
"alternatives",
"audit-libs",
"basesystem",
"bash",
"ca-certificates",
"coreutils-single",
"crypto-policies-scripts",
"curl-minimal",
"dejavu-sans-fonts",
"dnf",
"filesystem",
"findutils",
"gdb-gdbserver",
// Differs from official UBI, as we don't include CRB repos
// "gdbm",
"glibc-minimal-langpack",
"gmp",
"gnupg2",
"gobject-introspection",
"hostname",
"langpacks-en",
"libcurl-minimal",
"openssl",
"pam",
"passwd",
"procps-ng",
"python3",
"python3-inotify",
"redhat-release",
"rootfiles",
"rpm",
"sed",
"setup",
"shadow-utils",
"subscription-manager",
"systemd",
"tar",
"tpm2-tss",
"tzdata",
"util-linux",
"vim-minimal",
"yum",
},
Exclude: []string{
"gawk-all-langpacks",
"glibc-gconv-extra",
"glibc-langpack-en",
"openssl-pkcs11",
"python-unversioned-command",
"redhat-release-eula",
"rpm-plugin-systemd-inhibit",
},
}
return ps
}

View file

@ -87,17 +87,17 @@ func (img *LiveImage) InstantiateManifest(m *manifest.Manifest,
artifactPipeline = vpcPipeline
artifact = vpcPipeline.Export()
case platform.FORMAT_VMDK:
vmdkPipeline := manifest.NewVMDK(m, buildPipeline, imagePipeline)
vmdkPipeline := manifest.NewVMDK(m, buildPipeline, imagePipeline, nil)
if img.Compression == "" {
vmdkPipeline.Filename = img.Filename
}
artifactPipeline = vmdkPipeline
artifact = vmdkPipeline.Export()
case platform.FORMAT_OVA:
vmdkPipeline := manifest.NewVMDK(m, buildPipeline, imagePipeline)
vmdkPipeline := manifest.NewVMDK(m, buildPipeline, imagePipeline, nil)
ovfPipeline := manifest.NewOVF(m, buildPipeline, vmdkPipeline)
artifactPipeline := manifest.NewTar(m, buildPipeline, ovfPipeline, "archive")
artifactPipeline.Format = osbuild.TarArchiveFormatOldgnu
artifactPipeline.Format = osbuild.TarArchiveFormatUstar
artifactPipeline.RootNode = osbuild.TarRootNodeOmit
artifactPipeline.Filename = img.Filename
artifact = artifactPipeline.Export()

View file

@ -91,16 +91,27 @@ func (img *OSTreeRawImage) InstantiateManifest(m *manifest.Manifest,
buildPipeline.Checkpoint()
var art *artifact.Artifact
switch img.Compression {
case "xz":
ostreeCompressed := ostreeCompressedImagePipelines(img, m, buildPipeline)
art = ostreeCompressed.Export()
case "":
switch img.Platform.GetImageFormat() {
case platform.FORMAT_VMDK:
if img.Compression != "" {
panic(fmt.Sprintf("no compression is allowed with VMDK format for %q", img.name))
}
ostreeBase := baseRawOstreeImage(img, m, buildPipeline)
ostreeBase.Filename = img.Filename
art = ostreeBase.Export()
vmdkPipeline := manifest.NewVMDK(m, buildPipeline, nil, ostreeBase)
vmdkPipeline.Filename = img.Filename
art = vmdkPipeline.Export()
default:
panic(fmt.Sprintf("unsupported compression type %q on %q", img.Compression, img.name))
switch img.Compression {
case "xz":
ostreeCompressed := ostreeCompressedImagePipelines(img, m, buildPipeline)
art = ostreeCompressed.Export()
case "":
ostreeBase := baseRawOstreeImage(img, m, buildPipeline)
ostreeBase.Filename = img.Filename
art = ostreeBase.Export()
default:
panic(fmt.Sprintf("unsupported compression type %q on %q", img.Compression, img.name))
}
}
return art, nil

View file

@ -119,6 +119,7 @@ type OSCustomizations struct {
NTPServers []osbuild.ChronyConfigServer
WAAgentConfig *osbuild.WAAgentConfStageOptions
UdevRules *osbuild.UdevRulesStageOptions
WSLConfig *osbuild.WSLConfStageOptions
LeapSecTZ *string
FactAPIType *facts.APIType
@ -271,7 +272,7 @@ func (p *OS) getBuildPackages(distro Distro) []string {
packages = append(packages, "python3-pyyaml")
}
}
if len(p.DNFConfig) > 0 || len(p.RHSMConfig) > 0 {
if len(p.DNFConfig) > 0 || len(p.RHSMConfig) > 0 || p.WSLConfig != nil {
packages = append(packages, "python3-iniparse")
}
@ -705,6 +706,10 @@ func (p *OS) serialize() osbuild.Pipeline {
pipeline.AddStage(osbuild.GenShellInitStage(p.ShellInit))
}
if wslConf := p.WSLConfig; wslConf != nil {
pipeline.AddStage(osbuild.NewWSLConfStage(wslConf))
}
if p.SElinux != "" {
pipeline.AddStage(osbuild.NewSELinuxStage(&osbuild.SELinuxStageOptions{
FileContexts: fmt.Sprintf("etc/selinux/%s/contexts/files/file_contexts", p.SElinux),

View file

@ -5,26 +5,43 @@ import (
"github.com/osbuild/images/pkg/osbuild"
)
// A VMDK turns a raw image file into vmdk image.
// A VMDK turns a raw image file or a raw ostree image file into vmdk image.
type VMDK struct {
Base
Filename string
imgPipeline *RawImage
imgPipeline Pipeline
}
// NewVMDK creates a new VMDK pipeline. imgPipeline is the pipeline producing the
// raw image. Filename is the name of the produced image.
// raw image. imgOstreePipeline is the pipeline producing the raw ostree image.
// Either imgPipeline or imgOStreePipeline are required, but not both at the same time.
// Filename is the name of the produced image.
func NewVMDK(m *Manifest,
buildPipeline *Build,
imgPipeline *RawImage) *VMDK {
p := &VMDK{
Base: NewBase(m, "vmdk", buildPipeline),
imgPipeline: imgPipeline,
Filename: "image.vmdk",
imgPipeline *RawImage, imgOstreePipeline *RawOSTreeImage) *VMDK {
if imgPipeline != nil && imgOstreePipeline != nil {
panic("NewVMDK requires either RawImage or RawOSTreeImage")
}
if imgPipeline.Base.manifest != m {
panic("live image pipeline from different manifest")
var p *VMDK
if imgPipeline != nil {
p = &VMDK{
Base: NewBase(m, "vmdk", buildPipeline),
imgPipeline: imgPipeline,
Filename: "image.vmdk",
}
if imgPipeline.Base.manifest != m {
panic("live image pipeline from different manifest")
}
} else {
p = &VMDK{
Base: NewBase(m, "vmdk", buildPipeline),
imgPipeline: imgOstreePipeline,
Filename: "image.vmdk",
}
if imgOstreePipeline.Base.manifest != m {
panic("live image pipeline from different manifest")
}
}
buildPipeline.addDependent(p)
m.addPipeline(p)
@ -38,7 +55,7 @@ func (p *VMDK) serialize() osbuild.Pipeline {
osbuild.NewQEMUStageOptions(p.Filename, osbuild.QEMUFormatVMDK, osbuild.VMDKOptions{
Subformat: osbuild.VMDKSubformatStreamOptimized,
}),
osbuild.NewQemuStagePipelineFilesInputs(p.imgPipeline.Name(), p.imgPipeline.Filename),
osbuild.NewQemuStagePipelineFilesInputs(p.imgPipeline.Name(), p.imgPipeline.Export().Filename()),
))
return pipeline

View file

@ -76,8 +76,8 @@ func RunOSBuild(manifest []byte, store, outputDirectory string, exports, checkpo
}
if err != nil {
// ignore ExitError if output could be decoded correctly
if _, isExitError := err.(*exec.ExitError); !isExitError {
// ignore ExitError if output could be decoded correctly (only if running with --json)
if _, isExitError := err.(*exec.ExitError); !isExitError || !result {
return nil, fmt.Errorf("running osbuild failed: %v", err)
}
}

View file

@ -4,6 +4,8 @@ type SysconfigStageOptions struct {
Kernel *SysconfigKernelOptions `json:"kernel,omitempty"`
Network *SysconfigNetworkOptions `json:"network,omitempty"`
NetworkScripts *NetworkScriptsOptions `json:"network-scripts,omitempty"`
Desktop *SysconfigDesktopOptions `json:"desktop,omitempty"`
LiveSys *SysconfigLivesysOptions `json:"livesys,omitempty"`
}
func (SysconfigStageOptions) isStageOptions() {}
@ -25,6 +27,15 @@ type SysconfigKernelOptions struct {
DefaultKernel string `json:"default_kernel,omitempty"`
}
type SysconfigDesktopOptions struct {
Preferred string `json:"preferred,omitempty"`
DisplayManager string `json:"displaymanager,omitempty"`
}
type SysconfigLivesysOptions struct {
Session string `json:"session"`
}
type NetworkScriptsOptions struct {
// Keys are interface names, values are objects containing interface configuration
IfcfgFiles map[string]IfcfgFile `json:"ifcfg,omitempty"`

View file

@ -0,0 +1,18 @@
package osbuild
type WSLConfStageOptions struct {
Boot WSLConfBootOptions `json:"boot"`
}
type WSLConfBootOptions struct {
Systemd bool `json:"systemd"`
}
func (WSLConfStageOptions) isStageOptions() {}
func NewWSLConfStage(options *WSLConfStageOptions) *Stage {
return &Stage{
Type: "org.osbuild.wsl.conf",
Options: options,
}
}