osbuild-worker: use the new ostree resolver API

This commit is contained in:
Lukas Zapletal 2024-10-31 15:31:16 +01:00 committed by Achilleas Koutsou
parent f291f41dbc
commit 64f479092d
27 changed files with 318 additions and 136 deletions

View file

@ -23,7 +23,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/osbuild/images/pkg/distro" "github.com/osbuild/images/pkg/platform"
"github.com/osbuild/images/pkg/rpmmd" "github.com/osbuild/images/pkg/rpmmd"
"github.com/osbuild/osbuild-composer/internal/upload/koji" "github.com/osbuild/osbuild-composer/internal/upload/koji"
) )
@ -182,7 +182,7 @@ func TestKojiImport(t *testing.T) {
Extra: &koji.BuildOutputExtra{ Extra: &koji.BuildOutputExtra{
ImageOutput: koji.ImageExtraInfo{ ImageOutput: koji.ImageExtraInfo{
Arch: "noarch", Arch: "noarch",
BootMode: distro.BOOT_LEGACY.String(), BootMode: platform.BOOT_LEGACY.String(),
}, },
}, },
}, },

View file

@ -8,7 +8,7 @@ import (
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/osbuild/images/pkg/distro" "github.com/osbuild/images/pkg/platform"
"github.com/osbuild/images/pkg/rpmmd" "github.com/osbuild/images/pkg/rpmmd"
"github.com/osbuild/osbuild-composer/internal/upload/koji" "github.com/osbuild/osbuild-composer/internal/upload/koji"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -101,7 +101,7 @@ func main() {
Extra: &koji.BuildOutputExtra{ Extra: &koji.BuildOutputExtra{
ImageOutput: koji.ImageExtraInfo{ ImageOutput: koji.ImageExtraInfo{
Arch: arch, Arch: arch,
BootMode: distro.BOOT_NONE.String(), // TODO: put the correct boot mode here BootMode: platform.BOOT_NONE.String(), // TODO: put the correct boot mode here
}, },
}, },
}, },

View file

@ -516,14 +516,19 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
} }
} }
// Both curl and ostree input share the same MTLS config
if impl.RepositoryMTLSConfig != nil { if impl.RepositoryMTLSConfig != nil {
if impl.RepositoryMTLSConfig.CA != "" { if impl.RepositoryMTLSConfig.CA != "" {
extraEnv = append(extraEnv, fmt.Sprintf("OSBUILD_SOURCES_CURL_SSL_CA_CERT=%s", impl.RepositoryMTLSConfig.CA)) extraEnv = append(extraEnv, fmt.Sprintf("OSBUILD_SOURCES_CURL_SSL_CA_CERT=%s", impl.RepositoryMTLSConfig.CA))
extraEnv = append(extraEnv, fmt.Sprintf("OSBUILD_SOURCES_OSTREE_SSL_CA_CERT=%s", impl.RepositoryMTLSConfig.CA))
} }
extraEnv = append(extraEnv, fmt.Sprintf("OSBUILD_SOURCES_CURL_SSL_CLIENT_KEY=%s", impl.RepositoryMTLSConfig.MTLSClientKey)) extraEnv = append(extraEnv, fmt.Sprintf("OSBUILD_SOURCES_CURL_SSL_CLIENT_KEY=%s", impl.RepositoryMTLSConfig.MTLSClientKey))
extraEnv = append(extraEnv, fmt.Sprintf("OSBUILD_SOURCES_CURL_SSL_CLIENT_CERT=%s", impl.RepositoryMTLSConfig.MTLSClientCert)) extraEnv = append(extraEnv, fmt.Sprintf("OSBUILD_SOURCES_CURL_SSL_CLIENT_CERT=%s", impl.RepositoryMTLSConfig.MTLSClientCert))
extraEnv = append(extraEnv, fmt.Sprintf("OSBUILD_SOURCES_OSTREE_SSL_CLIENT_KEY=%s", impl.RepositoryMTLSConfig.MTLSClientKey))
extraEnv = append(extraEnv, fmt.Sprintf("OSBUILD_SOURCES_OSTREE_SSL_CLIENT_CERT=%s", impl.RepositoryMTLSConfig.MTLSClientCert))
if impl.RepositoryMTLSConfig.Proxy != nil { if impl.RepositoryMTLSConfig.Proxy != nil {
extraEnv = append(extraEnv, fmt.Sprintf("OSBUILD_SOURCES_CURL_PROXY=%s", impl.RepositoryMTLSConfig.Proxy.String())) extraEnv = append(extraEnv, fmt.Sprintf("OSBUILD_SOURCES_CURL_PROXY=%s", impl.RepositoryMTLSConfig.Proxy.String()))
extraEnv = append(extraEnv, fmt.Sprintf("OSBUILD_SOURCES_OSTREE_PROXY=%s", impl.RepositoryMTLSConfig.Proxy.String()))
} }
} }

View file

