Update osbuild/images to v0.41.0

Multiple blueprint fixes:

- Extend the blueprint service customizations to accept services to be
  masked.

- The `storage-path` and `container-transport` fields were removed in
  imagees 41.0 in order to simplify the way local storage containers are
  handled.
This commit is contained in:
Gianluca Zuccarelli 2024-02-28 14:57:33 +00:00 committed by Tomáš Hozza
parent 4e504f7905
commit f6b76cce31
50 changed files with 615 additions and 529 deletions

View file

@ -1,13 +1,6 @@
// Package blueprint contains primitives for representing weldr blueprints
package blueprint
import "fmt"
const (
dockerTransport = "docker"
containersStorageTransport = "containers-storage"
)
// A Blueprint is a high-level description of an image.
type Blueprint struct {
Name string `json:"name" toml:"name"`
@ -36,12 +29,11 @@ type Group struct {
}
type Container struct {
Source string `json:"source,omitempty" toml:"source"`
Source string `json:"source" toml:"source"`
Name string `json:"name,omitempty" toml:"name,omitempty"`
TLSVerify *bool `json:"tls-verify,omitempty" toml:"tls-verify,omitempty"`
ContainersTransport *string `json:"containers-transport,omitempty" toml:"containers-transport,omitempty"`
StoragePath *string `json:"source-path,omitempty" toml:"source-path,omitempty"`
TLSVerify *bool `json:"tls-verify,omitempty" toml:"tls-verify,omitempty"`
LocalStorage bool `json:"local-storage,omitempty" toml:"local-storage,omitempty"`
}
// packages, modules, and groups all resolve to rpm packages right now. This
@ -79,31 +71,3 @@ func (p Package) ToNameVersion() string {
return p.Name + "-" + p.Version
}
func (c Container) Validate() error {
if c.StoragePath != nil {
if c.ContainersTransport == nil {
// error out here, but realistically we could also just
// set the transport instead
return fmt.Errorf("Cannot specify storage location %s without a transport", *c.StoragePath)
}
if *c.ContainersTransport != containersStorageTransport {
return fmt.Errorf(
"Incompatible transport %s for storage location %s, only containers-storage transport is supported",
*c.ContainersTransport,
*c.StoragePath,
)
}
}
if c.ContainersTransport == nil {
return nil
}
if *c.ContainersTransport != dockerTransport && *c.ContainersTransport != containersStorageTransport {
return fmt.Errorf("Unknown containers-transport: %s", *c.ContainersTransport)
}
return nil
}

View file

@ -108,6 +108,7 @@ type FirewallServicesCustomization struct {
type ServicesCustomization struct {
Enabled []string `json:"enabled,omitempty" toml:"enabled,omitempty"`
Disabled []string `json:"disabled,omitempty" toml:"disabled,omitempty"`
Masked []string `json:"masked,omitempty" toml:"masked,omitempty"`
}
type OpenSCAPCustomization struct {

View file

@ -35,9 +35,6 @@ import (
const (
DefaultUserAgent = "osbuild-composer/1.0"
DefaultPolicyPath = "/etc/containers/policy.json"
containersStorageTransport = "containers-storage"
dockerTransport = "docker"
)
// GetDefaultAuthFile returns the authentication file to use for the
@ -344,32 +341,17 @@ func (m RawManifest) Digest() (digest.Digest, error) {
return manifest.Digest(m.Data)
}
func getImageRef(target reference.Named, transport string, storagePath string) (types.ImageReference, error) {
switch transport {
case "", dockerTransport:
ref, err := docker.NewReference(target)
if err != nil {
return nil, err
}
return ref, nil
case containersStorageTransport:
var storage string
if storagePath != "" {
storage = fmt.Sprintf("[overlay@%s]", storagePath)
}
ref, err := alltransports.ParseImageName(fmt.Sprintf("%s:%s%s", transport, storage, target.Name()))
if err != nil {
return nil, err
}
return ref, nil
default:
return nil, fmt.Errorf("Unknown containers-transport: %s", transport)
func getImageRef(target reference.Named, local bool) (types.ImageReference, error) {
if local {
return alltransports.ParseImageName(fmt.Sprintf("containers-storage:%s", target))
}
return docker.NewReference(target)
}
// GetManifest fetches the raw manifest data from the server. If digest is not empty
// it will override any given tag for the Client's Target.
func (cl *Client) GetManifest(ctx context.Context, digest digest.Digest, container *SourceSpec) (r RawManifest, err error) {
func (cl *Client) GetManifest(ctx context.Context, digest digest.Digest, local bool) (r RawManifest, err error) {
target := cl.Target
if digest != "" {
@ -382,17 +364,7 @@ func (cl *Client) GetManifest(ctx context.Context, digest digest.Digest, contain
target = t
}
var transport string
if container != nil && container.ContainersTransport != nil {
transport = *container.ContainersTransport
}
var storagePath string
if container != nil && container.StoragePath != nil {
storagePath = *container.StoragePath
}
ref, err := getImageRef(target, transport, storagePath)
ref, err := getImageRef(target, local)
if err != nil {
return
}
@ -438,7 +410,7 @@ func (cl *Client) resolveManifestList(ctx context.Context, list manifestList) (r
return resolvedIds{}, err
}
raw, err := cl.GetManifest(ctx, digest, nil)
raw, err := cl.GetManifest(ctx, digest, false)
if err != nil {
return resolvedIds{}, fmt.Errorf("error getting manifest: %w", err)
}
@ -522,10 +494,9 @@ func (cl *Client) resolveRawManifest(ctx context.Context, rm RawManifest) (resol
// which is the digest of the configuration object. It uses the architecture and
// variant specified via SetArchitectureChoice or the corresponding defaults for
// the host.
func (cl *Client) Resolve(ctx context.Context, name string, container *SourceSpec) (Spec, error) {
raw, err := cl.GetManifest(ctx, "", container)
func (cl *Client) Resolve(ctx context.Context, name string, local bool) (Spec, error) {
raw, err := cl.GetManifest(ctx, "", local)
if err != nil {
return Spec{}, fmt.Errorf("error getting manifest: %w", err)
}
@ -535,13 +506,6 @@ func (cl *Client) Resolve(ctx context.Context, name string, container *SourceSpe
return Spec{}, err
}
var transport *string
var location *string
if container != nil {
transport = container.ContainersTransport
location = container.StoragePath
}
spec := NewSpec(
cl.Target,
ids.Manifest,
@ -549,8 +513,7 @@ func (cl *Client) Resolve(ctx context.Context, name string, container *SourceSpe
cl.GetTLSVerify(),
ids.ListManifest.String(),
name,
transport,
location,
local,
)
return spec, nil

View file

@ -23,12 +23,11 @@ type Resolver struct {
}
type SourceSpec struct {
Source string
Name string
Digest *string
TLSVerify *bool
ContainersTransport *string
StoragePath *string
Source string
Name string
Digest *string
TLSVerify *bool
Local bool
}
func NewResolver(arch string) *Resolver {
@ -55,7 +54,7 @@ func (r *Resolver) Add(spec SourceSpec) {
}
go func() {
spec, err := client.Resolve(r.ctx, spec.Name, &spec)
spec, err := client.Resolve(r.ctx, spec.Name, spec.Local)
if err != nil {
err = fmt.Errorf("'%s': %w", spec.Source, err)
}

View file

@ -11,31 +11,29 @@ import (
// at the Source via Digest and ImageID. The latter one
// should remain the same in the target image as well.
type Spec struct {
Source string // does not include the manifest digest
Digest string // digest of the manifest at the Source
TLSVerify *bool // controls TLS verification
ImageID string // container image identifier
LocalName string // name to use inside the image
ListDigest string // digest of the list manifest at the Source (optional)
ContainersTransport *string // the type of transport used for the container
StoragePath *string // location of the local containers-storage
Source string // does not include the manifest digest
Digest string // digest of the manifest at the Source
TLSVerify *bool // controls TLS verification
ImageID string // container image identifier
LocalName string // name to use inside the image
ListDigest string // digest of the list manifest at the Source (optional)
LocalStorage bool
}
// NewSpec creates a new Spec from the essential information.
// It also converts is the transition point from container
// specific types (digest.Digest) to generic types (string).
func NewSpec(source reference.Named, digest, imageID digest.Digest, tlsVerify *bool, listDigest string, localName string, transport *string, storagePath *string) Spec {
func NewSpec(source reference.Named, digest, imageID digest.Digest, tlsVerify *bool, listDigest string, localName string, localStorage bool) Spec {
if localName == "" {
localName = source.String()
}
return Spec{
Source: source.Name(),
Digest: digest.String(),
TLSVerify: tlsVerify,
ImageID: imageID.String(),
LocalName: localName,
ListDigest: listDigest,
ContainersTransport: transport,
StoragePath: storagePath,
Source: source.Name(),
Digest: digest.String(),
TLSVerify: tlsVerify,
ImageID: imageID.String(),
LocalName: localName,
ListDigest: listDigest,
LocalStorage: localStorage,
}
}

View file

@ -818,58 +818,56 @@ func newDistro(version int) distro.Distro {
minimalrawImgType,
)
if !common.VersionLessThan(rd.Releasever(), "38") {
// iot simplified installer was introduced in F38
x86_64.addImageTypes(
&platform.X86{
BasePlatform: platform.BasePlatform{
ImageFormat: platform.FORMAT_RAW,
FirmwarePackages: []string{
"grub2-efi-x64",
"grub2-efi-x64-cdboot",
"grub2-tools",
"grub2-tools-minimal",
"efibootmgr",
"shim-x64",
"brcmfmac-firmware",
"iwlwifi-dvm-firmware",
"iwlwifi-mvm-firmware",
"realtek-firmware",
"microcode_ctl",
},
// iot simplified installer was introduced in F38
x86_64.addImageTypes(
&platform.X86{
BasePlatform: platform.BasePlatform{
ImageFormat: platform.FORMAT_RAW,
FirmwarePackages: []string{
"grub2-efi-x64",
"grub2-efi-x64-cdboot",
"grub2-tools",
"grub2-tools-minimal",
"efibootmgr",
"shim-x64",
"brcmfmac-firmware",
"iwlwifi-dvm-firmware",
"iwlwifi-mvm-firmware",
"realtek-firmware",
"microcode_ctl",
},
BIOS: false,
UEFIVendor: "fedora",
},
iotSimplifiedInstallerImgType,
)
BIOS: false,
UEFIVendor: "fedora",
},
iotSimplifiedInstallerImgType,
)
aarch64.addImageTypes(
&platform.Aarch64{
BasePlatform: platform.BasePlatform{
FirmwarePackages: []string{
"arm-image-installer",
"bcm283x-firmware",
"grub2-efi-aa64",
"grub2-efi-aa64-cdboot",
"grub2-tools",
"grub2-tools-minimal",
"efibootmgr",
"shim-aa64",
"brcmfmac-firmware",
"iwlwifi-dvm-firmware",
"iwlwifi-mvm-firmware",
"realtek-firmware",
"uboot-images-armv8",
},
aarch64.addImageTypes(
&platform.Aarch64{
BasePlatform: platform.BasePlatform{
FirmwarePackages: []string{
"arm-image-installer",
"bcm283x-firmware",
"grub2-efi-aa64",
"grub2-efi-aa64-cdboot",
"grub2-tools",
"grub2-tools-minimal",
"efibootmgr",
"shim-aa64",
"brcmfmac-firmware",
"iwlwifi-dvm-firmware",
"iwlwifi-mvm-firmware",
"realtek-firmware",
"uboot-images-armv8",
},
UEFIVendor: "fedora",
},
iotSimplifiedInstallerImgType,
)
}
UEFIVendor: "fedora",
},
iotSimplifiedInstallerImgType,
)
if !common.VersionLessThan(rd.Releasever(), "39") {
if common.VersionGreaterThanOrEqual(rd.Releasever(), "39") {
// bootc was introduced in F39
x86_64.addImageTypes(
&platform.X86{

View file

@ -69,6 +69,7 @@ func osCustomizations(
osc.EnabledServices = imageConfig.EnabledServices
osc.DisabledServices = imageConfig.DisabledServices
osc.MaskedServices = imageConfig.MaskedServices
if imageConfig.DefaultTarget != nil {
osc.DefaultTarget = *imageConfig.DefaultTarget
}
@ -353,9 +354,8 @@ func imageInstallerImage(workload workload.Workload,
img.UnattendedKickstart = instCust.Unattended
}
// Enable anaconda-webui for Fedora > 38
distro := t.Arch().Distro()
if !common.VersionLessThan(distro.Releasever(), "38") {
if common.VersionGreaterThanOrEqual(distro.Releasever(), "39") {
img.AdditionalAnacondaModules = []string{
"org.fedoraproject.Anaconda.Modules.Security",
"org.fedoraproject.Anaconda.Modules.Timezone",
@ -369,8 +369,6 @@ func imageInstallerImage(workload workload.Workload,
// time since they might be running headless and a UI is
// unnecessary.
img.AdditionalKernelOpts = []string{"inst.text", "inst.noninteractive"}
} else {
img.AdditionalKernelOpts = []string{"inst.webui", "inst.webui.remote"}
}
}
img.AdditionalAnacondaModules = append(img.AdditionalAnacondaModules, "org.fedoraproject.Anaconda.Modules.Users")
@ -412,23 +410,23 @@ func iotCommitImage(workload workload.Workload,
img.Platform = t.platform
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], containers, bp.Customizations)
if !common.VersionLessThan(d.Releasever(), "38") {
// see https://github.com/ostreedev/ostree/issues/2840
img.OSCustomizations.Presets = []osbuild.Preset{
{
Name: "ignition-firstboot-complete.service",
State: osbuild.StateEnable,
},
{
Name: "coreos-ignition-write-issues.service",
State: osbuild.StateEnable,
},
{
Name: "fdo-client-linuxapp.service",
State: osbuild.StateEnable,
},
}
// see https://github.com/ostreedev/ostree/issues/2840
img.OSCustomizations.Presets = []osbuild.Preset{
{
Name: "ignition-firstboot-complete.service",
State: osbuild.StateEnable,
},
{
Name: "coreos-ignition-write-issues.service",
State: osbuild.StateEnable,
},
{
Name: "fdo-client-linuxapp.service",
State: osbuild.StateEnable,
},
}
img.Environment = t.environment
img.Workload = workload
img.OSTreeParent = parentCommit
@ -478,23 +476,23 @@ func iotContainerImage(workload workload.Workload,
d := t.arch.distro
img.Platform = t.platform
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], containers, bp.Customizations)
if !common.VersionLessThan(d.Releasever(), "38") {
// see https://github.com/ostreedev/ostree/issues/2840
img.OSCustomizations.Presets = []osbuild.Preset{
{
Name: "ignition-firstboot-complete.service",
State: osbuild.StateEnable,
},
{
Name: "coreos-ignition-write-issues.service",
State: osbuild.StateEnable,
},
{
Name: "fdo-client-linuxapp.service",
State: osbuild.StateEnable,
},
}
// see https://github.com/ostreedev/ostree/issues/2840
img.OSCustomizations.Presets = []osbuild.Preset{
{
Name: "ignition-firstboot-complete.service",
State: osbuild.StateEnable,
},
{
Name: "coreos-ignition-write-issues.service",
State: osbuild.StateEnable,
},
{
Name: "fdo-client-linuxapp.service",
State: osbuild.StateEnable,
},
}
img.ContainerLanguage = img.OSCustomizations.Language
img.Environment = t.environment
img.Workload = workload
@ -575,8 +573,6 @@ func iotImage(workload workload.Workload,
}
img := image.NewOSTreeDiskImageFromCommit(commit)
distro := t.Arch().Distro()
customizations := bp.Customizations
img.FIPS = customizations.GetFIPS()
img.Users = users.UsersFromBP(customizations.GetUsers())
@ -607,17 +603,15 @@ func iotImage(workload workload.Workload,
img.OSName = "fedora-iot"
img.LockRoot = true
if !common.VersionLessThan(distro.Releasever(), "38") {
img.KernelOptionsAppend = append(img.KernelOptionsAppend, "coreos.no_persist_ip")
switch img.Platform.GetImageFormat() {
case platform.FORMAT_RAW:
img.IgnitionPlatform = "metal"
if bpIgnition := customizations.GetIgnition(); bpIgnition != nil && bpIgnition.FirstBoot != nil && bpIgnition.FirstBoot.ProvisioningURL != "" {
img.KernelOptionsAppend = append(img.KernelOptionsAppend, "ignition.config.url="+bpIgnition.FirstBoot.ProvisioningURL)
}
case platform.FORMAT_QCOW2:
img.IgnitionPlatform = "qemu"
img.KernelOptionsAppend = append(img.KernelOptionsAppend, "coreos.no_persist_ip")
switch img.Platform.GetImageFormat() {
case platform.FORMAT_RAW:
img.IgnitionPlatform = "metal"
if bpIgnition := customizations.GetIgnition(); bpIgnition != nil && bpIgnition.FirstBoot != nil && bpIgnition.FirstBoot.ProvisioningURL != "" {
img.KernelOptionsAppend = append(img.KernelOptionsAppend, "ignition.config.url="+bpIgnition.FirstBoot.ProvisioningURL)
}
case platform.FORMAT_QCOW2:
img.IgnitionPlatform = "qemu"
}
if kopts := customizations.GetKernel(); kopts != nil && kopts.Append != "" {
@ -659,10 +653,8 @@ func iotSimplifiedInstallerImage(workload workload.Workload,
rawImg.KernelOptionsAppend = []string{"modprobe.blacklist=vc4"}
rawImg.Keyboard = "us"
rawImg.Locale = "C.UTF-8"
if !common.VersionLessThan(t.arch.distro.osVersion, "38") {
rawImg.SysrootReadOnly = true
rawImg.KernelOptionsAppend = append(rawImg.KernelOptionsAppend, "rw")
}
rawImg.SysrootReadOnly = true
rawImg.KernelOptionsAppend = append(rawImg.KernelOptionsAppend, "rw")
rawImg.Platform = t.platform
rawImg.Workload = workload
@ -672,12 +664,10 @@ func iotSimplifiedInstallerImage(workload workload.Workload,
rawImg.OSName = "fedora"
rawImg.LockRoot = true
if !common.VersionLessThan(t.arch.distro.osVersion, "38") {
rawImg.IgnitionPlatform = "metal"
rawImg.KernelOptionsAppend = append(rawImg.KernelOptionsAppend, "coreos.no_persist_ip")
if bpIgnition := customizations.GetIgnition(); bpIgnition != nil && bpIgnition.FirstBoot != nil && bpIgnition.FirstBoot.ProvisioningURL != "" {
rawImg.KernelOptionsAppend = append(rawImg.KernelOptionsAppend, "ignition.config.url="+bpIgnition.FirstBoot.ProvisioningURL)
}
rawImg.IgnitionPlatform = "metal"
rawImg.KernelOptionsAppend = append(rawImg.KernelOptionsAppend, "coreos.no_persist_ip")
if bpIgnition := customizations.GetIgnition(); bpIgnition != nil && bpIgnition.FirstBoot != nil && bpIgnition.FirstBoot.ProvisioningURL != "" {
rawImg.KernelOptionsAppend = append(rawImg.KernelOptionsAppend, "ignition.config.url="+bpIgnition.FirstBoot.ProvisioningURL)
}
// TODO: move generation into LiveImage

View file

@ -224,11 +224,10 @@ func (t *imageType) Manifest(bp *blueprint.Blueprint,
containerSources := make([]container.SourceSpec, len(bp.Containers))
for idx, cont := range bp.Containers {
containerSources[idx] = container.SourceSpec{
Source: cont.Source,
Name: cont.Name,
TLSVerify: cont.TLSVerify,
ContainersTransport: cont.ContainersTransport,
StoragePath: cont.StoragePath,
Source: cont.Source,
Name: cont.Name,
TLSVerify: cont.TLSVerify,
Local: cont.LocalStorage,
}
}
@ -262,14 +261,6 @@ func (t *imageType) checkOptions(bp *blueprint.Blueprint, options distro.ImageOp
return nil, fmt.Errorf("embedding containers is not supported for %s on %s", t.name, t.arch.distro.name)
}
if len(bp.Containers) > 0 {
for _, container := range bp.Containers {
if err := container.Validate(); err != nil {
return nil, err
}
}
}
if options.OSTree != nil {
if err := options.OSTree.Validate(); err != nil {
return nil, err

View file

@ -4,7 +4,6 @@ package fedora
import (
"fmt"
"strconv"
"github.com/osbuild/images/internal/common"
"github.com/osbuild/images/pkg/arch"
@ -74,6 +73,9 @@ func vmdkCommonPackageSet(t *imageType) rpmmd.PackageSet {
func iotCommitPackageSet(t *imageType) rpmmd.PackageSet {
ps := rpmmd.PackageSet{
Include: []string{
"NetworkManager",
"NetworkManager-wifi",
"NetworkManager-wwan",
"aardvark-dns",
"atheros-firmware",
"attr",
@ -87,8 +89,8 @@ func iotCommitPackageSet(t *imageType) rpmmd.PackageSet {
"clevis-dracut",
"clevis-luks",
"clevis-pin-tpm2",
"containernetworking-plugins",
"container-selinux",
"containernetworking-plugins",
"coreutils",
"cracklib-dicts",
"criu",
@ -101,6 +103,8 @@ func iotCommitPackageSet(t *imageType) rpmmd.PackageSet {
"dracut-network",
"e2fsprogs",
"efibootmgr",
"fdo-client",
"fdo-owner-cli",
"fedora-iot-config",
"fedora-release-iot",
"firewalld",
@ -116,6 +120,7 @@ func iotCommitPackageSet(t *imageType) rpmmd.PackageSet {
"gzip",
"hostname",
"ignition",
"ignition-edge",
"ima-evm-utils",
"iproute",
"iputils",
@ -128,9 +133,6 @@ func iotCommitPackageSet(t *imageType) rpmmd.PackageSet {
"linux-firmware",
"lvm2",
"netavark",
"NetworkManager",
"NetworkManager-wifi",
"NetworkManager-wwan",
"nss-altfiles",
"openssh-clients",
"openssh-server",
@ -154,6 +156,7 @@ func iotCommitPackageSet(t *imageType) rpmmd.PackageSet {
"shadow-utils",
"skopeo",
"slirp4netns",
"ssh-key-dir",
"sssd-client",
"sudo",
"systemd",
@ -174,17 +177,6 @@ func iotCommitPackageSet(t *imageType) rpmmd.PackageSet {
},
}
if !common.VersionLessThan(t.arch.distro.osVersion, "38") {
ps = ps.Append(rpmmd.PackageSet{
Include: []string{
"fdo-client",
"fdo-owner-cli",
"ignition-edge",
"ssh-key-dir",
},
})
}
return ps
}
@ -498,13 +490,7 @@ func iotInstallerPackageSet(t *imageType) rpmmd.PackageSet {
// include anaconda packages
ps := anacondaPackageSet(t)
releasever := t.Arch().Distro().Releasever()
version, err := strconv.Atoi(releasever)
if err != nil {
panic("cannot convert releasever to int: " + err.Error())
}
if version >= 38 {
if common.VersionGreaterThanOrEqual(t.arch.distro.osVersion, "39") {
ps = ps.Append(rpmmd.PackageSet{
Include: []string{
"fedora-release-iot",
@ -545,8 +531,7 @@ func liveInstallerPackageSet(t *imageType) rpmmd.PackageSet {
},
}
// We want to generate a preview image when rawhide is built
if !common.VersionLessThan(t.arch.distro.osVersion, "39") {
if common.VersionGreaterThanOrEqual(t.arch.distro.osVersion, "39") {
ps = ps.Append(rpmmd.PackageSet{
Include: []string{
"anaconda-webui",
@ -560,14 +545,7 @@ func liveInstallerPackageSet(t *imageType) rpmmd.PackageSet {
func imageInstallerPackageSet(t *imageType) rpmmd.PackageSet {
ps := anacondaPackageSet(t)
releasever := t.Arch().Distro().Releasever()
version, err := strconv.Atoi(releasever)
if err != nil {
panic("cannot convert releasever to int: " + err.Error())
}
// We want to generate a preview image when rawhide is built
if version >= 38 {
if common.VersionGreaterThanOrEqual(t.arch.distro.osVersion, "39") {
ps = ps.Append(rpmmd.PackageSet{
Include: []string{
"anaconda-webui",

View file

@ -18,6 +18,7 @@ type ImageConfig struct {
Keyboard *osbuild.KeymapStageOptions
EnabledServices []string
DisabledServices []string
MaskedServices []string
DefaultTarget *string
Sysconfig []*osbuild.SysconfigStageOptions

View file

@ -63,6 +63,7 @@ func osCustomizations(
osc.EnabledServices = imageConfig.EnabledServices
osc.DisabledServices = imageConfig.DisabledServices
osc.MaskedServices = imageConfig.MaskedServices
if imageConfig.DefaultTarget != nil {
osc.DefaultTarget = *imageConfig.DefaultTarget
}

View file

@ -200,11 +200,10 @@ func (t *imageType) Manifest(bp *blueprint.Blueprint,
containerSources := make([]container.SourceSpec, len(bp.Containers))
for idx, cont := range bp.Containers {
containerSources[idx] = container.SourceSpec{
Source: cont.Source,
Name: cont.Name,
TLSVerify: cont.TLSVerify,
ContainersTransport: cont.ContainersTransport,
StoragePath: cont.StoragePath,
Source: cont.Source,
Name: cont.Name,
TLSVerify: cont.TLSVerify,
Local: cont.LocalStorage,
}
}

View file

@ -368,7 +368,7 @@ func ec2CommonPackageSet(t *imageType) rpmmd.PackageSet {
func rhelEc2CommonPackageSet(t *imageType) rpmmd.PackageSet {
ps := ec2CommonPackageSet(t)
// Include "redhat-cloud-client-configuration" on 8.7+ (COMPOSER-1804)
if !common.VersionLessThan(t.arch.distro.osVersion, "8.7") {
if common.VersionGreaterThanOrEqual(t.arch.distro.osVersion, "8.7") {
ps.Include = append(ps.Include, "redhat-cloud-client-configuration")
}
return ps

View file

@ -397,7 +397,7 @@ func newDistro(name string, minor int) *distribution {
)
if rd.isRHEL() {
if !common.VersionLessThan(rd.osVersion, "8.6") {
if common.VersionGreaterThanOrEqual(rd.osVersion, "8.6") {
// image types only available on 8.6 and later on RHEL
// These edge image types require FDO which aren't available on older versions
x86_64.addImageTypes(

View file

@ -71,6 +71,7 @@ func osCustomizations(
osc.EnabledServices = imageConfig.EnabledServices
osc.DisabledServices = imageConfig.DisabledServices
osc.MaskedServices = imageConfig.MaskedServices
if imageConfig.DefaultTarget != nil {
osc.DefaultTarget = *imageConfig.DefaultTarget
}

View file

@ -245,11 +245,10 @@ func (t *imageType) Manifest(bp *blueprint.Blueprint,
containerSources := make([]container.SourceSpec, len(bp.Containers))
for idx, cont := range bp.Containers {
containerSources[idx] = container.SourceSpec{
Source: cont.Source,
Name: cont.Name,
TLSVerify: cont.TLSVerify,
ContainersTransport: cont.ContainersTransport,
StoragePath: cont.StoragePath,
Source: cont.Source,
Name: cont.Name,
TLSVerify: cont.TLSVerify,
Local: cont.LocalStorage,
}
}
@ -296,14 +295,6 @@ func (t *imageType) checkOptions(bp *blueprint.Blueprint, options distro.ImageOp
return warnings, fmt.Errorf("embedding containers is not supported for %s on %s", t.name, t.arch.distro.name)
}
if len(bp.Containers) > 0 {
for _, container := range bp.Containers {
if err := container.Validate(); err != nil {
return nil, err
}
}
}
if options.OSTree != nil {
if err := options.OSTree.Validate(); err != nil {
return nil, err

View file

@ -356,7 +356,7 @@ func ec2CommonPackageSet(t *imageType) rpmmd.PackageSet {
func rhelEc2CommonPackageSet(t *imageType) rpmmd.PackageSet {
ps := ec2CommonPackageSet(t)
// Include "redhat-cloud-client-configuration" on 9.1+ (COMPOSER-1805)
if !common.VersionLessThan(t.arch.distro.osVersion, "9.1") {
if common.VersionGreaterThanOrEqual(t.arch.distro.osVersion, "9.1") {
ps.Include = append(ps.Include, "redhat-cloud-client-configuration")
}
return ps

View file

@ -589,7 +589,7 @@ func edgeCommitPackageSet(t *imageType) rpmmd.PackageSet {
ps = ps.Append(aarch64EdgeCommitPackageSet(t))
}
if !common.VersionLessThan(t.arch.distro.osVersion, "9.2") || !t.arch.distro.isRHEL() {
if common.VersionGreaterThanOrEqual(t.arch.distro.osVersion, "9.2") || !t.arch.distro.isRHEL() {
ps.Include = append(ps.Include, "ignition", "ignition-edge", "ssh-key-dir")
}

View file

@ -68,6 +68,7 @@ func osCustomizations(
osc.EnabledServices = imageConfig.EnabledServices
osc.DisabledServices = imageConfig.DisabledServices
osc.MaskedServices = imageConfig.MaskedServices
if imageConfig.DefaultTarget != nil {
osc.DefaultTarget = *imageConfig.DefaultTarget
}
@ -327,7 +328,7 @@ func edgeCommitImage(workload workload.Workload,
img.OSVersion = t.arch.distro.osVersion
img.Filename = t.Filename()
if !common.VersionLessThan(t.arch.distro.osVersion, "9.2") || !t.arch.distro.isRHEL() {
if common.VersionGreaterThanOrEqual(t.arch.distro.osVersion, "9.2") || !t.arch.distro.isRHEL() {
img.OSCustomizations.EnabledServices = append(img.OSCustomizations.EnabledServices, "ignition-firstboot-complete.service", "coreos-ignition-write-issues.service")
}
img.Environment = t.environment
@ -361,7 +362,7 @@ func edgeContainerImage(workload workload.Workload,
img.ExtraContainerPackages = packageSets[containerPkgsKey]
img.Filename = t.Filename()
if !common.VersionLessThan(t.arch.distro.osVersion, "9.2") || !t.arch.distro.isRHEL() {
if common.VersionGreaterThanOrEqual(t.arch.distro.osVersion, "9.2") || !t.arch.distro.isRHEL() {
img.OSCustomizations.EnabledServices = append(img.OSCustomizations.EnabledServices, "ignition-firstboot-complete.service", "coreos-ignition-write-issues.service")
}
@ -452,12 +453,12 @@ func edgeRawImage(workload workload.Workload,
}
img.Keyboard = "us"
img.Locale = "C.UTF-8"
if !common.VersionLessThan(t.arch.distro.osVersion, "9.2") || !t.arch.distro.isRHEL() {
if common.VersionGreaterThanOrEqual(t.arch.distro.osVersion, "9.2") || !t.arch.distro.isRHEL() {
img.SysrootReadOnly = true
img.KernelOptionsAppend = append(img.KernelOptionsAppend, "rw")
}
if !common.VersionLessThan(t.arch.distro.osVersion, "9.2") || !t.arch.distro.isRHEL() {
if common.VersionGreaterThanOrEqual(t.arch.distro.osVersion, "9.2") || !t.arch.distro.isRHEL() {
img.IgnitionPlatform = "metal"
img.KernelOptionsAppend = append(img.KernelOptionsAppend, "coreos.no_persist_ip")
if bpIgnition := customizations.GetIgnition(); bpIgnition != nil && bpIgnition.FirstBoot != nil && bpIgnition.FirstBoot.ProvisioningURL != "" {
@ -513,7 +514,7 @@ func edgeSimplifiedInstallerImage(workload workload.Workload,
rawImg.KernelOptionsAppend = []string{"modprobe.blacklist=vc4"}
rawImg.Keyboard = "us"
rawImg.Locale = "C.UTF-8"
if !common.VersionLessThan(t.arch.distro.osVersion, "9.2") || !t.arch.distro.isRHEL() {
if common.VersionGreaterThanOrEqual(t.arch.distro.osVersion, "9.2") || !t.arch.distro.isRHEL() {
rawImg.SysrootReadOnly = true
rawImg.KernelOptionsAppend = append(rawImg.KernelOptionsAppend, "rw")
}
@ -528,7 +529,7 @@ func edgeSimplifiedInstallerImage(workload workload.Workload,
rawImg.OSName = "redhat"
rawImg.LockRoot = true
if !common.VersionLessThan(t.arch.distro.osVersion, "9.2") || !t.arch.distro.isRHEL() {
if common.VersionGreaterThanOrEqual(t.arch.distro.osVersion, "9.2") || !t.arch.distro.isRHEL() {
rawImg.IgnitionPlatform = "metal"
rawImg.KernelOptionsAppend = append(rawImg.KernelOptionsAppend, "coreos.no_persist_ip")
if bpIgnition := customizations.GetIgnition(); bpIgnition != nil && bpIgnition.FirstBoot != nil && bpIgnition.FirstBoot.ProvisioningURL != "" {

View file

@ -250,11 +250,10 @@ func (t *imageType) Manifest(bp *blueprint.Blueprint,
containerSources := make([]container.SourceSpec, len(bp.Containers))
for idx, cont := range bp.Containers {
containerSources[idx] = container.SourceSpec{
Source: cont.Source,
Name: cont.Name,
TLSVerify: cont.TLSVerify,
ContainersTransport: cont.ContainersTransport,
StoragePath: cont.StoragePath,
Source: cont.Source,
Name: cont.Name,
TLSVerify: cont.TLSVerify,
Local: cont.LocalStorage,
}
}
@ -301,14 +300,6 @@ func (t *imageType) checkOptions(bp *blueprint.Blueprint, options distro.ImageOp
return warnings, fmt.Errorf("embedding containers is not supported for %s on %s", t.name, t.arch.distro.name)
}
if len(bp.Containers) > 0 {
for _, container := range bp.Containers {
if err := container.Validate(); err != nil {
return nil, err
}
}
}
if options.OSTree != nil {
if err := options.OSTree.Validate(); err != nil {
return nil, err

View file

@ -389,7 +389,7 @@ func (p *AnacondaInstallerISOTree) ostreeCommitStages() []*osbuild.Stage {
func (p *AnacondaInstallerISOTree) ostreeContainerStages() []*osbuild.Stage {
stages := make([]*osbuild.Stage, 0)
images := osbuild.NewContainersInputForSources([]container.Spec{*p.containerSpec})
image := osbuild.NewContainersInputForSingleSource(*p.containerSpec)
stages = append(stages, osbuild.NewMkdirStage(&osbuild.MkdirStageOptions{
Paths: []osbuild.MkdirStagePath{
@ -402,7 +402,7 @@ func (p *AnacondaInstallerISOTree) ostreeContainerStages() []*osbuild.Stage {
// copy the container in
stages = append(stages, osbuild.NewSkopeoStageWithOCI(
p.PayloadPath,
images,
image,
nil))
// do what we can in our kickstart stage
@ -439,13 +439,6 @@ func (p *AnacondaInstallerISOTree) ostreeContainerStages() []*osbuild.Stage {
// and what we can't do in a separate kickstart that we include
targetContainerTransport := "registry"
if p.containerSpec.ContainersTransport != nil {
targetContainerTransport = *p.containerSpec.ContainersTransport
}
// Canonicalize to registry, as that's what the bootc stack wants
if targetContainerTransport == "docker://" {
targetContainerTransport = "registry"
}
// Because osbuild core only supports a subset of options, we append to the
// base here with some more hardcoded defaults

View file

@ -1,6 +1,8 @@
package manifest
import (
"fmt"
"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/osbuild"
"github.com/osbuild/images/pkg/ostree"
@ -225,14 +227,19 @@ func (p *BuildrootFromContainer) serialize() osbuild.Pipeline {
if len(p.containerSpecs) == 0 {
panic("serialization not started")
}
if len(p.containerSpecs) != 1 {
panic(fmt.Sprintf("BuildrootFromContainer expectes exactly one container input, got: %v", p.containerSpecs))
}
pipeline := p.Base.serialize()
pipeline.Runner = p.runner.String()
inputs := osbuild.NewContainersInputForSources(p.containerSpecs)
image := osbuild.NewContainersInputForSingleSource(p.containerSpecs[0])
options := &osbuild.ContainerDeployOptions{
Exclude: []string{"/sysroot"},
}
stage, err := osbuild.NewContainerDeployStage(inputs, options)
stage, err := osbuild.NewContainerDeployStage(image, options)
if err != nil {
panic(err)
}

View file

@ -77,6 +77,7 @@ type OSCustomizations struct {
Timezone string
EnabledServices []string
DisabledServices []string
MaskedServices []string
DefaultTarget string
// SELinux policy, when set it enables the labeling of the tree with the
@ -405,21 +406,14 @@ func (p *OS) serialize() osbuild.Pipeline {
}
if len(p.containerSpecs) > 0 {
images := osbuild.NewContainersInputForSources(p.containerSpecs)
var storagePath string
if containerStore := p.OSCustomizations.ContainersStorage; containerStore != nil {
storagePath = *containerStore
storageConf := "/etc/containers/storage.conf"
containerStoreOpts := osbuild.NewContainerStorageOptions(storageConf, storagePath)
pipeline.AddStage(osbuild.NewContainersStorageConfStage(containerStoreOpts))
}
manifests := osbuild.NewFilesInputForManifestLists(p.containerSpecs)
skopeo := osbuild.NewSkopeoStageWithContainersStorage(storagePath, images, manifests)
pipeline.AddStage(skopeo)
for _, stage := range osbuild.GenContainerStorageStages(storagePath, p.containerSpecs) {
pipeline.AddStage(stage)
}
}
pipeline.AddStage(osbuild.NewLocaleStage(&osbuild.LocaleStageOptions{Language: p.Language}))
@ -706,8 +700,10 @@ func (p *OS) serialize() osbuild.Pipeline {
enabledServices := []string{}
disabledServices := []string{}
maskedServices := []string{}
enabledServices = append(enabledServices, p.EnabledServices...)
disabledServices = append(disabledServices, p.DisabledServices...)
maskedServices = append(maskedServices, p.MaskedServices...)
if p.Environment != nil {
enabledServices = append(enabledServices, p.Environment.GetServices()...)
}
@ -716,10 +712,12 @@ func (p *OS) serialize() osbuild.Pipeline {
disabledServices = append(disabledServices, p.Workload.GetDisabledServices()...)
}
if len(enabledServices) != 0 ||
len(disabledServices) != 0 || p.DefaultTarget != "" {
len(disabledServices) != 0 ||
len(maskedServices) != 0 || p.DefaultTarget != "" {
pipeline.AddStage(osbuild.NewSystemdStage(&osbuild.SystemdStageOptions{
EnabledServices: enabledServices,
DisabledServices: disabledServices,
MaskedServices: maskedServices,
DefaultTarget: p.DefaultTarget,
}))
}

View file

@ -228,7 +228,6 @@ func (p *OSTreeDeployment) doOSTreeSpec(pipeline *osbuild.Pipeline, repoPath str
}
func (p *OSTreeDeployment) doOSTreeContainerSpec(pipeline *osbuild.Pipeline, repoPath string, kernelOpts []string) string {
cont := *p.containerSpec
ref := p.ref
var targetImgref string
@ -249,7 +248,8 @@ func (p *OSTreeDeployment) doOSTreeContainerSpec(pipeline *osbuild.Pipeline, rep
Label: "root",
},
}
images := osbuild.NewContainersInputForSources([]container.Spec{cont})
images := osbuild.NewContainersInputForSingleSource(*p.containerSpec)
pipeline.AddStage(osbuild.NewOSTreeDeployContainerStage(options, images))
return ref
}

View file

@ -0,0 +1,29 @@
package osbuild
import (
"github.com/osbuild/images/pkg/container"
)
func GenContainerStorageStages(storagePath string, containerSpecs []container.Spec) (stages []*Stage) {
if storagePath != "" {
storageConf := "/etc/containers/storage.conf"
containerStoreOpts := NewContainerStorageOptions(storageConf, storagePath)
stages = append(stages, NewContainersStorageConfStage(containerStoreOpts))
}
images := NewContainersInputForSources(containerSpecs)
localImages := NewLocalContainersInputForSources(containerSpecs)
if len(images.References) > 0 {
manifests := NewFilesInputForManifestLists(containerSpecs)
stages = append(stages, NewSkopeoStageWithContainersStorage(storagePath, images, manifests))
}
if len(localImages.References) > 0 {
stages = append(stages, NewSkopeoStageWithContainersStorage(storagePath, localImages, nil))
}
return stages
}

View file

@ -13,22 +13,50 @@ type ContainersInput struct {
References map[string]ContainersInputSourceRef `json:"references"`
}
func NewContainersInputForSources(containers []container.Spec) ContainersInput {
func (c ContainersInput) isStageInputs() {}
func newContainersInputForSources(containers []container.Spec, forLocal bool) ContainersInput {
refs := make(map[string]ContainersInputSourceRef, len(containers))
for _, c := range containers {
if forLocal != c.LocalStorage {
continue
}
ref := ContainersInputSourceRef{
Name: c.LocalName,
}
refs[c.ImageID] = ref
}
var sourceType string
if forLocal {
sourceType = "org.osbuild.containers-storage"
} else {
sourceType = "org.osbuild.containers"
}
return ContainersInput{
References: refs,
inputCommon: inputCommon{
Type: "org.osbuild.containers",
Type: sourceType,
Origin: InputOriginSource,
},
}
}
func (c ContainersInput) isStageInputs() {}
func NewContainersInputForSources(containers []container.Spec) ContainersInput {
return newContainersInputForSources(containers, false)
}
func NewLocalContainersInputForSources(containers []container.Spec) ContainersInput {
return newContainersInputForSources(containers, true)
}
// NewContainersInputForSingleSource will return a containers input for a
// single container spec. It will automatically select the right local or
// remote input.
func NewContainersInputForSingleSource(spec container.Spec) ContainersInput {
if spec.LocalStorage {
return NewLocalContainersInputForSources([]container.Spec{spec})
}
return NewContainersInputForSources([]container.Spec{spec})
}

View file

@ -0,0 +1,22 @@
package osbuild
import "fmt"
type ContainersStorageSource struct {
Items map[string]struct{} `json:"items"`
}
func (ContainersStorageSource) isSource() {}
func NewContainersStorageSource() *ContainersStorageSource {
return &ContainersStorageSource{
Items: make(map[string]struct{}),
}
}
func (source *ContainersStorageSource) AddItem(id string) {
if !skopeoDigestPattern.MatchString(id) {
panic(fmt.Errorf("item %#v has invalid image id", id))
}
source.Items[id] = struct{}{}
}

View file

@ -11,38 +11,20 @@ type SkopeoIndexSource struct {
func (SkopeoIndexSource) isSource() {}
type SkopeoIndexSourceImage struct {
Name string `json:"name"`
TLSVerify *bool `json:"tls-verify,omitempty"`
ContainersTransport *string `json:"containers-transport,omitempty"`
StorageLocation *string `json:"storage-location,omitempty"`
Name string `json:"name"`
TLSVerify *bool `json:"tls-verify,omitempty"`
}
type SkopeoIndexSourceItem struct {
Image SkopeoIndexSourceImage `json:"image"`
}
func validateTransport(transport *string) error {
if transport == nil {
return nil
}
if *transport != DockerTransport && *transport != ContainersStorageTransport {
return fmt.Errorf("invalid container transport: %s", *transport)
}
return nil
}
func (item SkopeoIndexSourceItem) validate() error {
if item.Image.Name == "" {
return fmt.Errorf("source item has empty name")
}
if err := validateTransport(item.Image.ContainersTransport); err != nil {
return err
}
return nil
}
@ -55,13 +37,11 @@ func NewSkopeoIndexSource() *SkopeoIndexSource {
// AddItem adds a source item to the source; will panic
// if any of the supplied options are invalid or missing
func (source *SkopeoIndexSource) AddItem(name, image string, tlsVerify *bool, containersTransport *string, storageLocation *string) {
func (source *SkopeoIndexSource) AddItem(name, image string, tlsVerify *bool) {
item := SkopeoIndexSourceItem{
Image: SkopeoIndexSourceImage{
Name: name,
TLSVerify: tlsVerify,
ContainersTransport: containersTransport,
StorageLocation: storageLocation,
Name: name,
TLSVerify: tlsVerify,
},
}

View file

@ -17,11 +17,9 @@ type SkopeoSource struct {
func (SkopeoSource) isSource() {}
type SkopeopSourceImage struct {
Name string `json:"name,omitempty"`
Digest string `json:"digest,omitempty"`
TLSVerify *bool `json:"tls-verify,omitempty"`
ContainersTransport *string `json:"containers-transport,omitempty"`
StorageLocation *string `json:"storage-location,omitempty"`
Name string `json:"name,omitempty"`
Digest string `json:"digest,omitempty"`
TLSVerify *bool `json:"tls-verify,omitempty"`
}
type SkopeoSourceItem struct {
@ -29,14 +27,12 @@ type SkopeoSourceItem struct {
}
// NewSkopeoSourceItem creates a new source item for name and digest
func NewSkopeoSourceItem(name, digest string, tlsVerify *bool, containersTransport *string, storageLocation *string) SkopeoSourceItem {
func NewSkopeoSourceItem(name, digest string, tlsVerify *bool) SkopeoSourceItem {
item := SkopeoSourceItem{
Image: SkopeopSourceImage{
Name: name,
Digest: digest,
TLSVerify: tlsVerify,
ContainersTransport: containersTransport,
StorageLocation: storageLocation,
Name: name,
Digest: digest,
TLSVerify: tlsVerify,
},
}
if err := item.validate(); err != nil {
@ -54,10 +50,6 @@ func (item SkopeoSourceItem) validate() error {
return fmt.Errorf("source item %#v has invalid digest", item)
}
if err := validateTransport(item.Image.ContainersTransport); err != nil {
return err
}
return nil
}
@ -70,8 +62,8 @@ func NewSkopeoSource() *SkopeoSource {
// AddItem adds a source item to the source; will panic
// if any of the supplied options are invalid or missing
func (source *SkopeoSource) AddItem(name, digest, image string, tlsVerify *bool, containersTransport *string, storageLocation *string) {
item := NewSkopeoSourceItem(name, digest, tlsVerify, containersTransport, storageLocation)
func (source *SkopeoSource) AddItem(name, digest, image string, tlsVerify *bool) {
item := NewSkopeoSourceItem(name, digest, tlsVerify)
if !skopeoDigestPattern.MatchString(image) {
panic(fmt.Errorf("item %#v has invalid image id", image))
}

View file

@ -90,16 +90,20 @@ func GenSources(packages []rpmmd.PackageSpec, ostreeCommits []ostree.CommitSpec,
sources["org.osbuild.inline"] = ils
}
// collect skopeo container sources
// collect skopeo and local container sources
if len(containers) > 0 {
skopeo := NewSkopeoSource()
skopeoIndex := NewSkopeoIndexSource()
localContainers := NewContainersStorageSource()
for _, c := range containers {
skopeo.AddItem(c.Source, c.Digest, c.ImageID, c.TLSVerify, c.ContainersTransport, c.StoragePath)
// if we have a list digest, add a skopeo-index source as well
if c.ListDigest != "" {
skopeoIndex.AddItem(c.Source, c.ListDigest, c.TLSVerify, c.ContainersTransport, c.StoragePath)
if c.LocalStorage {
localContainers.AddItem(c.ImageID)
} else {
skopeo.AddItem(c.Source, c.Digest, c.ImageID, c.TLSVerify)
// if we have a list digest, add a skopeo-index source as well
if c.ListDigest != "" {
skopeoIndex.AddItem(c.Source, c.ListDigest, c.TLSVerify)
}
}
}
if len(skopeo.Items) > 0 {
@ -108,6 +112,9 @@ func GenSources(packages []rpmmd.PackageSpec, ostreeCommits []ostree.CommitSpec,
if len(skopeoIndex.Items) > 0 {
sources["org.osbuild.skopeo-index"] = skopeoIndex
}
if len(localContainers.Items) > 0 {
sources["org.osbuild.containers-storage"] = localContainers
}
}
return sources, nil