@ -2,6 +2,8 @@ package main
import ( import (
"fmt" "fmt"
"net/url"
"strings"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -11,6 +13,26 @@ import (
) )
type OSTreeResolveJobImpl struct { type OSTreeResolveJobImpl struct {
RepositoryMTLSConfig *RepositoryMTLSConfig
}
func (job *OSTreeResolveJobImpl) CompareBaseURL(baseURLStr string) (bool, error) {
baseURL, err := url.Parse(baseURLStr)
if err != nil {
return false, err
}
if baseURL.Scheme != job.RepositoryMTLSConfig.BaseURL.Scheme {
return false, nil
}
if baseURL.Host != job.RepositoryMTLSConfig.BaseURL.Host {
return false, nil
}
if !strings.HasPrefix(baseURL.Path, job.RepositoryMTLSConfig.BaseURL.Path) {
return false, nil
}
return true, nil
} }
func setError(err error, result *worker.OSTreeResolveJobResult) { func setError(err error, result *worker.OSTreeResolveJobResult) {
@ -51,7 +73,25 @@ func (impl *OSTreeResolveJobImpl) Run(job worker.Job) error {
logWithId.Infof("Resolving (%d) ostree commits", len(args.Specs)) logWithId.Infof("Resolving (%d) ostree commits", len(args.Specs))
for i, s := range args.Specs { for i, s := range args.Specs {
reqParams := ostree.SourceSpec(s) reqParams := ostree.SourceSpec{}
reqParams.URL = s.URL
reqParams.Ref = s.Ref
if match, err := impl.CompareBaseURL(s.URL); match && err == nil {
reqParams.Proxy = impl.RepositoryMTLSConfig.Proxy.String()
reqParams.MTLS = &ostree.MTLS{
CA: impl.RepositoryMTLSConfig.CA,
ClientCert: impl.RepositoryMTLSConfig.MTLSClientCert,
ClientKey: impl.RepositoryMTLSConfig.MTLSClientKey,
}
} else if err != nil {
logWithId.Errorf("Error comparing base URL: %v", err)
result.JobError = clienterrors.New(
clienterrors.ErrorInvalidRepositoryURL,
"Repository URL is malformed",
err.Error(),
)
break
}
commitSpec, err := ostree.Resolve(reqParams) commitSpec, err := ostree.Resolve(reqParams)
if err != nil { if err != nil {
logWithId.Infof("Resolving ostree params failed: %v", err) logWithId.Infof("Resolving ostree params failed: %v", err)

View file

@ -7,13 +7,14 @@ import (
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
slogger "github.com/osbuild/osbuild-composer/pkg/splunk_logger"
"net/url" "net/url"
"os" "os"
"path" "path"
"strings" "strings"
"time" "time"
slogger "github.com/osbuild/osbuild-composer/pkg/splunk_logger"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -508,8 +509,10 @@ func main() {
worker.JobTypeContainerResolve: &ContainerResolveJobImpl{ worker.JobTypeContainerResolve: &ContainerResolveJobImpl{
AuthFilePath: containersAuthFilePath, AuthFilePath: containersAuthFilePath,
}, },
worker.JobTypeOSTreeResolve: &OSTreeResolveJobImpl{}, worker.JobTypeOSTreeResolve: &OSTreeResolveJobImpl{
worker.JobTypeFileResolve: &FileResolveJobImpl{}, RepositoryMTLSConfig: repositoryMTLSConfig,
},
worker.JobTypeFileResolve: &FileResolveJobImpl{},
worker.JobTypeAWSEC2Copy: &AWSEC2CopyJobImpl{ worker.JobTypeAWSEC2Copy: &AWSEC2CopyJobImpl{
AWSCreds: awsCredentials, AWSCreds: awsCredentials,
}, },

2
go.mod
View file

@ -46,7 +46,7 @@ require (
github.com/labstack/gommon v0.4.2 github.com/labstack/gommon v0.4.2
github.com/openshift-online/ocm-sdk-go v0.1.438 github.com/openshift-online/ocm-sdk-go v0.1.438
github.com/oracle/oci-go-sdk/v54 v54.0.0 github.com/oracle/oci-go-sdk/v54 v54.0.0
github.com/osbuild/images v0.95.0 github.com/osbuild/images v0.96.0
github.com/osbuild/osbuild-composer/pkg/splunk_logger v0.0.0-20240814102216-0239db53236d github.com/osbuild/osbuild-composer/pkg/splunk_logger v0.0.0-20240814102216-0239db53236d
github.com/osbuild/pulp-client v0.1.0 github.com/osbuild/pulp-client v0.1.0
github.com/prometheus/client_golang v1.20.2 github.com/prometheus/client_golang v1.20.2

4
go.sum
View file

@ -534,8 +534,8 @@ github.com/openshift-online/ocm-sdk-go v0.1.438 h1:tsLCCUzbLCTL4RZG02y9RuopmGCXp
github.com/openshift-online/ocm-sdk-go v0.1.438/go.mod h1:CiAu2jwl3ITKOxkeV0Qnhzv4gs35AmpIzVABQLtcI2Y= github.com/openshift-online/ocm-sdk-go v0.1.438/go.mod h1:CiAu2jwl3ITKOxkeV0Qnhzv4gs35AmpIzVABQLtcI2Y=
github.com/oracle/oci-go-sdk/v54 v54.0.0 h1:CDLjeSejv2aDpElAJrhKpi6zvT/zhZCZuXchUUZ+LS4= github.com/oracle/oci-go-sdk/v54 v54.0.0 h1:CDLjeSejv2aDpElAJrhKpi6zvT/zhZCZuXchUUZ+LS4=
github.com/oracle/oci-go-sdk/v54 v54.0.0/go.mod h1:+t+yvcFGVp+3ZnztnyxqXfQDsMlq8U25faBLa+mqCMc= github.com/oracle/oci-go-sdk/v54 v54.0.0/go.mod h1:+t+yvcFGVp+3ZnztnyxqXfQDsMlq8U25faBLa+mqCMc=
github.com/osbuild/images v0.95.0 h1:WWxYEQKD9wFGs/zkWF4wd3IDwNColZwzKsQh/+dwvUw= github.com/osbuild/images v0.96.0 h1:ZieK4i5pyKTdLaA/EwxeNEQsWBLEkX3FsZVyIaYCJKI=
github.com/osbuild/images v0.95.0/go.mod h1:4bNmMQOVadIKVC1q8zsLO8tdEQFH90zIp+MQBQUnCiE= github.com/osbuild/images v0.96.0/go.mod h1:4bNmMQOVadIKVC1q8zsLO8tdEQFH90zIp+MQBQUnCiE=
github.com/osbuild/osbuild-composer/pkg/splunk_logger v0.0.0-20240814102216-0239db53236d h1:r9BFPDv0uuA9k1947Jybcxs36c/pTywWS1gjeizvtcQ= github.com/osbuild/osbuild-composer/pkg/splunk_logger v0.0.0-20240814102216-0239db53236d h1:r9BFPDv0uuA9k1947Jybcxs36c/pTywWS1gjeizvtcQ=
github.com/osbuild/osbuild-composer/pkg/splunk_logger v0.0.0-20240814102216-0239db53236d/go.mod h1:zR1iu/hOuf+OQNJlk70tju9IqzzM4ycq0ectkFBm94U= github.com/osbuild/osbuild-composer/pkg/splunk_logger v0.0.0-20240814102216-0239db53236d/go.mod h1:zR1iu/hOuf+OQNJlk70tju9IqzzM4ycq0ectkFBm94U=
github.com/osbuild/pulp-client v0.1.0 h1:L0C4ezBJGTamN3BKdv+rKLuq/WxXJbsFwz/Hj7aEmJ8= github.com/osbuild/pulp-client v0.1.0 h1:L0C4ezBJGTamN3BKdv+rKLuq/WxXJbsFwz/Hj7aEmJ8=

View file

@ -10,6 +10,7 @@ import (
"github.com/osbuild/images/pkg/disk" "github.com/osbuild/images/pkg/disk"
"github.com/osbuild/images/pkg/distro" "github.com/osbuild/images/pkg/distro"
"github.com/osbuild/images/pkg/ostree" "github.com/osbuild/images/pkg/ostree"
"github.com/osbuild/images/pkg/platform"
"github.com/osbuild/osbuild-composer/internal/blueprint" "github.com/osbuild/osbuild-composer/internal/blueprint"
"github.com/osbuild/osbuild-composer/internal/cloud/gcp" "github.com/osbuild/osbuild-composer/internal/cloud/gcp"
"github.com/osbuild/osbuild-composer/internal/common" "github.com/osbuild/osbuild-composer/internal/common"
@ -56,11 +57,11 @@ func newAWSTarget(options UploadOptions, imageType distro.ImageType) (*target.Ta
var amiBootMode *string var amiBootMode *string
switch imageType.BootMode() { switch imageType.BootMode() {
case distro.BOOT_HYBRID: case platform.BOOT_HYBRID:
amiBootMode = common.ToPtr(string(ec2types.BootModeValuesUefiPreferred)) amiBootMode = common.ToPtr(string(ec2types.BootModeValuesUefiPreferred))
case distro.BOOT_UEFI: case platform.BOOT_UEFI:
amiBootMode = common.ToPtr(string(ec2types.BootModeValuesUefi)) amiBootMode = common.ToPtr(string(ec2types.BootModeValuesUefi))
case distro.BOOT_LEGACY: case platform.BOOT_LEGACY:
amiBootMode = common.ToPtr(string(ec2types.BootModeValuesLegacyBios)) amiBootMode = common.ToPtr(string(ec2types.BootModeValuesLegacyBios))
} }

View file

@ -228,7 +228,12 @@ func (s *Server) enqueueCompose(irs []imageRequest, channel string) (uuid.UUID,
workerResolveSpecs := make([]worker.OSTreeResolveSpec, len(sources)) workerResolveSpecs := make([]worker.OSTreeResolveSpec, len(sources))
for idx, source := range sources { for idx, source := range sources {
// ostree.SourceSpec is directly convertible to worker.OSTreeResolveSpec // ostree.SourceSpec is directly convertible to worker.OSTreeResolveSpec
workerResolveSpecs[idx] = worker.OSTreeResolveSpec(source) workerResolveSpecs[idx] = worker.OSTreeResolveSpec{
URL: source.URL,
Ref: source.Ref,
RHSM: source.RHSM,
}
} }
jobID, err := s.workers.EnqueueOSTreeResolveJob(&worker.OSTreeResolveJob{Specs: workerResolveSpecs}, channel) jobID, err := s.workers.EnqueueOSTreeResolveJob(&worker.OSTreeResolveJob{Specs: workerResolveSpecs}, channel)
if err != nil { if err != nil {
@ -356,7 +361,11 @@ func (s *Server) enqueueKojiCompose(taskID uint64, server, name, version, releas
workerResolveSpecs := make([]worker.OSTreeResolveSpec, len(sources)) workerResolveSpecs := make([]worker.OSTreeResolveSpec, len(sources))
for idx, source := range sources { for idx, source := range sources {
// ostree.SourceSpec is directly convertible to worker.OSTreeResolveSpec // ostree.SourceSpec is directly convertible to worker.OSTreeResolveSpec
workerResolveSpecs[idx] = worker.OSTreeResolveSpec(source) workerResolveSpecs[idx] = worker.OSTreeResolveSpec{
URL: source.URL,
Ref: source.Ref,
RHSM: source.RHSM,
}
} }
jobID, err := s.workers.EnqueueOSTreeResolveJob(&worker.OSTreeResolveJob{Specs: workerResolveSpecs}, channel) jobID, err := s.workers.EnqueueOSTreeResolveJob(&worker.OSTreeResolveJob{Specs: workerResolveSpecs}, channel)
if err != nil { if err != nil {

View file

@ -2393,6 +2393,7 @@ func (api *API) resolveOSTreeCommits(sourceSpecs map[string][]ostree.SourceSpec,
Checksum: checksum, Checksum: checksum,
} }
} else { } else {
// MTLS not supported on-prem
commit, err := ostree.Resolve(source) commit, err := ostree.Resolve(source)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -10,6 +10,7 @@ import (
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/osbuild/images/pkg/distro" "github.com/osbuild/images/pkg/distro"
"github.com/osbuild/images/pkg/platform"
"github.com/osbuild/osbuild-composer/internal/cloud/gcp" "github.com/osbuild/osbuild-composer/internal/cloud/gcp"
"github.com/osbuild/osbuild-composer/internal/common" "github.com/osbuild/osbuild-composer/internal/common"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -283,11 +284,11 @@ func uploadRequestToTarget(u uploadRequest, imageType distro.ImageType) *target.
var amiBootMode *string var amiBootMode *string
switch imageType.BootMode() { switch imageType.BootMode() {
case distro.BOOT_HYBRID: case platform.BOOT_HYBRID:
amiBootMode = common.ToPtr(string(ec2types.BootModeValuesUefiPreferred)) amiBootMode = common.ToPtr(string(ec2types.BootModeValuesUefiPreferred))
case distro.BOOT_UEFI: case platform.BOOT_UEFI:
amiBootMode = common.ToPtr(string(ec2types.BootModeValuesUefi)) amiBootMode = common.ToPtr(string(ec2types.BootModeValuesUefi))
case distro.BOOT_LEGACY: case platform.BOOT_LEGACY:
amiBootMode = common.ToPtr(string(ec2types.BootModeValuesLegacyBios)) amiBootMode = common.ToPtr(string(ec2types.BootModeValuesLegacyBios))
} }

View file

@ -19,6 +19,7 @@ package disk
import ( import (
"encoding/hex" "encoding/hex"
"fmt"
"io" "io"
"math/rand" "math/rand"
"reflect" "reflect"
@ -61,6 +62,54 @@ const (
DosFat16B = "06" DosFat16B = "06"
) )
// FSType is the filesystem type enum.
//
// There should always be one value for each filesystem type supported by
// osbuild stages (stages/org.osbuild.mkfs.*) and the unset/none value.
type FSType uint64
const (
FS_NONE FSType = iota
FS_VFAT
FS_EXT4
FS_XFS
FS_BTRFS
)
func (f FSType) String() string {
switch f {
case FS_NONE:
return ""
case FS_VFAT:
return "vfat"
case FS_EXT4:
return "ext4"
case FS_XFS:
return "xfs"
case FS_BTRFS:
return "btrfs"
default:
panic(fmt.Sprintf("unknown or unsupported filesystem type with enum value %d", f))
}
}
func NewFSType(s string) (FSType, error) {
switch s {
case "":
return FS_NONE, nil
case "vfat":
return FS_VFAT, nil
case "ext4":
return FS_EXT4, nil
case "xfs":
return FS_XFS, nil
case "btrfs":
return FS_BTRFS, nil
default:
return FS_NONE, fmt.Errorf("unknown or unsupported filesystem type name: %s", s)
}
}
// Entity is the base interface for all disk-related entities. // Entity is the base interface for all disk-related entities.
type Entity interface { type Entity interface {
// Clone returns a deep copy of the entity. // Clone returns a deep copy of the entity.
@ -205,3 +254,24 @@ func NewVolIDFromRand(r *rand.Rand) string {
} }
return hex.EncodeToString(volid) return hex.EncodeToString(volid)
} }
// genUniqueString returns a string based on base that does does not exist in
// the existing set. If the base itself does not exist, it is returned as is,
// otherwise a two digit number is added and incremented until a unique string
// is found.
// This function is mimicking what blivet does for avoiding name collisions.
// See blivet/blivet.py#L1060 commit 2eb4bd4
func genUniqueString(base string, existing map[string]bool) (string, error) {
if !existing[base] {
return base, nil
}
for i := 0; i < 100; i++ {
uniq := fmt.Sprintf("%s%02d", base, i)
if !existing[uniq] {
return uniq, nil
}
}
return "", fmt.Errorf("name collision: could not generate unique version of %q", base)
}

View file

@ -81,41 +81,49 @@ func (vg *LVMVolumeGroup) CreateMountpoint(mountpoint string, size uint64) (Enti
FSTabPassNo: 0, FSTabPassNo: 0,
} }
return vg.CreateLogicalVolume(mountpoint, size, &filesystem) // leave lv name empty to autogenerate based on mountpoint
return vg.CreateLogicalVolume("", size, &filesystem)
} }
func (vg *LVMVolumeGroup) CreateLogicalVolume(lvName string, size uint64, payload Entity) (Entity, error) { // genLVName generates a valid logical volume name from a mountpoint or base
if vg == nil { // that does not conflict with existing ones.
panic("LVMVolumeGroup.CreateLogicalVolume: nil entity") func (vg *LVMVolumeGroup) genLVName(base string) (string, error) {
}
names := make(map[string]bool, len(vg.LogicalVolumes)) names := make(map[string]bool, len(vg.LogicalVolumes))
for _, lv := range vg.LogicalVolumes { for _, lv := range vg.LogicalVolumes {
names[lv.Name] = true names[lv.Name] = true
} }
base := lvname(lvName) base = lvname(base) // if the mountpoint is used (i.e. if the base contains /), sanitize it and append 'lv'
var exists bool
name := base
// Make sure that we don't collide with an existing volume, e.g. 'home/test' // Make sure that we don't collide with an existing volume, e.g.
// and /home/test_test would collide. We try 100 times and then give up. This // 'home/test' and /home_test would collide.
// is mimicking what blivet does. See blivet/blivet.py#L1060 commit 2eb4bd4 return genUniqueString(base, names)
for i := 0; i < 100; i++ { }
exists = names[name]
if !exists {
break
}
name = fmt.Sprintf("%s%02d", base, i) // CreateLogicalVolume creates a new logical volume on the volume group. If a
// name is not provided, a valid one is generated based on the payload
// mountpoint. If a name is provided, it is used directly without validating.
func (vg *LVMVolumeGroup) CreateLogicalVolume(lvName string, size uint64, payload Entity) (*LVMLogicalVolume, error) {
if vg == nil {
panic("LVMVolumeGroup.CreateLogicalVolume: nil entity")
} }
if exists { if lvName == "" {
return nil, fmt.Errorf("could not create logical volume: name collision") // generate a name based on the payload's mountpoint
mntble, ok := payload.(Mountable)
if !ok {
return nil, fmt.Errorf("could not create logical volume: no name provided and payload is not mountable")
}
mountpoint := mntble.GetMountpoint()
autoName, err := vg.genLVName(mountpoint)
if err != nil {
return nil, err
}
lvName = autoName
} }
lv := LVMLogicalVolume{ lv := LVMLogicalVolume{
Name: name, Name: lvName,
Size: vg.AlignUp(size), Size: vg.AlignUp(size),
Payload: payload, Payload: payload,
} }

View file

@ -715,7 +715,7 @@ func (pt *PartitionTable) ensureLVM() error {
// create root logical volume on the new volume group with the same // create root logical volume on the new volume group with the same
// size and filesystem as the previous root partition // size and filesystem as the previous root partition
_, err := vg.CreateLogicalVolume("root", part.Size, filesystem) _, err := vg.CreateLogicalVolume("rootlv", part.Size, filesystem)
if err != nil { if err != nil {
panic(fmt.Sprintf("Could not create LV: %v", err)) panic(fmt.Sprintf("Could not create LV: %v", err))
} }

View file

@ -6,36 +6,16 @@ import (
"github.com/osbuild/images/pkg/disk" "github.com/osbuild/images/pkg/disk"
"github.com/osbuild/images/pkg/manifest" "github.com/osbuild/images/pkg/manifest"
"github.com/osbuild/images/pkg/ostree" "github.com/osbuild/images/pkg/ostree"
"github.com/osbuild/images/pkg/platform"
"github.com/osbuild/images/pkg/rhsm/facts" "github.com/osbuild/images/pkg/rhsm/facts"
"github.com/osbuild/images/pkg/rpmmd" "github.com/osbuild/images/pkg/rpmmd"
) )
type BootMode uint64
const ( const (
BOOT_NONE BootMode = iota
BOOT_LEGACY
BOOT_UEFI
BOOT_HYBRID
UnsupportedCustomizationError = "unsupported blueprint customizations found for image type %q: (allowed: %s)" UnsupportedCustomizationError = "unsupported blueprint customizations found for image type %q: (allowed: %s)"
NoCustomizationsAllowedError = "image type %q does not support customizations" NoCustomizationsAllowedError = "image type %q does not support customizations"
) )
func (m BootMode) String() string {
switch m {
case BOOT_NONE:
return "none"
case BOOT_LEGACY:
return "legacy"
case BOOT_UEFI:
return "uefi"
case BOOT_HYBRID:
return "hybrid"
default:
panic("invalid boot mode")
}
}
// A Distro represents composer's notion of what a given distribution is. // A Distro represents composer's notion of what a given distribution is.
type Distro interface { type Distro interface {
// Returns the name of the distro. // Returns the name of the distro.
@ -121,7 +101,7 @@ type ImageType interface {
PartitionType() string PartitionType() string
// Returns the corresponding boot mode ("legacy", "uefi", "hybrid") or "none" // Returns the corresponding boot mode ("legacy", "uefi", "hybrid") or "none"
BootMode() BootMode BootMode() platform.BootMode
// Returns the names of the pipelines that set up the build environment (buildroot). // Returns the names of the pipelines that set up the build environment (buildroot).
BuildPipelines() []string BuildPipelines() []string

View file

@ -126,15 +126,15 @@ func (t *imageType) Exports() []string {
return []string{"assembler"} return []string{"assembler"}
} }
func (t *imageType) BootMode() distro.BootMode { func (t *imageType) BootMode() platform.BootMode {
if t.platform.GetUEFIVendor() != "" && t.platform.GetBIOSPlatform() != "" { if t.platform.GetUEFIVendor() != "" && t.platform.GetBIOSPlatform() != "" {
return distro.BOOT_HYBRID return platform.BOOT_HYBRID
} else if t.platform.GetUEFIVendor() != "" { } else if t.platform.GetUEFIVendor() != "" {
return distro.BOOT_UEFI return platform.BOOT_UEFI
} else if t.platform.GetBIOSPlatform() != "" || t.platform.GetZiplSupport() { } else if t.platform.GetBIOSPlatform() != "" || t.platform.GetZiplSupport() {
return distro.BOOT_LEGACY return platform.BOOT_LEGACY
} }
return distro.BOOT_NONE return platform.BOOT_NONE
} }
func (t *imageType) getPartitionTable( func (t *imageType) getPartitionTable(

View file

@ -168,15 +168,15 @@ func (t *ImageType) Exports() []string {
return []string{"assembler"} return []string{"assembler"}
} }
func (t *ImageType) BootMode() distro.BootMode { func (t *ImageType) BootMode() platform.BootMode {
if t.platform.GetUEFIVendor() != "" && t.platform.GetBIOSPlatform() != "" { if t.platform.GetUEFIVendor() != "" && t.platform.GetBIOSPlatform() != "" {
return distro.BOOT_HYBRID return platform.BOOT_HYBRID
} else if t.platform.GetUEFIVendor() != "" { } else if t.platform.GetUEFIVendor() != "" {
return distro.BOOT_UEFI return platform.BOOT_UEFI
} else if t.platform.GetBIOSPlatform() != "" || t.platform.GetZiplSupport() { } else if t.platform.GetBIOSPlatform() != "" || t.platform.GetZiplSupport() {
return distro.BOOT_LEGACY return platform.BOOT_LEGACY
} }
return distro.BOOT_NONE return platform.BOOT_NONE
} }
func (t *ImageType) GetPartitionTable( func (t *ImageType) GetPartitionTable(

View file

@ -529,7 +529,6 @@ var defaultAzureImageConfig = &distro.ImageConfig{
"nm-cloud-setup.service", "nm-cloud-setup.service",
"nm-cloud-setup.timer", "nm-cloud-setup.timer",
"sshd", "sshd",
"systemd-resolved",
"waagent", "waagent",
}, },
SshdConfig: &osbuild.SshdConfigStageOptions{ SshdConfig: &osbuild.SshdConfigStageOptions{

View file

@ -9,6 +9,7 @@ import (
"github.com/osbuild/images/pkg/distro" "github.com/osbuild/images/pkg/distro"
"github.com/osbuild/images/pkg/manifest" "github.com/osbuild/images/pkg/manifest"
"github.com/osbuild/images/pkg/ostree" "github.com/osbuild/images/pkg/ostree"
"github.com/osbuild/images/pkg/platform"
"github.com/osbuild/images/pkg/policies" "github.com/osbuild/images/pkg/policies"
"github.com/osbuild/images/pkg/rpmmd" "github.com/osbuild/images/pkg/rpmmd"
) )
@ -210,8 +211,8 @@ func (t *TestImageType) PartitionType() string {
return "" return ""
} }
func (t *TestImageType) BootMode() distro.BootMode { func (t *TestImageType) BootMode() platform.BootMode {
return distro.BOOT_HYBRID return platform.BOOT_HYBRID
} }
func (t *TestImageType) BuildPipelines() []string { func (t *TestImageType) BuildPipelines() []string {

View file

@ -3,9 +3,7 @@ package image
import ( import (
"fmt" "fmt"
"math/rand" "math/rand"
"regexp"
"github.com/osbuild/images/internal/common"
"github.com/osbuild/images/pkg/container" "github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/customizations/users" "github.com/osbuild/images/pkg/customizations/users"
"github.com/osbuild/images/pkg/disk" "github.com/osbuild/images/pkg/disk"
@ -92,15 +90,7 @@ func (img *BootcDiskImage) InstantiateManifestFromContainers(m *manifest.Manifes
fmt.Sprintf("%s.vhd", fileBasename), fmt.Sprintf("%s.vhd", fileBasename),
} }
// XXX: copied from https://github.com/osbuild/images/blob/v0.85.0/pkg/image/disk.go#L102 gcePipeline := newGCETarPipelineForImg(buildPipeline, rawImage, "gce")
gcePipeline := manifest.NewTar(buildPipeline, rawImage, "gce")
gcePipeline.Format = osbuild.TarArchiveFormatOldgnu
gcePipeline.RootNode = osbuild.TarRootNodeOmit
// these are required to successfully import the image to GCP
gcePipeline.ACLs = common.ToPtr(false)
gcePipeline.SELinux = common.ToPtr(false)
gcePipeline.Xattrs = common.ToPtr(false)
gcePipeline.Transform = fmt.Sprintf(`s/%s/disk.raw/`, regexp.QuoteMeta(rawImage.Filename()))
gcePipeline.SetFilename("image.tar.gz") gcePipeline.SetFilename("image.tar.gz")
return nil return nil

View file

@ -6,7 +6,6 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/osbuild/images/internal/common"
"github.com/osbuild/images/internal/environment" "github.com/osbuild/images/internal/environment"
"github.com/osbuild/images/internal/workload" "github.com/osbuild/images/internal/workload"
"github.com/osbuild/images/pkg/artifact" "github.com/osbuild/images/pkg/artifact"
@ -103,13 +102,7 @@ func (img *DiskImage) InstantiateManifest(m *manifest.Manifest,
// NOTE(akoutsou): temporary workaround; filename required for GCP // NOTE(akoutsou): temporary workaround; filename required for GCP
// TODO: define internal raw filename on image type // TODO: define internal raw filename on image type
rawImagePipeline.SetFilename("disk.raw") rawImagePipeline.SetFilename("disk.raw")
tarPipeline := manifest.NewTar(buildPipeline, rawImagePipeline, "archive") tarPipeline := newGCETarPipelineForImg(buildPipeline, rawImagePipeline, "archive")
tarPipeline.Format = osbuild.TarArchiveFormatOldgnu
tarPipeline.RootNode = osbuild.TarRootNodeOmit
// these are required to successfully import the image to GCP
tarPipeline.ACLs = common.ToPtr(false)
tarPipeline.SELinux = common.ToPtr(false)
tarPipeline.Xattrs = common.ToPtr(false)
tarPipeline.SetFilename(img.Filename) // filename extension will determine compression tarPipeline.SetFilename(img.Filename) // filename extension will determine compression
imagePipeline = tarPipeline imagePipeline = tarPipeline
default: default:

24
vendor/github.com/osbuild/images/pkg/image/gce.go generated vendored Normal file
View file

@ -0,0 +1,24 @@
package image
import (
"fmt"
"regexp"
"github.com/osbuild/images/internal/common"
"github.com/osbuild/images/pkg/manifest"
"github.com/osbuild/images/pkg/osbuild"
)
func newGCETarPipelineForImg(buildPipeline manifest.Build, inputPipeline manifest.FilePipeline, pipelinename string) *manifest.Tar {
tarPipeline := manifest.NewTar(buildPipeline, inputPipeline, pipelinename)
tarPipeline.Format = osbuild.TarArchiveFormatOldgnu
tarPipeline.RootNode = osbuild.TarRootNodeOmit
// these are required to successfully import the image to GCP
tarPipeline.ACLs = common.ToPtr(false)
tarPipeline.SELinux = common.ToPtr(false)
tarPipeline.Xattrs = common.ToPtr(false)
if inputPipeline.Filename() != "disk.raw" {
tarPipeline.Transform = fmt.Sprintf(`s/%s/disk.raw/`, regexp.QuoteMeta(inputPipeline.Filename()))
}
return tarPipeline
}

View file

@ -236,7 +236,9 @@ func (p *BuildrootFromContainer) serialize() osbuild.Pipeline {
pipeline.Runner = p.runner.String() pipeline.Runner = p.runner.String()
image := osbuild.NewContainersInputForSingleSource(p.containerSpecs[0]) image := osbuild.NewContainersInputForSingleSource(p.containerSpecs[0])
stage, err := osbuild.NewContainerDeployStage(image, &osbuild.ContainerDeployOptions{}) // Make skopeo copy to remove the signatures of signed containers by default to workaround
// build failures until https://github.com/containers/image/issues/2599 is implemented
stage, err := osbuild.NewContainerDeployStage(image, &osbuild.ContainerDeployOptions{RemoveSignatures: true})
if err != nil { if err != nil {
panic(err) panic(err)
} }

View file

@ -9,7 +9,8 @@ type ContainerDeployInputs struct {
func (ContainerDeployInputs) isStageInputs() {} func (ContainerDeployInputs) isStageInputs() {}
type ContainerDeployOptions struct { type ContainerDeployOptions struct {
Exclude []string `json:"exclude,omitempty"` Exclude []string `json:"exclude,omitempty"`
RemoveSignatures bool `json:"remove-signatures,omitempty"`
} }
func (ContainerDeployOptions) isStageOptions() {} func (ContainerDeployOptions) isStageOptions() {}

View file

@ -25,9 +25,21 @@ var (
// SourceSpec serves as input for ResolveParams, and contains all necessary // SourceSpec serves as input for ResolveParams, and contains all necessary
// variables to resolve a ref, which can then be turned into a CommitSpec. // variables to resolve a ref, which can then be turned into a CommitSpec.
type SourceSpec struct { type SourceSpec struct {
URL string URL string
Ref string Ref string
// RHSM indicates to use RHSM secrets when pulling from the remote. Alternatively, you can use MTLS with plain certs.
RHSM bool RHSM bool
// MTLS information. Will be ignored if RHSM is set.
MTLS *MTLS
// Proxy as HTTP proxy to use when fetching the ref.
Proxy string
}
// MTLS contains the options for resolving an ostree source.
type MTLS struct {
CA string
ClientCert string
ClientKey string
} }
// CommitSpec specifies an ostree commit using any combination of Ref (branch), URL (source), and Checksum (commit ID). // CommitSpec specifies an ostree commit using any combination of Ref (branch), URL (source), and Checksum (commit ID).
@ -138,59 +150,53 @@ func verifyChecksum(commit string) bool {
return len(commit) > 0 && ostreeCommitRE.MatchString(commit) return len(commit) > 0 && ostreeCommitRE.MatchString(commit)
} }
// ResolveRef resolves the URL path specified by the location and ref // resolveRef resolves the URL path specified by the location and ref
// (location+"refs/heads/"+ref) and returns the commit ID for the named ref. If // (location+"refs/heads/"+ref) and returns the commit ID for the named ref. If
// there is an error, it will be of type ResolveRefError. // there is an error, it will be of type ResolveRefError.
func ResolveRef(location, ref string, consumerCerts bool, subs *rhsm.Subscriptions, ca *string) (string, error) { func resolveRef(ss SourceSpec) (string, error) {
u, err := url.Parse(location) u, err := url.Parse(ss.URL)
if err != nil { if err != nil {
return "", NewResolveRefError("error parsing ostree repository location: %v", err) return "", NewResolveRefError("error parsing ostree repository location: %v", err)
} }
u.Path = path.Join(u.Path, "refs/heads/", ref) u.Path = path.Join(u.Path, "refs/heads/", ss.Ref)
var client *http.Client
if consumerCerts {
if subs == nil {
subs, err = rhsm.LoadSystemSubscriptions()
if err != nil {
return "", NewResolveRefError("error adding rhsm certificates when resolving ref: %s", err)
}
if subs.Consumer == nil {
return "", NewResolveRefError("error adding rhsm certificates when resolving ref")
}
}
transport := http.DefaultTransport.(*http.Transport).Clone()
client := &http.Client{
Transport: transport,
Timeout: 300 * time.Second,
}
if u.Scheme == "https" {
tlsConf := &tls.Config{ tlsConf := &tls.Config{
MinVersion: tls.VersionTLS12, MinVersion: tls.VersionTLS12,
} }
if ca != nil { // If CA is set, load the CA certificate and add it to the TLS configuration. Otherwise, use the system CA.
caCertPEM, err := os.ReadFile(*ca) if ss.MTLS.CA != "" {
caCertPEM, err := os.ReadFile(ss.MTLS.CA)
if err != nil { if err != nil {
return "", NewResolveRefError("error adding rhsm certificates when resolving ref: %s", err) return "", NewResolveRefError("error adding ca certificate when resolving ref: %s", err)
} }
roots := x509.NewCertPool() tlsConf.RootCAs = x509.NewCertPool()
ok := roots.AppendCertsFromPEM(caCertPEM) if ok := tlsConf.RootCAs.AppendCertsFromPEM(caCertPEM); !ok {
if !ok { return "", NewResolveRefError("error adding ca certificate when resolving ref")
return "", NewResolveRefError("error adding rhsm certificates when resolving ref")
} }
tlsConf.RootCAs = roots
} }
cert, err := tls.LoadX509KeyPair(subs.Consumer.ConsumerCert, subs.Consumer.ConsumerKey) if ss.MTLS.ClientCert != "" && ss.MTLS.ClientKey != "" {
if err != nil { cert, err := tls.LoadX509KeyPair(ss.MTLS.ClientCert, ss.MTLS.ClientKey)
return "", NewResolveRefError("error adding rhsm certificates when resolving ref: %s", err) if err != nil {
return "", NewResolveRefError("error adding client certificate when resolving ref: %s", err)
}
tlsConf.Certificates = []tls.Certificate{cert}
} }
tlsConf.Certificates = []tls.Certificate{cert}
client = &http.Client{ transport.TLSClientConfig = tlsConf
Transport: &http.Transport{ }
TLSClientConfig: tlsConf,
}, if ss.Proxy != "" {
Timeout: 300 * time.Second, transport.Proxy = func(request *http.Request) (*url.URL, error) {
return url.Parse(ss.Proxy)
} }
} else {
client = &http.Client{}
} }
req, err := http.NewRequest(http.MethodGet, u.String(), nil) req, err := http.NewRequest(http.MethodGet, u.String(), nil)
@ -234,8 +240,31 @@ func Resolve(source SourceSpec) (CommitSpec, error) {
URL: source.URL, URL: source.URL,
} }
if source.RHSM && source.MTLS != nil {
return commit, NewResolveRefError("cannot use both RHSM and MTLS when resolving ref")
}
if source.RHSM { if source.RHSM {
var subs *rhsm.Subscriptions
var err error
commit.Secrets = "org.osbuild.rhsm.consumer" commit.Secrets = "org.osbuild.rhsm.consumer"
subs, err = rhsm.LoadSystemSubscriptions()
if err != nil {
return commit, NewResolveRefError("error adding rhsm certificates when resolving ref: %s", err)
}
if subs.Consumer == nil {
return commit, NewResolveRefError("error adding rhsm certificates when resolving ref")
}
source.MTLS = &MTLS{
ClientCert: subs.Consumer.ConsumerCert,
ClientKey: subs.Consumer.ConsumerKey,
}
} else if source.MTLS != nil {
commit.Secrets = "org.osbuild.mtls"
} }
if verifyChecksum(source.Ref) { if verifyChecksum(source.Ref) {
@ -252,7 +281,7 @@ func Resolve(source SourceSpec) (CommitSpec, error) {
// URL set: Resolve checksum // URL set: Resolve checksum
if source.URL != "" { if source.URL != "" {
// If a URL is specified, we need to fetch the commit at the URL. // If a URL is specified, we need to fetch the commit at the URL.
checksum, err := ResolveRef(source.URL, source.Ref, source.RHSM, nil, nil) checksum, err := resolveRef(source)
if err != nil { if err != nil {
return CommitSpec{}, err // ResolveRefError return CommitSpec{}, err // ResolveRefError
} }

View file

@ -0,0 +1,25 @@
package platform
type BootMode uint64
const (
BOOT_NONE BootMode = iota
BOOT_LEGACY
BOOT_UEFI
BOOT_HYBRID
)
func (m BootMode) String() string {
switch m {
case BOOT_NONE:
return "none"
case BOOT_LEGACY:
return "legacy"
case BOOT_UEFI:
return "uefi"
case BOOT_HYBRID:
return "hybrid"
default:
panic("invalid boot mode")
}
}

2
vendor/modules.txt vendored
View file

@ -1022,7 +1022,7 @@ github.com/oracle/oci-go-sdk/v54/identity
github.com/oracle/oci-go-sdk/v54/objectstorage github.com/oracle/oci-go-sdk/v54/objectstorage
github.com/oracle/oci-go-sdk/v54/objectstorage/transfer github.com/oracle/oci-go-sdk/v54/objectstorage/transfer
github.com/oracle/oci-go-sdk/v54/workrequests github.com/oracle/oci-go-sdk/v54/workrequests
# github.com/osbuild/images v0.95.0 # github.com/osbuild/images v0.96.0
## explicit; go 1.21.0 ## explicit; go 1.21.0
github.com/osbuild/images/internal/common github.com/osbuild/images/internal/common
github.com/osbuild/images/internal/environment github.com/osbuild/images/internal/environment