go.mod: update osbuild/images (v0.172.0) and osbuild/blueprint (v1.12.0)
The internal blueprint implementation has been removed from osbuild/images. Conversion from osbuild/blueprint blueprints to osbuild/images blueprints is no longer necessary.
This commit is contained in:
parent
19dd832876
commit
8d9f52cf1f
35 changed files with 44 additions and 2465 deletions
|
|
@ -10,8 +10,8 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/osbuild/blueprint/pkg/blueprint"
|
||||
"github.com/osbuild/images/pkg/arch"
|
||||
"github.com/osbuild/images/pkg/blueprint"
|
||||
"github.com/osbuild/images/pkg/distro"
|
||||
"github.com/osbuild/images/pkg/distrofactory"
|
||||
"github.com/osbuild/images/pkg/dnfjson"
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@ import (
|
|||
)
|
||||
|
||||
func getManifest(bp blueprint.Blueprint, t distro.ImageType, a distro.Arch, d distro.Distro, cacheDir string, repos []rpmmd.RepoConfig) (manifest.OSBuildManifest, []rpmmd.PackageSpec) {
|
||||
ibp := blueprint.Convert(bp)
|
||||
manifest, _, err := t.Manifest(&ibp, distro.ImageOptions{}, repos, nil)
|
||||
manifest, _, err := t.Manifest(&bp, distro.ImageOptions{}, repos, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
|||
4
go.mod
4
go.mod
|
|
@ -37,8 +37,8 @@ require (
|
|||
github.com/oapi-codegen/runtime v1.1.2
|
||||
github.com/openshift-online/ocm-sdk-go v0.1.473
|
||||
github.com/oracle/oci-go-sdk/v54 v54.0.0
|
||||
github.com/osbuild/blueprint v1.11.0
|
||||
github.com/osbuild/images v0.171.0
|
||||
github.com/osbuild/blueprint v1.12.0
|
||||
github.com/osbuild/images v0.172.0
|
||||
github.com/osbuild/osbuild-composer/pkg/splunk_logger v0.0.0-20240814102216-0239db53236d
|
||||
github.com/osbuild/pulp-client v0.1.0
|
||||
github.com/prometheus/client_golang v1.23.0
|
||||
|
|
|
|||
8
go.sum
8
go.sum
|
|
@ -520,10 +520,10 @@ github.com/openshift-online/ocm-sdk-go v0.1.473 h1:m/NWIBCzhC/8PototMQ7x8MQXCeSL
|
|||
github.com/openshift-online/ocm-sdk-go v0.1.473/go.mod h1:5Gw/YZE+c5FAPaBtO1w/asd9qbs2ljQwg7fpVq51UW4=
|
||||
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/osbuild/blueprint v1.11.0 h1:Crqt+RRSE84JOoajzTIGrQaXXxnAgGUCDYe3nump54g=
|
||||
github.com/osbuild/blueprint v1.11.0/go.mod h1:uknOfX/bAoi+dbeNJj+uAir1T++/LVEtoY8HO3U7MiQ=
|
||||
github.com/osbuild/images v0.171.0 h1:7lfYqIJUYh6QM6ioLW3cYLAzIu8lqPX5aGreyzEwRV8=
|
||||
github.com/osbuild/images v0.171.0/go.mod h1:uZAQRhxUB5G9aAczIcgU2d9VxLude4OXEdvtnKNgAEs=
|
||||
github.com/osbuild/blueprint v1.12.0 h1:Q2VXPyOnRs9uqgH1lNsvob6PS+73oPF0K9FmsyC98RI=
|
||||
github.com/osbuild/blueprint v1.12.0/go.mod h1:HPlJzkEl7q5g8hzaGksUk7ifFAy9QFw9LmzhuFOAVm4=
|
||||
github.com/osbuild/images v0.172.0 h1:UccT9dK7P5325HavVEYg4kG96GDqxQT4MTxC0mC1rdU=
|
||||
github.com/osbuild/images v0.172.0/go.mod h1:Iz2dCTJOrKBjiwp6mt24m3hqLWkfVmg8ZvrhAQbgV9g=
|
||||
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/pulp-client v0.1.0 h1:L0C4ezBJGTamN3BKdv+rKLuq/WxXJbsFwz/Hj7aEmJ8=
|
||||
|
|
|
|||
|
|
@ -572,6 +572,6 @@ func TestComposeUnsupportedMountPointV0(t *testing.T) {
|
|||
require.NoError(t, err, "failed with a client error")
|
||||
require.NotNil(t, resp)
|
||||
require.Equal(t, "ManifestCreationFailed", resp.Errors[0].ID)
|
||||
require.Contains(t, resp.Errors[0].Msg, "path \"/etc\" is not allowed")
|
||||
require.Contains(t, resp.Errors[0].Msg, "The following custom mountpoints are not supported [\"/etc\"]")
|
||||
require.Equal(t, 0, len(body))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import (
|
|||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/osbuild/blueprint/pkg/blueprint"
|
||||
"github.com/osbuild/images/pkg/distrofactory"
|
||||
"github.com/osbuild/images/pkg/reporegistry"
|
||||
"github.com/osbuild/images/pkg/rpmmd"
|
||||
|
|
@ -82,8 +81,7 @@ func (request *DepsolveRequest) Depsolve(df *distrofactory.Factory, rr *reporegi
|
|||
manifestSeed: manifestSeed,
|
||||
}
|
||||
|
||||
ibp := blueprint.Convert(bp)
|
||||
manifestSource, _, err := ir.imageType.Manifest(&ibp, ir.imageOptions, ir.repositories, &ir.manifestSeed)
|
||||
manifestSource, _, err := ir.imageType.Manifest(&bp, ir.imageOptions, ir.repositories, &ir.manifestSeed)
|
||||
if err != nil {
|
||||
return nil, HTTPErrorWithInternal(ErrorFailedToDepsolve, err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
|
||||
"github.com/osbuild/osbuild-composer/pkg/jobqueue"
|
||||
|
||||
"github.com/osbuild/blueprint/pkg/blueprint"
|
||||
"github.com/osbuild/images/pkg/container"
|
||||
"github.com/osbuild/images/pkg/distrofactory"
|
||||
"github.com/osbuild/images/pkg/dnfjson"
|
||||
|
|
@ -160,12 +159,11 @@ func (s *Server) enqueueCompose(irs []imageRequest, channel string) (uuid.UUID,
|
|||
}
|
||||
ir := irs[0]
|
||||
|
||||
ibp := blueprint.Convert(ir.blueprint)
|
||||
// shortcuts
|
||||
arch := ir.imageType.Arch()
|
||||
distribution := arch.Distro()
|
||||
|
||||
manifestSource, _, err := ir.imageType.Manifest(&ibp, ir.imageOptions, ir.repositories, &ir.manifestSeed)
|
||||
manifestSource, _, err := ir.imageType.Manifest(&ir.blueprint, ir.imageOptions, ir.repositories, &ir.manifestSeed)
|
||||
if err != nil {
|
||||
logrus.Warningf("ErrorEnqueueingJob, failed generating manifest: %v", err)
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
|
||||
|
|
@ -292,13 +290,12 @@ func (s *Server) enqueueKojiCompose(taskID uint64, server, name, version, releas
|
|||
var kojiFilenames []string
|
||||
var buildIDs []uuid.UUID
|
||||
for idx, ir := range irs {
|
||||
ibp := blueprint.Convert(ir.blueprint)
|
||||
|
||||
// shortcuts
|
||||
arch := ir.imageType.Arch()
|
||||
distribution := arch.Distro()
|
||||
|
||||
manifestSource, _, err := ir.imageType.Manifest(&ibp, ir.imageOptions, ir.repositories, &irs[idx].manifestSeed)
|
||||
manifestSource, _, err := ir.imageType.Manifest(&ir.blueprint, ir.imageOptions, ir.repositories, &irs[idx].manifestSeed)
|
||||
if err != nil {
|
||||
logrus.Errorf("ErrorEnqueueingJob, failed generating manifest: %v", err)
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
|
||||
|
|
|
|||
|
|
@ -57,8 +57,7 @@ func (suite *storeTest) SetupSuite() {
|
|||
suite.NoError(err)
|
||||
suite.myImageType, err = suite.myArch.GetImageType(test_distro.TestImageTypeName)
|
||||
suite.NoError(err)
|
||||
ibp := blueprint.Convert(suite.myBP)
|
||||
manifest, _, _ := suite.myImageType.Manifest(&ibp, suite.myImageOptions, suite.myRepoConfig, nil)
|
||||
manifest, _, _ := suite.myImageType.Manifest(&suite.myBP, suite.myImageOptions, suite.myRepoConfig, nil)
|
||||
suite.myManifest, _ = manifest.Serialize(nil, nil, nil, nil)
|
||||
suite.mySourceConfig = SourceConfig{
|
||||
Name: "testSourceConfig",
|
||||
|
|
|
|||
|
|
@ -2563,8 +2563,7 @@ func (api *API) composeHandler(writer http.ResponseWriter, request *http.Request
|
|||
return
|
||||
}
|
||||
|
||||
ibp := blueprint.Convert(*bp)
|
||||
manifest, warnings, err := imageType.Manifest(&ibp, options, imageRepos, &seed)
|
||||
manifest, warnings, err := imageType.Manifest(bp, options, imageRepos, &seed)
|
||||
if err != nil {
|
||||
errors := responseError{
|
||||
ID: "ManifestCreationFailed",
|
||||
|
|
|
|||
284
vendor/github.com/osbuild/blueprint/pkg/blueprint/blueprint.go
generated
vendored
284
vendor/github.com/osbuild/blueprint/pkg/blueprint/blueprint.go
generated
vendored
|
|
@ -5,11 +5,9 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/osbuild/blueprint/internal/common"
|
||||
"github.com/osbuild/images/pkg/crypt"
|
||||
|
||||
"github.com/coreos/go-semver/semver"
|
||||
iblueprint "github.com/osbuild/images/pkg/blueprint"
|
||||
)
|
||||
|
||||
// A Blueprint is a high-level description of an image.
|
||||
|
|
@ -229,285 +227,3 @@ func (b *Blueprint) CryptPasswords() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert(bp Blueprint) iblueprint.Blueprint {
|
||||
var pkgs []iblueprint.Package
|
||||
if len(bp.Packages) > 0 {
|
||||
pkgs = make([]iblueprint.Package, len(bp.Packages))
|
||||
for idx := range bp.Packages {
|
||||
pkgs[idx] = iblueprint.Package(bp.Packages[idx])
|
||||
}
|
||||
}
|
||||
|
||||
var modules []iblueprint.Package
|
||||
if len(bp.Modules) > 0 {
|
||||
modules = make([]iblueprint.Package, len(bp.Modules))
|
||||
for idx := range bp.Modules {
|
||||
modules[idx] = iblueprint.Package(bp.Modules[idx])
|
||||
}
|
||||
}
|
||||
|
||||
var enabledModules []iblueprint.EnabledModule
|
||||
if len(bp.EnabledModules) > 0 {
|
||||
enabledModules = make([]iblueprint.EnabledModule, len(bp.EnabledModules))
|
||||
for idx := range bp.EnabledModules {
|
||||
enabledModules[idx] = iblueprint.EnabledModule(bp.EnabledModules[idx])
|
||||
}
|
||||
}
|
||||
|
||||
var groups []iblueprint.Group
|
||||
if len(bp.Groups) > 0 {
|
||||
groups = make([]iblueprint.Group, len(bp.Groups))
|
||||
for idx := range bp.Groups {
|
||||
groups[idx] = iblueprint.Group(bp.Groups[idx])
|
||||
}
|
||||
}
|
||||
|
||||
var containers []iblueprint.Container
|
||||
|
||||
if len(bp.Containers) > 0 {
|
||||
containers = make([]iblueprint.Container, len(bp.Containers))
|
||||
for idx := range bp.Containers {
|
||||
containers[idx] = iblueprint.Container(bp.Containers[idx])
|
||||
}
|
||||
}
|
||||
|
||||
var customizations *iblueprint.Customizations
|
||||
if c := bp.Customizations; c != nil {
|
||||
customizations = &iblueprint.Customizations{
|
||||
Hostname: c.Hostname,
|
||||
InstallationDevice: c.InstallationDevice,
|
||||
}
|
||||
|
||||
if fdo := c.FDO; fdo != nil {
|
||||
ifdo := iblueprint.FDOCustomization(*fdo)
|
||||
customizations.FDO = &ifdo
|
||||
}
|
||||
if oscap := c.OpenSCAP; oscap != nil {
|
||||
ioscap := iblueprint.OpenSCAPCustomization{
|
||||
DataStream: oscap.DataStream,
|
||||
ProfileID: oscap.ProfileID,
|
||||
}
|
||||
if tailoring := oscap.Tailoring; tailoring != nil {
|
||||
itailoring := iblueprint.OpenSCAPTailoringCustomizations(*tailoring)
|
||||
ioscap.Tailoring = &itailoring
|
||||
}
|
||||
customizations.OpenSCAP = &ioscap
|
||||
}
|
||||
if ign := c.Ignition; ign != nil {
|
||||
iign := iblueprint.IgnitionCustomization{}
|
||||
if embed := ign.Embedded; embed != nil {
|
||||
iembed := iblueprint.EmbeddedIgnitionCustomization(*embed)
|
||||
iign.Embedded = &iembed
|
||||
}
|
||||
if fb := ign.FirstBoot; fb != nil {
|
||||
ifb := iblueprint.FirstBootIgnitionCustomization(*fb)
|
||||
iign.FirstBoot = &ifb
|
||||
}
|
||||
customizations.Ignition = &iign
|
||||
}
|
||||
if dirs := c.Directories; dirs != nil {
|
||||
idirs := make([]iblueprint.DirectoryCustomization, len(dirs))
|
||||
for idx := range dirs {
|
||||
idirs[idx] = iblueprint.DirectoryCustomization(dirs[idx])
|
||||
}
|
||||
customizations.Directories = idirs
|
||||
}
|
||||
if files := c.Files; files != nil {
|
||||
ifiles := make([]iblueprint.FileCustomization, len(files))
|
||||
for idx := range files {
|
||||
ifiles[idx] = iblueprint.FileCustomization(files[idx])
|
||||
}
|
||||
customizations.Files = ifiles
|
||||
}
|
||||
if repos := c.Repositories; repos != nil {
|
||||
irepos := make([]iblueprint.RepositoryCustomization, len(repos))
|
||||
for idx := range repos {
|
||||
irepos[idx] = iblueprint.RepositoryCustomization(repos[idx])
|
||||
}
|
||||
customizations.Repositories = irepos
|
||||
}
|
||||
if kernel := c.Kernel; kernel != nil {
|
||||
ikernel := iblueprint.KernelCustomization(*kernel)
|
||||
customizations.Kernel = &ikernel
|
||||
}
|
||||
if users := c.GetUsers(); users != nil { // contains both user customizations and converted sshkey customizations
|
||||
iusers := make([]iblueprint.UserCustomization, len(users))
|
||||
for idx := range users {
|
||||
iusers[idx] = iblueprint.UserCustomization(users[idx])
|
||||
}
|
||||
customizations.User = iusers
|
||||
}
|
||||
if groups := c.Group; groups != nil {
|
||||
igroups := make([]iblueprint.GroupCustomization, len(groups))
|
||||
for idx := range groups {
|
||||
igroups[idx] = iblueprint.GroupCustomization(groups[idx])
|
||||
}
|
||||
customizations.Group = igroups
|
||||
}
|
||||
if fs := c.Filesystem; fs != nil {
|
||||
ifs := make([]iblueprint.FilesystemCustomization, len(fs))
|
||||
for idx := range fs {
|
||||
ifs[idx] = iblueprint.FilesystemCustomization(fs[idx])
|
||||
}
|
||||
customizations.Filesystem = ifs
|
||||
}
|
||||
if disk := c.Disk; disk != nil {
|
||||
idisk := &iblueprint.DiskCustomization{
|
||||
Type: disk.Type,
|
||||
MinSize: disk.MinSize,
|
||||
Partitions: make([]iblueprint.PartitionCustomization, len(disk.Partitions)),
|
||||
}
|
||||
for idx, part := range disk.Partitions {
|
||||
ipart := iblueprint.PartitionCustomization{
|
||||
Type: part.Type,
|
||||
MinSize: part.MinSize,
|
||||
PartType: part.PartType,
|
||||
PartLabel: part.PartLabel,
|
||||
PartUUID: part.PartUUID,
|
||||
BtrfsVolumeCustomization: iblueprint.BtrfsVolumeCustomization{},
|
||||
VGCustomization: iblueprint.VGCustomization{
|
||||
Name: part.VGCustomization.Name,
|
||||
},
|
||||
FilesystemTypedCustomization: iblueprint.FilesystemTypedCustomization(part.FilesystemTypedCustomization),
|
||||
}
|
||||
|
||||
if len(part.LogicalVolumes) > 0 {
|
||||
ipart.LogicalVolumes = make([]iblueprint.LVCustomization, len(part.LogicalVolumes))
|
||||
for lvidx, lv := range part.LogicalVolumes {
|
||||
ipart.LogicalVolumes[lvidx] = iblueprint.LVCustomization{
|
||||
Name: lv.Name,
|
||||
MinSize: lv.MinSize,
|
||||
FilesystemTypedCustomization: iblueprint.FilesystemTypedCustomization(lv.FilesystemTypedCustomization),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(part.Subvolumes) > 0 {
|
||||
ipart.Subvolumes = make([]iblueprint.BtrfsSubvolumeCustomization, len(part.Subvolumes))
|
||||
for svidx, sv := range part.Subvolumes {
|
||||
ipart.Subvolumes[svidx] = iblueprint.BtrfsSubvolumeCustomization(sv)
|
||||
}
|
||||
}
|
||||
|
||||
idisk.Partitions[idx] = ipart
|
||||
}
|
||||
customizations.Disk = idisk
|
||||
}
|
||||
if tz := c.Timezone; tz != nil {
|
||||
itz := iblueprint.TimezoneCustomization(*tz)
|
||||
customizations.Timezone = &itz
|
||||
}
|
||||
if locale := c.Locale; locale != nil {
|
||||
ilocale := iblueprint.LocaleCustomization(*locale)
|
||||
customizations.Locale = &ilocale
|
||||
}
|
||||
if fw := c.Firewall; fw != nil {
|
||||
ifw := iblueprint.FirewallCustomization{
|
||||
Ports: fw.Ports,
|
||||
}
|
||||
if services := fw.Services; services != nil {
|
||||
iservices := iblueprint.FirewallServicesCustomization(*services)
|
||||
ifw.Services = &iservices
|
||||
}
|
||||
if zones := fw.Zones; zones != nil {
|
||||
izones := make([]iblueprint.FirewallZoneCustomization, len(zones))
|
||||
for idx := range zones {
|
||||
izones[idx] = iblueprint.FirewallZoneCustomization(zones[idx])
|
||||
}
|
||||
ifw.Zones = izones
|
||||
}
|
||||
customizations.Firewall = &ifw
|
||||
}
|
||||
if services := c.Services; services != nil {
|
||||
iservices := iblueprint.ServicesCustomization(*services)
|
||||
customizations.Services = &iservices
|
||||
}
|
||||
if fips := c.FIPS; fips != nil {
|
||||
customizations.FIPS = fips
|
||||
}
|
||||
if installer := c.Installer; installer != nil {
|
||||
iinst := iblueprint.InstallerCustomization{
|
||||
Unattended: installer.Unattended,
|
||||
SudoNopasswd: installer.SudoNopasswd,
|
||||
}
|
||||
if installer.Kickstart != nil {
|
||||
iinst.Kickstart = &iblueprint.Kickstart{
|
||||
Contents: installer.Kickstart.Contents,
|
||||
}
|
||||
}
|
||||
if installer.Modules != nil {
|
||||
iinst.Modules = &iblueprint.AnacondaModules{
|
||||
Enable: installer.Modules.Enable,
|
||||
Disable: installer.Modules.Disable,
|
||||
}
|
||||
}
|
||||
customizations.Installer = &iinst
|
||||
}
|
||||
if rpm := c.RPM; rpm != nil && rpm.ImportKeys != nil {
|
||||
irpm := iblueprint.RPMCustomization{
|
||||
ImportKeys: &iblueprint.RPMImportKeys{
|
||||
Files: rpm.ImportKeys.Files,
|
||||
},
|
||||
}
|
||||
customizations.RPM = &irpm
|
||||
}
|
||||
if rhsm := c.RHSM; rhsm != nil && rhsm.Config != nil {
|
||||
irhsm := iblueprint.RHSMCustomization{
|
||||
Config: &iblueprint.RHSMConfig{},
|
||||
}
|
||||
|
||||
if plugins := rhsm.Config.DNFPlugins; plugins != nil {
|
||||
irhsm.Config.DNFPlugins = &iblueprint.SubManDNFPluginsConfig{}
|
||||
if plugins.ProductID != nil && plugins.ProductID.Enabled != nil {
|
||||
irhsm.Config.DNFPlugins.ProductID = &iblueprint.DNFPluginConfig{
|
||||
Enabled: common.ToPtr(*plugins.ProductID.Enabled),
|
||||
}
|
||||
}
|
||||
if plugins.SubscriptionManager != nil && plugins.SubscriptionManager.Enabled != nil {
|
||||
irhsm.Config.DNFPlugins.SubscriptionManager = &iblueprint.DNFPluginConfig{
|
||||
Enabled: common.ToPtr(*plugins.SubscriptionManager.Enabled),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if subManConf := rhsm.Config.SubscriptionManager; subManConf != nil {
|
||||
irhsm.Config.SubscriptionManager = &iblueprint.SubManConfig{}
|
||||
if subManConf.RHSMConfig != nil && subManConf.RHSMConfig.ManageRepos != nil {
|
||||
irhsm.Config.SubscriptionManager.RHSMConfig = &iblueprint.SubManRHSMConfig{
|
||||
ManageRepos: common.ToPtr(*subManConf.RHSMConfig.ManageRepos),
|
||||
}
|
||||
}
|
||||
if subManConf.RHSMCertdConfig != nil && subManConf.RHSMCertdConfig.AutoRegistration != nil {
|
||||
irhsm.Config.SubscriptionManager.RHSMCertdConfig = &iblueprint.SubManRHSMCertdConfig{
|
||||
AutoRegistration: common.ToPtr(*subManConf.RHSMCertdConfig.AutoRegistration),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customizations.RHSM = &irhsm
|
||||
}
|
||||
|
||||
if ca := c.CACerts; ca != nil {
|
||||
ica := iblueprint.CACustomization{
|
||||
PEMCerts: ca.PEMCerts,
|
||||
}
|
||||
customizations.CACerts = &ica
|
||||
}
|
||||
}
|
||||
|
||||
ibp := iblueprint.Blueprint{
|
||||
Name: bp.Name,
|
||||
Description: bp.Description,
|
||||
Version: bp.Version,
|
||||
Packages: pkgs,
|
||||
Modules: modules,
|
||||
EnabledModules: enabledModules,
|
||||
Groups: groups,
|
||||
Containers: containers,
|
||||
Customizations: customizations,
|
||||
Distro: bp.Distro,
|
||||
}
|
||||
|
||||
return ibp
|
||||
}
|
||||
|
|
|
|||
16
vendor/github.com/osbuild/blueprint/pkg/blueprint/customizations.go
generated
vendored
16
vendor/github.com/osbuild/blueprint/pkg/blueprint/customizations.go
generated
vendored
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
"github.com/osbuild/images/pkg/cert"
|
||||
"github.com/osbuild/images/pkg/customizations/anaconda"
|
||||
"github.com/osbuild/images/pkg/disk"
|
||||
"github.com/osbuild/images/pkg/disk/partition"
|
||||
)
|
||||
|
||||
type Customizations struct {
|
||||
|
|
@ -346,22 +346,22 @@ func (c *Customizations) GetPartitioning() (*DiskCustomization, error) {
|
|||
}
|
||||
|
||||
// GetPartitioningMode converts the string to a disk.PartitioningMode type
|
||||
func (c *Customizations) GetPartitioningMode() (disk.PartitioningMode, error) {
|
||||
func (c *Customizations) GetPartitioningMode() (partition.PartitioningMode, error) {
|
||||
if c == nil {
|
||||
return disk.DefaultPartitioningMode, nil
|
||||
return partition.DefaultPartitioningMode, nil
|
||||
}
|
||||
|
||||
switch c.PartitioningMode {
|
||||
case "raw":
|
||||
return disk.RawPartitioningMode, nil
|
||||
return partition.RawPartitioningMode, nil
|
||||
case "lvm":
|
||||
return disk.LVMPartitioningMode, nil
|
||||
return partition.LVMPartitioningMode, nil
|
||||
case "auto-lvm":
|
||||
return disk.AutoLVMPartitioningMode, nil
|
||||
return partition.AutoLVMPartitioningMode, nil
|
||||
case "":
|
||||
return disk.DefaultPartitioningMode, nil
|
||||
return partition.DefaultPartitioningMode, nil
|
||||
default:
|
||||
return disk.DefaultPartitioningMode, fmt.Errorf("invalid partitioning mode '%s'", c.PartitioningMode)
|
||||
return partition.DefaultPartitioningMode, fmt.Errorf("invalid partitioning mode '%s'", c.PartitioningMode)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
8
vendor/github.com/osbuild/blueprint/pkg/blueprint/disk_customizations.go
generated
vendored
8
vendor/github.com/osbuild/blueprint/pkg/blueprint/disk_customizations.go
generated
vendored
|
|
@ -607,6 +607,14 @@ func (p *PartitionCustomization) ValidatePartitionLabel(ptType string) error {
|
|||
}
|
||||
|
||||
func (p *PartitionCustomization) validatePlain(mountpoints map[string]bool) error {
|
||||
if p.FSType == "none" {
|
||||
// make sure the mountpoint is empty and return
|
||||
if p.Mountpoint != "" {
|
||||
return fmt.Errorf("mountpoint for none partition must be empty (got %q)", p.Mountpoint)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.FSType == "swap" {
|
||||
// make sure the mountpoint is empty and return
|
||||
if p.Mountpoint != "" {
|
||||
|
|
|
|||
98
vendor/github.com/osbuild/images/pkg/blueprint/blueprint.go
generated
vendored
98
vendor/github.com/osbuild/images/pkg/blueprint/blueprint.go
generated
vendored
|
|
@ -1,98 +0,0 @@
|
|||
// Package blueprint contains primitives for representing weldr blueprints
|
||||
package blueprint
|
||||
|
||||
// A Blueprint is a high-level description of an image.
|
||||
type Blueprint struct {
|
||||
Name string `json:"name" toml:"name"`
|
||||
Description string `json:"description" toml:"description"`
|
||||
Version string `json:"version,omitempty" toml:"version,omitempty"`
|
||||
Packages []Package `json:"packages" toml:"packages"`
|
||||
Modules []Package `json:"modules" toml:"modules"`
|
||||
|
||||
// Note, this is called "enabled modules" because we already have "modules" except
|
||||
// the "modules" refers to packages and "enabled modules" refers to modularity modules.
|
||||
EnabledModules []EnabledModule `json:"enabled_modules" toml:"enabled_modules"`
|
||||
|
||||
Groups []Group `json:"groups" toml:"groups"`
|
||||
Containers []Container `json:"containers,omitempty" toml:"containers,omitempty"`
|
||||
Customizations *Customizations `json:"customizations,omitempty" toml:"customizations,omitempty"`
|
||||
Distro string `json:"distro" toml:"distro"`
|
||||
|
||||
// EXPERIMENTAL
|
||||
Minimal bool `json:"minimal" toml:"minimal"`
|
||||
}
|
||||
|
||||
// A Package specifies an RPM package.
|
||||
type Package struct {
|
||||
Name string `json:"name" toml:"name"`
|
||||
Version string `json:"version,omitempty" toml:"version,omitempty"`
|
||||
}
|
||||
|
||||
// A module specifies a modularity stream.
|
||||
type EnabledModule struct {
|
||||
Name string `json:"name" toml:"name"`
|
||||
Stream string `json:"stream,omitempty" toml:"stream,omitempty"`
|
||||
}
|
||||
|
||||
// A group specifies an package group.
|
||||
type Group struct {
|
||||
Name string `json:"name" toml:"name"`
|
||||
}
|
||||
|
||||
type Container struct {
|
||||
Source string `json:"source" toml:"source"`
|
||||
Name string `json:"name,omitempty" toml:"name,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
|
||||
// function returns a combined list of "name-version" strings.
|
||||
func (b *Blueprint) GetPackages() []string {
|
||||
return b.GetPackagesEx(true)
|
||||
}
|
||||
|
||||
func (b *Blueprint) GetPackagesEx(bootable bool) []string {
|
||||
packages := []string{}
|
||||
for _, pkg := range b.Packages {
|
||||
packages = append(packages, pkg.ToNameVersion())
|
||||
}
|
||||
for _, pkg := range b.Modules {
|
||||
packages = append(packages, pkg.ToNameVersion())
|
||||
}
|
||||
for _, group := range b.Groups {
|
||||
packages = append(packages, "@"+group.Name)
|
||||
}
|
||||
|
||||
if bootable {
|
||||
kc := b.Customizations.GetKernel()
|
||||
kpkg := Package{Name: kc.Name}
|
||||
packages = append(packages, kpkg.ToNameVersion())
|
||||
}
|
||||
|
||||
return packages
|
||||
}
|
||||
|
||||
func (b *Blueprint) GetEnabledModules() []string {
|
||||
modules := []string{}
|
||||
|
||||
for _, mod := range b.EnabledModules {
|
||||
modules = append(modules, mod.ToNameStream())
|
||||
}
|
||||
|
||||
return modules
|
||||
}
|
||||
|
||||
func (p Package) ToNameVersion() string {
|
||||
// Omit version to prevent all packages with prefix of name to be installed
|
||||
if p.Version == "*" || p.Version == "" {
|
||||
return p.Name
|
||||
}
|
||||
|
||||
return p.Name + "-" + p.Version
|
||||
}
|
||||
|
||||
func (p EnabledModule) ToNameStream() string {
|
||||
return p.Name + ":" + p.Stream
|
||||
}
|
||||
471
vendor/github.com/osbuild/images/pkg/blueprint/customizations.go
generated
vendored
471
vendor/github.com/osbuild/images/pkg/blueprint/customizations.go
generated
vendored
|
|
@ -1,471 +0,0 @@
|
|||
package blueprint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/osbuild/images/pkg/cert"
|
||||
"github.com/osbuild/images/pkg/customizations/anaconda"
|
||||
)
|
||||
|
||||
type Customizations struct {
|
||||
Hostname *string `json:"hostname,omitempty" toml:"hostname,omitempty"`
|
||||
Kernel *KernelCustomization `json:"kernel,omitempty" toml:"kernel,omitempty"`
|
||||
User []UserCustomization `json:"user,omitempty" toml:"user,omitempty"`
|
||||
Group []GroupCustomization `json:"group,omitempty" toml:"group,omitempty"`
|
||||
Timezone *TimezoneCustomization `json:"timezone,omitempty" toml:"timezone,omitempty"`
|
||||
Locale *LocaleCustomization `json:"locale,omitempty" toml:"locale,omitempty"`
|
||||
Firewall *FirewallCustomization `json:"firewall,omitempty" toml:"firewall,omitempty"`
|
||||
Services *ServicesCustomization `json:"services,omitempty" toml:"services,omitempty"`
|
||||
Filesystem []FilesystemCustomization `json:"filesystem,omitempty" toml:"filesystem,omitempty"`
|
||||
Disk *DiskCustomization `json:"disk,omitempty" toml:"disk,omitempty"`
|
||||
InstallationDevice string `json:"installation_device,omitempty" toml:"installation_device,omitempty"`
|
||||
FDO *FDOCustomization `json:"fdo,omitempty" toml:"fdo,omitempty"`
|
||||
OpenSCAP *OpenSCAPCustomization `json:"openscap,omitempty" toml:"openscap,omitempty"`
|
||||
Ignition *IgnitionCustomization `json:"ignition,omitempty" toml:"ignition,omitempty"`
|
||||
Directories []DirectoryCustomization `json:"directories,omitempty" toml:"directories,omitempty"`
|
||||
Files []FileCustomization `json:"files,omitempty" toml:"files,omitempty"`
|
||||
Repositories []RepositoryCustomization `json:"repositories,omitempty" toml:"repositories,omitempty"`
|
||||
FIPS *bool `json:"fips,omitempty" toml:"fips,omitempty"`
|
||||
ContainersStorage *ContainerStorageCustomization `json:"containers-storage,omitempty" toml:"containers-storage,omitempty"`
|
||||
Installer *InstallerCustomization `json:"installer,omitempty" toml:"installer,omitempty"`
|
||||
RPM *RPMCustomization `json:"rpm,omitempty" toml:"rpm,omitempty"`
|
||||
RHSM *RHSMCustomization `json:"rhsm,omitempty" toml:"rhsm,omitempty"`
|
||||
CACerts *CACustomization `json:"cacerts,omitempty" toml:"cacerts,omitempty"`
|
||||
}
|
||||
|
||||
type IgnitionCustomization struct {
|
||||
Embedded *EmbeddedIgnitionCustomization `json:"embedded,omitempty" toml:"embedded,omitempty"`
|
||||
FirstBoot *FirstBootIgnitionCustomization `json:"firstboot,omitempty" toml:"firstboot,omitempty"`
|
||||
}
|
||||
|
||||
type EmbeddedIgnitionCustomization struct {
|
||||
Config string `json:"config,omitempty" toml:"config,omitempty"`
|
||||
}
|
||||
|
||||
type FirstBootIgnitionCustomization struct {
|
||||
ProvisioningURL string `json:"url,omitempty" toml:"url,omitempty"`
|
||||
}
|
||||
|
||||
type FDOCustomization struct {
|
||||
ManufacturingServerURL string `json:"manufacturing_server_url,omitempty" toml:"manufacturing_server_url,omitempty"`
|
||||
DiunPubKeyInsecure string `json:"diun_pub_key_insecure,omitempty" toml:"diun_pub_key_insecure,omitempty"`
|
||||
// This is the output of:
|
||||
// echo "sha256:$(openssl x509 -fingerprint -sha256 -noout -in diun_cert.pem | cut -d"=" -f2 | sed 's/://g')"
|
||||
DiunPubKeyHash string `json:"diun_pub_key_hash,omitempty" toml:"diun_pub_key_hash,omitempty"`
|
||||
DiunPubKeyRootCerts string `json:"diun_pub_key_root_certs,omitempty" toml:"diun_pub_key_root_certs,omitempty"`
|
||||
DiMfgStringTypeMacIface string `json:"di_mfg_string_type_mac_iface,omitempty" toml:"di_mfg_string_type_mac_iface,omitempty"`
|
||||
}
|
||||
|
||||
type KernelCustomization struct {
|
||||
Name string `json:"name,omitempty" toml:"name,omitempty"`
|
||||
Append string `json:"append" toml:"append"`
|
||||
}
|
||||
|
||||
type SSHKeyCustomization struct {
|
||||
User string `json:"user" toml:"user"`
|
||||
Key string `json:"key" toml:"key"`
|
||||
}
|
||||
|
||||
type UserCustomization struct {
|
||||
Name string `json:"name" toml:"name"`
|
||||
Description *string `json:"description,omitempty" toml:"description,omitempty"`
|
||||
Password *string `json:"password,omitempty" toml:"password,omitempty"`
|
||||
Key *string `json:"key,omitempty" toml:"key,omitempty"`
|
||||
Home *string `json:"home,omitempty" toml:"home,omitempty"`
|
||||
Shell *string `json:"shell,omitempty" toml:"shell,omitempty"`
|
||||
Groups []string `json:"groups,omitempty" toml:"groups,omitempty"`
|
||||
UID *int `json:"uid,omitempty" toml:"uid,omitempty"`
|
||||
GID *int `json:"gid,omitempty" toml:"gid,omitempty"`
|
||||
ExpireDate *int `json:"expiredate,omitempty" toml:"expiredate,omitempty"`
|
||||
ForcePasswordReset *bool `json:"force_password_reset,omitempty" toml:"force_password_reset,omitempty"`
|
||||
}
|
||||
|
||||
type GroupCustomization struct {
|
||||
Name string `json:"name" toml:"name"`
|
||||
GID *int `json:"gid,omitempty" toml:"gid,omitempty"`
|
||||
}
|
||||
|
||||
type TimezoneCustomization struct {
|
||||
Timezone *string `json:"timezone,omitempty" toml:"timezone,omitempty"`
|
||||
NTPServers []string `json:"ntpservers,omitempty" toml:"ntpservers,omitempty"`
|
||||
}
|
||||
|
||||
type LocaleCustomization struct {
|
||||
Languages []string `json:"languages,omitempty" toml:"languages,omitempty"`
|
||||
Keyboard *string `json:"keyboard,omitempty" toml:"keyboard,omitempty"`
|
||||
}
|
||||
|
||||
type FirewallCustomization struct {
|
||||
Ports []string `json:"ports,omitempty" toml:"ports,omitempty"`
|
||||
Services *FirewallServicesCustomization `json:"services,omitempty" toml:"services,omitempty"`
|
||||
Zones []FirewallZoneCustomization `json:"zones,omitempty" toml:"zones,omitempty"`
|
||||
}
|
||||
|
||||
type FirewallZoneCustomization struct {
|
||||
Name *string `json:"name,omitempty" toml:"name,omitempty"`
|
||||
Sources []string `json:"sources,omitempty" toml:"sources,omitempty"`
|
||||
}
|
||||
|
||||
type FirewallServicesCustomization struct {
|
||||
Enabled []string `json:"enabled,omitempty" toml:"enabled,omitempty"`
|
||||
Disabled []string `json:"disabled,omitempty" toml:"disabled,omitempty"`
|
||||
}
|
||||
|
||||
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 {
|
||||
DataStream string `json:"datastream,omitempty" toml:"datastream,omitempty"`
|
||||
ProfileID string `json:"profile_id,omitempty" toml:"profile_id,omitempty"`
|
||||
Tailoring *OpenSCAPTailoringCustomizations `json:"tailoring,omitempty" toml:"tailoring,omitempty"`
|
||||
JSONTailoring *OpenSCAPJSONTailoringCustomizations `json:"json_tailoring,omitempty" toml:"json_tailoring,omitempty"`
|
||||
}
|
||||
|
||||
type OpenSCAPTailoringCustomizations struct {
|
||||
Selected []string `json:"selected,omitempty" toml:"selected,omitempty"`
|
||||
Unselected []string `json:"unselected,omitempty" toml:"unselected,omitempty"`
|
||||
}
|
||||
|
||||
type OpenSCAPJSONTailoringCustomizations struct {
|
||||
ProfileID string `json:"profile_id,omitempty" toml:"profile_id,omitempty"`
|
||||
Filepath string `json:"filepath,omitempty" toml:"filepath,omitempty"`
|
||||
}
|
||||
|
||||
// Configure the container storage separately from containers, since we most likely would
|
||||
// like to use the same storage path for all of the containers.
|
||||
type ContainerStorageCustomization struct {
|
||||
// destination is always `containers-storage`, so we won't expose this
|
||||
StoragePath *string `json:"destination-path,omitempty" toml:"destination-path,omitempty"`
|
||||
}
|
||||
|
||||
type CACustomization struct {
|
||||
PEMCerts []string `json:"pem_certs,omitempty" toml:"pem_certs,omitempty"`
|
||||
}
|
||||
|
||||
type CustomizationError struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e *CustomizationError) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
// CheckCustomizations returns an error of type `CustomizationError`
|
||||
// if `c` has any customizations not specified in `allowed`
|
||||
func (c *Customizations) CheckAllowed(allowed ...string) error {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
allowMap := make(map[string]bool)
|
||||
|
||||
for _, a := range allowed {
|
||||
allowMap[a] = true
|
||||
}
|
||||
|
||||
t := reflect.TypeOf(*c)
|
||||
v := reflect.ValueOf(*c)
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
|
||||
empty := false
|
||||
field := v.Field(i)
|
||||
|
||||
switch field.Kind() {
|
||||
case reflect.String:
|
||||
if field.String() == "" {
|
||||
empty = true
|
||||
}
|
||||
case reflect.Array, reflect.Slice:
|
||||
if field.Len() == 0 {
|
||||
empty = true
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if field.IsNil() {
|
||||
empty = true
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled customization field type %s, %s", v.Kind(), t.Field(i).Name))
|
||||
|
||||
}
|
||||
|
||||
if !empty && !allowMap[t.Field(i).Name] {
|
||||
return &CustomizationError{fmt.Sprintf("'%s' is not allowed", t.Field(i).Name)}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Customizations) GetHostname() *string {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.Hostname
|
||||
}
|
||||
|
||||
func (c *Customizations) GetPrimaryLocale() (*string, *string) {
|
||||
if c == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if c.Locale == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if len(c.Locale.Languages) == 0 {
|
||||
return nil, c.Locale.Keyboard
|
||||
}
|
||||
return &c.Locale.Languages[0], c.Locale.Keyboard
|
||||
}
|
||||
|
||||
func (c *Customizations) GetTimezoneSettings() (*string, []string) {
|
||||
if c == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if c.Timezone == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return c.Timezone.Timezone, c.Timezone.NTPServers
|
||||
}
|
||||
|
||||
func (c *Customizations) GetUsers() []UserCustomization {
|
||||
if c == nil || c.User == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
users := c.User
|
||||
|
||||
// sanitize user home directory in blueprint: if it has a trailing slash,
|
||||
// it might lead to the directory not getting the correct selinux labels
|
||||
for idx := range users {
|
||||
u := users[idx]
|
||||
if u.Home != nil {
|
||||
homedir := strings.TrimRight(*u.Home, "/")
|
||||
u.Home = &homedir
|
||||
users[idx] = u
|
||||
}
|
||||
}
|
||||
return users
|
||||
}
|
||||
|
||||
func (c *Customizations) GetGroups() []GroupCustomization {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.Group
|
||||
}
|
||||
|
||||
func (c *Customizations) GetKernel() *KernelCustomization {
|
||||
var kernelName, kernelAppend string
|
||||
if c != nil && c.Kernel != nil {
|
||||
kernelName = c.Kernel.Name
|
||||
kernelAppend = c.Kernel.Append
|
||||
}
|
||||
|
||||
if kernelName == "" {
|
||||
kernelName = "kernel"
|
||||
}
|
||||
|
||||
return &KernelCustomization{
|
||||
Name: kernelName,
|
||||
Append: kernelAppend,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Customizations) GetFirewall() *FirewallCustomization {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.Firewall
|
||||
}
|
||||
|
||||
func (c *Customizations) GetServices() *ServicesCustomization {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.Services
|
||||
}
|
||||
|
||||
func (c *Customizations) GetFilesystems() []FilesystemCustomization {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.Filesystem
|
||||
}
|
||||
|
||||
func (c *Customizations) GetFilesystemsMinSize() uint64 {
|
||||
if c == nil {
|
||||
return 0
|
||||
}
|
||||
var agg uint64
|
||||
for _, m := range c.Filesystem {
|
||||
agg += m.MinSize
|
||||
}
|
||||
// This ensures that file system customization `size` is a multiple of
|
||||
// sector size (512)
|
||||
if agg%512 != 0 {
|
||||
agg = (agg/512 + 1) * 512
|
||||
}
|
||||
return agg
|
||||
}
|
||||
|
||||
func (c *Customizations) GetPartitioning() (*DiskCustomization, error) {
|
||||
if c == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if err := c.Disk.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.Disk, nil
|
||||
}
|
||||
|
||||
func (c *Customizations) GetInstallationDevice() string {
|
||||
if c == nil || c.InstallationDevice == "" {
|
||||
return ""
|
||||
}
|
||||
return c.InstallationDevice
|
||||
}
|
||||
|
||||
func (c *Customizations) GetFDO() *FDOCustomization {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.FDO
|
||||
}
|
||||
|
||||
func (c *Customizations) GetOpenSCAP() *OpenSCAPCustomization {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.OpenSCAP
|
||||
}
|
||||
|
||||
func (c *Customizations) GetIgnition() *IgnitionCustomization {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.Ignition
|
||||
}
|
||||
|
||||
func (c *Customizations) GetDirectories() []DirectoryCustomization {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.Directories
|
||||
}
|
||||
|
||||
func (c *Customizations) GetFiles() []FileCustomization {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.Files
|
||||
}
|
||||
|
||||
func (c *Customizations) GetRepositories() ([]RepositoryCustomization, error) {
|
||||
if c == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
for _, repo := range c.Repositories {
|
||||
err := validateCustomRepository(&repo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return c.Repositories, nil
|
||||
}
|
||||
|
||||
func (c *Customizations) GetFIPS() bool {
|
||||
if c == nil || c.FIPS == nil {
|
||||
return false
|
||||
}
|
||||
return *c.FIPS
|
||||
}
|
||||
|
||||
func (c *Customizations) GetContainerStorage() *ContainerStorageCustomization {
|
||||
if c == nil || c.ContainersStorage == nil {
|
||||
return nil
|
||||
}
|
||||
if *c.ContainersStorage.StoragePath == "" {
|
||||
return nil
|
||||
}
|
||||
return c.ContainersStorage
|
||||
}
|
||||
|
||||
func (c *Customizations) GetInstaller() (*InstallerCustomization, error) {
|
||||
if c == nil || c.Installer == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Validate conflicting customizations: Installer options aren't supported
|
||||
// when the user adds their own kickstart content
|
||||
if c.Installer.Kickstart != nil && len(c.Installer.Kickstart.Contents) > 0 {
|
||||
if c.Installer.Unattended {
|
||||
return nil, fmt.Errorf("installer.unattended is not supported when adding custom kickstart contents")
|
||||
}
|
||||
if len(c.Installer.SudoNopasswd) > 0 {
|
||||
return nil, fmt.Errorf("installer.sudo-nopasswd is not supported when adding custom kickstart contents")
|
||||
}
|
||||
}
|
||||
|
||||
// Disabling the user module isn't supported when users or groups are
|
||||
// defined
|
||||
if c.Installer.Modules != nil &&
|
||||
slices.Contains(c.Installer.Modules.Disable, anaconda.ModuleUsers) &&
|
||||
len(c.User)+len(c.Group) > 0 {
|
||||
return nil, fmt.Errorf("blueprint contains user or group customizations but disables the required Users Anaconda module")
|
||||
}
|
||||
|
||||
return c.Installer, nil
|
||||
}
|
||||
|
||||
func (c *Customizations) GetRPM() *RPMCustomization {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.RPM
|
||||
}
|
||||
|
||||
func (c *Customizations) GetRHSM() *RHSMCustomization {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.RHSM
|
||||
}
|
||||
|
||||
func (c *Customizations) checkCACerts() error {
|
||||
if c == nil || c.CACerts == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, bundle := range c.CACerts.PEMCerts {
|
||||
_, err := cert.ParseCerts(bundle)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Customizations) GetCACerts() (*CACustomization, error) {
|
||||
if c == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if err := c.checkCACerts(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.CACerts, nil
|
||||
}
|
||||
750
vendor/github.com/osbuild/images/pkg/blueprint/disk_customizations.go
generated
vendored
750
vendor/github.com/osbuild/images/pkg/blueprint/disk_customizations.go
generated
vendored
|
|
@ -1,750 +0,0 @@
|
|||
package blueprint
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"unicode/utf16"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/osbuild/images/pkg/datasizes"
|
||||
"github.com/osbuild/images/pkg/pathpolicy"
|
||||
)
|
||||
|
||||
type DiskCustomization struct {
|
||||
// Type of the partition table: gpt or dos.
|
||||
// Optional, the default depends on the distro and image type.
|
||||
Type string
|
||||
MinSize uint64
|
||||
Partitions []PartitionCustomization
|
||||
}
|
||||
|
||||
type diskCustomizationMarshaler struct {
|
||||
Type string `json:"type,omitempty" toml:"type,omitempty"`
|
||||
MinSize datasizes.Size `json:"minsize,omitempty" toml:"minsize,omitempty"`
|
||||
Partitions []PartitionCustomization `json:"partitions,omitempty" toml:"partitions,omitempty"`
|
||||
}
|
||||
|
||||
func (dc *DiskCustomization) UnmarshalJSON(data []byte) error {
|
||||
var dcm diskCustomizationMarshaler
|
||||
if err := json.Unmarshal(data, &dcm); err != nil {
|
||||
return err
|
||||
}
|
||||
dc.Type = dcm.Type
|
||||
dc.MinSize = dcm.MinSize.Uint64()
|
||||
dc.Partitions = dcm.Partitions
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dc *DiskCustomization) UnmarshalTOML(data any) error {
|
||||
return unmarshalTOMLviaJSON(dc, data)
|
||||
}
|
||||
|
||||
// PartitionCustomization defines a single partition on a disk. The Type
|
||||
// defines the kind of "payload" for the partition: plain, lvm, or btrfs.
|
||||
// - plain: the payload will be a filesystem on a partition (e.g. xfs, ext4).
|
||||
// See [FilesystemTypedCustomization] for extra fields.
|
||||
// - lvm: the payload will be an LVM volume group. See [VGCustomization] for
|
||||
// extra fields
|
||||
// - btrfs: the payload will be a btrfs volume. See
|
||||
// [BtrfsVolumeCustomization] for extra fields.
|
||||
type PartitionCustomization struct {
|
||||
// The type of payload for the partition (optional, defaults to "plain").
|
||||
Type string `json:"type" toml:"type"`
|
||||
|
||||
// Minimum size of the partition that contains the filesystem (for "plain"
|
||||
// filesystem), volume group ("lvm"), or btrfs volume ("btrfs"). The final
|
||||
// size of the partition will be larger than the minsize if the sum of the
|
||||
// contained volumes (logical volumes or subvolumes) is larger. In
|
||||
// addition, certain mountpoints have required minimum sizes. See
|
||||
// https://osbuild.org/docs/user-guide/partitioning for more details.
|
||||
// (optional, defaults depend on payload and mountpoints).
|
||||
MinSize uint64 `json:"minsize" toml:"minsize"`
|
||||
|
||||
// The partition type GUID for GPT partitions. For DOS partitions, this
|
||||
// field can be used to set the (2 hex digit) partition type.
|
||||
// If not set, the type will be automatically set based on the mountpoint
|
||||
// or the payload type.
|
||||
PartType string `json:"part_type,omitempty" toml:"part_type,omitempty"`
|
||||
|
||||
// The partition label for GPT partitions, not supported for dos partitions.
|
||||
// Note: This is not the same as the label, which can be set in "Label"
|
||||
PartLabel string `json:"part_label,omitempty" toml:"part_label,omitempty"`
|
||||
|
||||
// The partition GUID for GPT partitions, not supported for dos partitions.
|
||||
// Note: This is the unique uuid, not the type guid, that is PartType
|
||||
PartUUID string `json:"part_uuid,omitempty" toml:"part_uuid,omitempty"`
|
||||
|
||||
BtrfsVolumeCustomization
|
||||
|
||||
VGCustomization
|
||||
|
||||
FilesystemTypedCustomization
|
||||
}
|
||||
|
||||
// A filesystem on a plain partition or LVM logical volume.
|
||||
// Note the differences from [FilesystemCustomization]:
|
||||
// - Adds a label.
|
||||
// - Adds a filesystem type (fs_type).
|
||||
// - Does not define a size. The size is defined by its container: a
|
||||
// partition ([PartitionCustomization]) or LVM logical volume
|
||||
// ([LVCustomization]).
|
||||
//
|
||||
// Setting the FSType to "swap" creates a swap area (and the Mountpoint must be
|
||||
// empty).
|
||||
type FilesystemTypedCustomization struct {
|
||||
Mountpoint string `json:"mountpoint" toml:"mountpoint"`
|
||||
Label string `json:"label,omitempty" toml:"label,omitempty"`
|
||||
FSType string `json:"fs_type,omitempty" toml:"fs_type,omitempty"`
|
||||
}
|
||||
|
||||
// An LVM volume group with one or more logical volumes.
|
||||
type VGCustomization struct {
|
||||
// Volume group name (optional, default will be automatically generated).
|
||||
Name string `json:"name" toml:"name"`
|
||||
LogicalVolumes []LVCustomization `json:"logical_volumes,omitempty" toml:"logical_volumes,omitempty"`
|
||||
}
|
||||
|
||||
type LVCustomization struct {
|
||||
// Logical volume name
|
||||
Name string `json:"name,omitempty" toml:"name,omitempty"`
|
||||
|
||||
// Minimum size of the logical volume
|
||||
MinSize uint64 `json:"minsize,omitempty" toml:"minsize,omitempty"`
|
||||
|
||||
FilesystemTypedCustomization
|
||||
}
|
||||
|
||||
// Custom JSON unmarshaller for LVCustomization for handling the conversion of
|
||||
// data sizes (minsize) expressed as strings to uint64.
|
||||
func (lv *LVCustomization) UnmarshalJSON(data []byte) error {
|
||||
var lvAnySize struct {
|
||||
Name string `json:"name,omitempty" toml:"name,omitempty"`
|
||||
MinSize any `json:"minsize,omitempty" toml:"minsize,omitempty"`
|
||||
FilesystemTypedCustomization
|
||||
}
|
||||
if err := json.Unmarshal(data, &lvAnySize); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lv.Name = lvAnySize.Name
|
||||
lv.FilesystemTypedCustomization = lvAnySize.FilesystemTypedCustomization
|
||||
|
||||
if lvAnySize.MinSize == nil {
|
||||
return fmt.Errorf("minsize is required")
|
||||
}
|
||||
size, err := decodeSize(lvAnySize.MinSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lv.MinSize = size
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// A btrfs volume consisting of one or more subvolumes.
|
||||
type BtrfsVolumeCustomization struct {
|
||||
Subvolumes []BtrfsSubvolumeCustomization
|
||||
}
|
||||
|
||||
type BtrfsSubvolumeCustomization struct {
|
||||
// The name of the subvolume, which defines the location (path) on the
|
||||
// root volume (required).
|
||||
// See https://btrfs.readthedocs.io/en/latest/Subvolumes.html
|
||||
Name string `json:"name" toml:"name"`
|
||||
|
||||
// Mountpoint for the subvolume.
|
||||
Mountpoint string `json:"mountpoint" toml:"mountpoint"`
|
||||
}
|
||||
|
||||
// Custom JSON unmarshaller that first reads the value of the "type" field and
|
||||
// then deserialises the whole object into a struct that only contains the
|
||||
// fields valid for that partition type. This ensures that no fields are set
|
||||
// for the substructure of a different type than the one defined in the "type"
|
||||
// fields.
|
||||
func (v *PartitionCustomization) UnmarshalJSON(data []byte) error {
|
||||
errPrefix := "JSON unmarshal:"
|
||||
var typeSniffer struct {
|
||||
Type string `json:"type"`
|
||||
MinSize any `json:"minsize"`
|
||||
PartType string `json:"part_type"`
|
||||
PartLabel string `json:"part_label"`
|
||||
PartUUID string `json:"part_uuid"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &typeSniffer); err != nil {
|
||||
return fmt.Errorf("%s %w", errPrefix, err)
|
||||
}
|
||||
|
||||
partType := "plain"
|
||||
if typeSniffer.Type != "" {
|
||||
partType = typeSniffer.Type
|
||||
}
|
||||
|
||||
switch partType {
|
||||
case "plain":
|
||||
if err := decodePlain(v, data); err != nil {
|
||||
return fmt.Errorf("%s %w", errPrefix, err)
|
||||
}
|
||||
case "btrfs":
|
||||
if err := decodeBtrfs(v, data); err != nil {
|
||||
return fmt.Errorf("%s %w", errPrefix, err)
|
||||
}
|
||||
case "lvm":
|
||||
if err := decodeLVM(v, data); err != nil {
|
||||
return fmt.Errorf("%s %w", errPrefix, err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("%s unknown partition type: %s", errPrefix, partType)
|
||||
}
|
||||
|
||||
v.Type = partType
|
||||
v.PartType = typeSniffer.PartType
|
||||
v.PartLabel = typeSniffer.PartLabel
|
||||
v.PartUUID = typeSniffer.PartUUID
|
||||
|
||||
if typeSniffer.MinSize == nil {
|
||||
return fmt.Errorf("minsize is required")
|
||||
}
|
||||
|
||||
minsize, err := decodeSize(typeSniffer.MinSize)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s error decoding minsize for partition: %w", errPrefix, err)
|
||||
}
|
||||
v.MinSize = minsize
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodePlain decodes the data into a struct that only embeds the
|
||||
// FilesystemCustomization with DisallowUnknownFields. This ensures that when
|
||||
// the type is "plain", none of the fields for btrfs or lvm are used.
|
||||
func decodePlain(v *PartitionCustomization, data []byte) error {
|
||||
var plain struct {
|
||||
// Type, minsize, and part_* are handled by the caller. These are added here to
|
||||
// satisfy "DisallowUnknownFields" when decoding.
|
||||
Type string `json:"type"`
|
||||
MinSize any `json:"minsize"`
|
||||
PartType string `json:"part_type"`
|
||||
PartLabel string `json:"part_label"`
|
||||
PartUUID string `json:"part_uuid"`
|
||||
FilesystemTypedCustomization
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(bytes.NewReader(data))
|
||||
decoder.DisallowUnknownFields()
|
||||
err := decoder.Decode(&plain)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding partition with type \"plain\": %w", err)
|
||||
}
|
||||
|
||||
v.FilesystemTypedCustomization = plain.FilesystemTypedCustomization
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeBtrfs decodes the data into a struct that only embeds the
|
||||
// BtrfsVolumeCustomization with DisallowUnknownFields. This ensures that when
|
||||
// the type is btrfs, none of the fields for plain or lvm are used.
|
||||
func decodeBtrfs(v *PartitionCustomization, data []byte) error {
|
||||
var btrfs struct {
|
||||
// Type, minsize, and part_* are handled by the caller. These are added here to
|
||||
// satisfy "DisallowUnknownFields" when decoding.
|
||||
Type string `json:"type"`
|
||||
MinSize any `json:"minsize"`
|
||||
PartType string `json:"part_type"`
|
||||
PartLabel string `json:"part_label"`
|
||||
PartUUID string `json:"part_uuid"`
|
||||
BtrfsVolumeCustomization
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(bytes.NewReader(data))
|
||||
decoder.DisallowUnknownFields()
|
||||
err := decoder.Decode(&btrfs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding partition with type \"btrfs\": %w", err)
|
||||
}
|
||||
|
||||
v.BtrfsVolumeCustomization = btrfs.BtrfsVolumeCustomization
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeLVM decodes the data into a struct that only embeds the
|
||||
// VGCustomization with DisallowUnknownFields. This ensures that when the type
|
||||
// is lvm, none of the fields for plain or btrfs are used.
|
||||
func decodeLVM(v *PartitionCustomization, data []byte) error {
|
||||
var vg struct {
|
||||
// Type, minsize, and part_* are handled by the caller. These are added here to
|
||||
// satisfy "DisallowUnknownFields" when decoding.
|
||||
Type string `json:"type"`
|
||||
MinSize any `json:"minsize"`
|
||||
PartType string `json:"part_type"`
|
||||
PartLabel string `json:"part_label"`
|
||||
PartUUID string `json:"part_uuid"`
|
||||
VGCustomization
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(bytes.NewReader(data))
|
||||
decoder.DisallowUnknownFields()
|
||||
if err := decoder.Decode(&vg); err != nil {
|
||||
return fmt.Errorf("error decoding partition with type \"lvm\": %w", err)
|
||||
}
|
||||
|
||||
v.VGCustomization = vg.VGCustomization
|
||||
return nil
|
||||
}
|
||||
|
||||
// Custom TOML unmarshaller that first reads the value of the "type" field and
|
||||
// then deserialises the whole object into a struct that only contains the
|
||||
// fields valid for that partition type. This ensures that no fields are set
|
||||
// for the substructure of a different type than the one defined in the "type"
|
||||
// fields.
|
||||
func (v *PartitionCustomization) UnmarshalTOML(data any) error {
|
||||
errPrefix := "TOML unmarshal:"
|
||||
d, ok := data.(map[string]any)
|
||||
if !ok {
|
||||
return fmt.Errorf("%s customizations.partition is not an object", errPrefix)
|
||||
}
|
||||
|
||||
partType := "plain"
|
||||
if typeField, ok := d["type"]; ok {
|
||||
typeStr, ok := typeField.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("%s type must be a string, got \"%v\" of type %T", errPrefix, typeField, typeField)
|
||||
}
|
||||
partType = typeStr
|
||||
}
|
||||
|
||||
// serialise the data to JSON and reuse the subobject decoders
|
||||
dataJSON, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s error while decoding partition customization: %w", errPrefix, err)
|
||||
}
|
||||
switch partType {
|
||||
case "plain":
|
||||
if err := decodePlain(v, dataJSON); err != nil {
|
||||
return fmt.Errorf("%s %w", errPrefix, err)
|
||||
}
|
||||
case "btrfs":
|
||||
if err := decodeBtrfs(v, dataJSON); err != nil {
|
||||
return fmt.Errorf("%s %w", errPrefix, err)
|
||||
}
|
||||
case "lvm":
|
||||
if err := decodeLVM(v, dataJSON); err != nil {
|
||||
return fmt.Errorf("%s %w", errPrefix, err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("%s unknown partition type: %s", errPrefix, partType)
|
||||
}
|
||||
|
||||
v.Type = partType
|
||||
|
||||
minsizeField, ok := d["minsize"]
|
||||
if !ok {
|
||||
return fmt.Errorf("minsize is required")
|
||||
}
|
||||
minsize, err := decodeSize(minsizeField)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s error decoding minsize for partition: %w", errPrefix, err)
|
||||
}
|
||||
v.MinSize = minsize
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate checks for customization combinations that are generally not
|
||||
// supported or can create conflicts, regardless of specific distro or image
|
||||
// type policies. The validator ensures all of the following properties:
|
||||
// - All mountpoints are valid
|
||||
// - All mountpoints are unique
|
||||
// - All LVM volume group names are unique
|
||||
// - All LVM logical volume names are unique within a given volume group
|
||||
// - All btrfs subvolume names are unique within a given btrfs volume
|
||||
// - All btrfs subvolume names are valid and non-empty
|
||||
// - All filesystems are valid for their mountpoints (e.g. xfs or ext4 for /boot)
|
||||
// - No LVM logical volume has an invalid mountpoint (/boot or /boot/efi)
|
||||
// - Plain filesystem types are valid for the partition type
|
||||
// - All non-empty properties are valid for the partition type (e.g.
|
||||
// LogicalVolumes is empty when the type is "plain" or "btrfs")
|
||||
// - Filesystems with FSType set to "none" or "swap" do not specify a mountpoint.
|
||||
//
|
||||
// Note that in *addition* consumers should also call
|
||||
// ValidateLayoutConstraints() to validate that the policy for disk
|
||||
// customizations is met.
|
||||
func (p *DiskCustomization) Validate() error {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch p.Type {
|
||||
case "gpt", "":
|
||||
case "dos":
|
||||
// dos/mbr only supports 4 partitions
|
||||
// Unfortunately, at this stage it's unknown whether we will need extra
|
||||
// partitions (bios boot, root, esp), so this check is just to catch
|
||||
// obvious invalid customizations early. The final partition table is
|
||||
// checked after it's created.
|
||||
if len(p.Partitions) > 4 {
|
||||
return fmt.Errorf("invalid partitioning customizations: \"dos\" partition table type only supports up to 4 partitions: got %d", len(p.Partitions))
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown partition table type: %s (valid: gpt, dos)", p.Type)
|
||||
}
|
||||
|
||||
mountpoints := make(map[string]bool)
|
||||
vgnames := make(map[string]bool)
|
||||
var errs []error
|
||||
for _, part := range p.Partitions {
|
||||
if err := part.ValidatePartitionTypeID(p.Type); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if err := part.ValidatePartitionID(p.Type); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if err := part.ValidatePartitionLabel(p.Type); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
switch part.Type {
|
||||
case "plain", "":
|
||||
errs = append(errs, part.validatePlain(mountpoints))
|
||||
case "lvm":
|
||||
errs = append(errs, part.validateLVM(mountpoints, vgnames))
|
||||
case "btrfs":
|
||||
errs = append(errs, part.validateBtrfs(mountpoints))
|
||||
default:
|
||||
errs = append(errs, fmt.Errorf("unknown partition type: %s", part.Type))
|
||||
}
|
||||
}
|
||||
|
||||
// will discard all nil errors
|
||||
if err := errors.Join(errs...); err != nil {
|
||||
return fmt.Errorf("invalid partitioning customizations:\n%w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateMountpoint(path string) error {
|
||||
if path == "" {
|
||||
return fmt.Errorf("mountpoint is empty")
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
return fmt.Errorf("mountpoint %q is not an absolute path", path)
|
||||
}
|
||||
|
||||
if cleanPath := filepath.Clean(path); path != cleanPath {
|
||||
return fmt.Errorf("mountpoint %q is not a canonical path (did you mean %q?)", path, cleanPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateLayoutConstraints checks that at most one LVM Volume Group or btrfs
|
||||
// volume is defined. Returns an error if both LVM and btrfs are set and if
|
||||
// either has more than one element.
|
||||
//
|
||||
// Note that this is a *policy* validation, in theory the "disk" code
|
||||
// does support the constraints but we choose not to allow them for
|
||||
// now. Each consumer of "DiskCustomization" should call this
|
||||
// *unless* it's very low-level and not end-user-facing.
|
||||
func (p *DiskCustomization) ValidateLayoutConstraints() error {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var btrfsVols, lvmVGs uint
|
||||
for _, part := range p.Partitions {
|
||||
switch part.Type {
|
||||
case "lvm":
|
||||
lvmVGs++
|
||||
case "btrfs":
|
||||
btrfsVols++
|
||||
}
|
||||
if lvmVGs > 0 && btrfsVols > 0 {
|
||||
return fmt.Errorf("btrfs and lvm partitioning cannot be combined")
|
||||
}
|
||||
}
|
||||
|
||||
if btrfsVols > 1 {
|
||||
return fmt.Errorf("multiple btrfs volumes are not yet supported")
|
||||
}
|
||||
|
||||
if lvmVGs > 1 {
|
||||
return fmt.Errorf("multiple LVM volume groups are not yet supported")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check that the fs type is valid for the mountpoint.
|
||||
func validateFilesystemType(path, fstype string) error {
|
||||
badfsMsgFmt := "unsupported filesystem type for %q: %s"
|
||||
switch path {
|
||||
case "/boot":
|
||||
switch fstype {
|
||||
case "xfs", "ext4":
|
||||
default:
|
||||
return fmt.Errorf(badfsMsgFmt, path, fstype)
|
||||
}
|
||||
case "/boot/efi":
|
||||
switch fstype {
|
||||
case "vfat":
|
||||
default:
|
||||
return fmt.Errorf(badfsMsgFmt, path, fstype)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// These mountpoints must be on a plain partition (i.e. not on LVM or btrfs).
|
||||
var plainOnlyMountpoints = []string{
|
||||
"/boot",
|
||||
"/boot/efi", // not allowed by our global policies, but that might change
|
||||
}
|
||||
|
||||
var validPlainFSTypes = []string{
|
||||
"ext4",
|
||||
"vfat",
|
||||
"xfs",
|
||||
}
|
||||
|
||||
// exactly 2 hex digits
|
||||
var validDosPartitionType = regexp.MustCompile(`^[0-9a-fA-F]{2}$`)
|
||||
|
||||
// ValidatePartitionTypeID returns an error if the partition type ID is not
|
||||
// valid given the partition table type. If the partition table type is an
|
||||
// empty string, the function returns an error only if the partition type ID is
|
||||
// invalid for both gpt and dos partition tables.
|
||||
func (p *PartitionCustomization) ValidatePartitionTypeID(ptType string) error {
|
||||
// Empty PartType is fine, it will be selected automatically
|
||||
if p.PartType == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, uuidErr := uuid.Parse(p.PartType)
|
||||
validDosType := validDosPartitionType.MatchString(p.PartType)
|
||||
|
||||
switch ptType {
|
||||
case "gpt":
|
||||
if uuidErr != nil {
|
||||
return fmt.Errorf("invalid partition part_type %q for partition table type %q (must be a valid UUID): %w", p.PartType, ptType, uuidErr)
|
||||
}
|
||||
case "dos":
|
||||
if !validDosType {
|
||||
return fmt.Errorf("invalid partition part_type %q for partition table type %q (must be a 2-digit hex number)", p.PartType, ptType)
|
||||
}
|
||||
case "":
|
||||
// We don't know the partition table type yet, the fallback is controlled
|
||||
// by the CustomPartitionTableOptions, so return an error if it fails both.
|
||||
if uuidErr != nil && !validDosType {
|
||||
return fmt.Errorf("invalid part_type %q: must be a valid UUID for GPT partition tables or a 2-digit hex number for DOS partition tables", p.PartType)
|
||||
}
|
||||
default:
|
||||
// ignore: handled elsewhere
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidatePartitionID returns an error if the partition ID is not
|
||||
// valid given the partition table type. If the partition table type is an
|
||||
// empty string, the function returns an error only if the partition type ID is
|
||||
// invalid for both gpt and dos partition tables.
|
||||
func (p *PartitionCustomization) ValidatePartitionID(ptType string) error {
|
||||
// Empty PartUUID is fine, it will be selected automatically if needed
|
||||
if p.PartUUID == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if ptType == "dos" {
|
||||
return fmt.Errorf("part_type is not supported for dos partition tables")
|
||||
}
|
||||
|
||||
_, uuidErr := uuid.Parse(p.PartUUID)
|
||||
if uuidErr != nil {
|
||||
return fmt.Errorf("invalid partition part_uuid %q (must be a valid UUID): %w", p.PartUUID, uuidErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidatePartitionID returns an error if the partition ID is not
|
||||
// valid given the partition table type.
|
||||
func (p *PartitionCustomization) ValidatePartitionLabel(ptType string) error {
|
||||
// Empty PartLabel is fine
|
||||
if p.PartLabel == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if ptType == "dos" {
|
||||
return fmt.Errorf("part_label is not supported for dos partition tables")
|
||||
}
|
||||
|
||||
// GPT Labels are up to 36 utf-16 chars
|
||||
if len(utf16.Encode([]rune(p.PartLabel))) > 36 {
|
||||
return fmt.Errorf("part_label is not a valid GPT label, it is too long")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PartitionCustomization) validatePlain(mountpoints map[string]bool) error {
|
||||
if p.FSType == "none" {
|
||||
// make sure the mountpoint is empty and return
|
||||
if p.Mountpoint != "" {
|
||||
return fmt.Errorf("mountpoint for none partition must be empty (got %q)", p.Mountpoint)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.FSType == "swap" {
|
||||
// make sure the mountpoint is empty and return
|
||||
if p.Mountpoint != "" {
|
||||
return fmt.Errorf("mountpoint for swap partition must be empty (got %q)", p.Mountpoint)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := validateMountpoint(p.Mountpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
if mountpoints[p.Mountpoint] {
|
||||
return fmt.Errorf("duplicate mountpoint %q in partitioning customizations", p.Mountpoint)
|
||||
}
|
||||
// TODO: allow empty fstype with default from distro
|
||||
if !slices.Contains(validPlainFSTypes, p.FSType) {
|
||||
return fmt.Errorf("unknown or invalid filesystem type (fs_type) for mountpoint %q: %s", p.Mountpoint, p.FSType)
|
||||
}
|
||||
if err := validateFilesystemType(p.Mountpoint, p.FSType); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mountpoints[p.Mountpoint] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PartitionCustomization) validateLVM(mountpoints, vgnames map[string]bool) error {
|
||||
if p.Name != "" && vgnames[p.Name] { // VGs with no name get autogenerated names
|
||||
return fmt.Errorf("duplicate LVM volume group name %q in partitioning customizations", p.Name)
|
||||
}
|
||||
|
||||
// check for invalid property usage
|
||||
if len(p.Subvolumes) > 0 {
|
||||
return fmt.Errorf("subvolumes defined for LVM volume group (partition type \"lvm\")")
|
||||
}
|
||||
|
||||
if p.Label != "" {
|
||||
return fmt.Errorf("label %q defined for LVM volume group (partition type \"lvm\")", p.Label)
|
||||
}
|
||||
|
||||
vgnames[p.Name] = true
|
||||
lvnames := make(map[string]bool)
|
||||
for _, lv := range p.LogicalVolumes {
|
||||
if lv.Name != "" && lvnames[lv.Name] { // LVs with no name get autogenerated names
|
||||
return fmt.Errorf("duplicate LVM logical volume name %q in volume group %q in partitioning customizations", lv.Name, p.Name)
|
||||
}
|
||||
lvnames[lv.Name] = true
|
||||
|
||||
if lv.FSType == "swap" {
|
||||
// make sure the mountpoint is empty and return
|
||||
if lv.Mountpoint != "" {
|
||||
return fmt.Errorf("mountpoint for swap logical volume with name %q in volume group %q must be empty", lv.Name, p.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := validateMountpoint(lv.Mountpoint); err != nil {
|
||||
return fmt.Errorf("invalid logical volume customization: %w", err)
|
||||
}
|
||||
if mountpoints[lv.Mountpoint] {
|
||||
return fmt.Errorf("duplicate mountpoint %q in partitioning customizations", lv.Mountpoint)
|
||||
}
|
||||
mountpoints[lv.Mountpoint] = true
|
||||
|
||||
if slices.Contains(plainOnlyMountpoints, lv.Mountpoint) {
|
||||
return fmt.Errorf("invalid mountpoint %q for logical volume", lv.Mountpoint)
|
||||
}
|
||||
|
||||
// TODO: allow empty fstype with default from distro
|
||||
if !slices.Contains(validPlainFSTypes, lv.FSType) {
|
||||
return fmt.Errorf("unknown or invalid filesystem type (fs_type) for logical volume with mountpoint %q: %s", lv.Mountpoint, lv.FSType)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PartitionCustomization) validateBtrfs(mountpoints map[string]bool) error {
|
||||
if p.Mountpoint != "" {
|
||||
return fmt.Errorf(`"mountpoint" is not supported for btrfs volumes (only subvolumes can have mountpoints)`)
|
||||
}
|
||||
|
||||
if len(p.Subvolumes) == 0 {
|
||||
return fmt.Errorf("btrfs volume requires subvolumes")
|
||||
}
|
||||
|
||||
if len(p.LogicalVolumes) > 0 {
|
||||
return fmt.Errorf("LVM logical volumes defined for btrfs volume (partition type \"btrfs\")")
|
||||
}
|
||||
|
||||
subvolnames := make(map[string]bool)
|
||||
for _, subvol := range p.Subvolumes {
|
||||
if subvol.Name == "" {
|
||||
return fmt.Errorf("btrfs subvolume with empty name in partitioning customizations")
|
||||
}
|
||||
if subvolnames[subvol.Name] {
|
||||
return fmt.Errorf("duplicate btrfs subvolume name %q in partitioning customizations", subvol.Name)
|
||||
}
|
||||
subvolnames[subvol.Name] = true
|
||||
|
||||
if err := validateMountpoint(subvol.Mountpoint); err != nil {
|
||||
return fmt.Errorf("invalid btrfs subvolume customization: %w", err)
|
||||
}
|
||||
if mountpoints[subvol.Mountpoint] {
|
||||
return fmt.Errorf("duplicate mountpoint %q in partitioning customizations", subvol.Mountpoint)
|
||||
}
|
||||
if slices.Contains(plainOnlyMountpoints, subvol.Mountpoint) {
|
||||
return fmt.Errorf("invalid mountpoint %q for btrfs subvolume", subvol.Mountpoint)
|
||||
}
|
||||
mountpoints[subvol.Mountpoint] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckDiskMountpointsPolicy checks if the mountpoints under a [DiskCustomization] are allowed by the policy.
|
||||
func CheckDiskMountpointsPolicy(partitioning *DiskCustomization, mountpointAllowList *pathpolicy.PathPolicies) error {
|
||||
if partitioning == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// collect all mountpoints
|
||||
var mountpoints []string
|
||||
for _, part := range partitioning.Partitions {
|
||||
if part.Mountpoint != "" {
|
||||
mountpoints = append(mountpoints, part.Mountpoint)
|
||||
}
|
||||
for _, lv := range part.LogicalVolumes {
|
||||
if lv.Mountpoint != "" {
|
||||
mountpoints = append(mountpoints, lv.Mountpoint)
|
||||
}
|
||||
}
|
||||
for _, subvol := range part.Subvolumes {
|
||||
mountpoints = append(mountpoints, subvol.Mountpoint)
|
||||
}
|
||||
}
|
||||
|
||||
var errs []error
|
||||
for _, mp := range mountpoints {
|
||||
if err := mountpointAllowList.Check(mp); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("The following errors occurred while setting up custom mountpoints:\n%w", errors.Join(errs...))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
78
vendor/github.com/osbuild/images/pkg/blueprint/filesystem_customizations.go
generated
vendored
78
vendor/github.com/osbuild/images/pkg/blueprint/filesystem_customizations.go
generated
vendored
|
|
@ -1,78 +0,0 @@
|
|||
package blueprint
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/osbuild/images/pkg/datasizes"
|
||||
"github.com/osbuild/images/pkg/pathpolicy"
|
||||
)
|
||||
|
||||
type FilesystemCustomization struct {
|
||||
Mountpoint string
|
||||
MinSize uint64
|
||||
}
|
||||
|
||||
type filesystemCustomizationMarshaling struct {
|
||||
Mountpoint string `json:"mountpoint,omitempty" toml:"mountpoint,omitempty"`
|
||||
MinSize datasizes.Size `json:"minsize,omitempty" toml:"minsize,omitempty"`
|
||||
}
|
||||
|
||||
func (fsc *FilesystemCustomization) UnmarshalJSON(data []byte) error {
|
||||
var fc filesystemCustomizationMarshaling
|
||||
if err := json.Unmarshal(data, &fc); err != nil {
|
||||
if fc.Mountpoint != "" {
|
||||
return fmt.Errorf("error decoding minsize value for mountpoint %q: %w", fc.Mountpoint, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
fsc.Mountpoint = fc.Mountpoint
|
||||
fsc.MinSize = fc.MinSize.Uint64()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fsc *FilesystemCustomization) UnmarshalTOML(data any) error {
|
||||
return unmarshalTOMLviaJSON(fsc, data)
|
||||
}
|
||||
|
||||
// CheckMountpointsPolicy checks if the mountpoints are allowed by the policy
|
||||
func CheckMountpointsPolicy(mountpoints []FilesystemCustomization, mountpointAllowList *pathpolicy.PathPolicies) error {
|
||||
var errs []error
|
||||
for _, m := range mountpoints {
|
||||
if err := mountpointAllowList.Check(m.Mountpoint); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("The following errors occurred while setting up custom mountpoints:\n%w", errors.Join(errs...))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeSize takes an integer or string representing a data size (with a data
|
||||
// suffix) and returns the uint64 representation.
|
||||
func decodeSize(size any) (uint64, error) {
|
||||
switch s := size.(type) {
|
||||
case string:
|
||||
return datasizes.Parse(s)
|
||||
case int64:
|
||||
if s < 0 {
|
||||
return 0, fmt.Errorf("cannot be negative")
|
||||
}
|
||||
return uint64(s), nil
|
||||
case float64:
|
||||
if s < 0 {
|
||||
return 0, fmt.Errorf("cannot be negative")
|
||||
}
|
||||
// TODO: emit warning of possible truncation?
|
||||
return uint64(s), nil
|
||||
case uint64:
|
||||
return s, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("failed to convert value \"%v\" to number", size)
|
||||
}
|
||||
}
|
||||
498
vendor/github.com/osbuild/images/pkg/blueprint/fsnode_customizations.go
generated
vendored
498
vendor/github.com/osbuild/images/pkg/blueprint/fsnode_customizations.go
generated
vendored
|
|
@ -1,498 +0,0 @@
|
|||
package blueprint
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/osbuild/images/internal/common"
|
||||
"github.com/osbuild/images/pkg/customizations/fsnode"
|
||||
"github.com/osbuild/images/pkg/pathpolicy"
|
||||
)
|
||||
|
||||
// validateModeString checks that the given string is a valid mode octal number
|
||||
func validateModeString(mode string) error {
|
||||
// Check that the mode string matches the octal format regular expression.
|
||||
// The leading is optional.
|
||||
if regexp.MustCompile(`^[0]{0,1}[0-7]{3}$`).MatchString(mode) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("invalid mode %s: must be an octal number", mode)
|
||||
}
|
||||
|
||||
// DirectoryCustomization represents a directory to be created in the image
|
||||
type DirectoryCustomization struct {
|
||||
// Absolute path to the directory
|
||||
Path string `json:"path" toml:"path"`
|
||||
// Owner of the directory specified as a string (user name), int64 (UID) or nil
|
||||
User interface{} `json:"user,omitempty" toml:"user,omitempty"`
|
||||
// Owner of the directory specified as a string (group name), int64 (UID) or nil
|
||||
Group interface{} `json:"group,omitempty" toml:"group,omitempty"`
|
||||
// Permissions of the directory specified as an octal number
|
||||
Mode string `json:"mode,omitempty" toml:"mode,omitempty"`
|
||||
// EnsureParents ensures that all parent directories of the directory exist
|
||||
EnsureParents bool `json:"ensure_parents,omitempty" toml:"ensure_parents,omitempty"`
|
||||
}
|
||||
|
||||
// Custom TOML unmarshalling for DirectoryCustomization with validation
|
||||
func (d *DirectoryCustomization) UnmarshalTOML(data interface{}) error {
|
||||
var dir DirectoryCustomization
|
||||
|
||||
dataMap, _ := data.(map[string]interface{})
|
||||
|
||||
switch path := dataMap["path"].(type) {
|
||||
case string:
|
||||
dir.Path = path
|
||||
default:
|
||||
return fmt.Errorf("UnmarshalTOML: path must be a string")
|
||||
}
|
||||
|
||||
switch user := dataMap["user"].(type) {
|
||||
case string:
|
||||
dir.User = user
|
||||
case int64:
|
||||
dir.User = user
|
||||
case nil:
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("UnmarshalTOML: user must be a string or an integer, got %T", user)
|
||||
}
|
||||
|
||||
switch group := dataMap["group"].(type) {
|
||||
case string:
|
||||
dir.Group = group
|
||||
case int64:
|
||||
dir.Group = group
|
||||
case nil:
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("UnmarshalTOML: group must be a string or an integer")
|
||||
}
|
||||
|
||||
switch mode := dataMap["mode"].(type) {
|
||||
case string:
|
||||
dir.Mode = mode
|
||||
case nil:
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("UnmarshalTOML: mode must be a string")
|
||||
}
|
||||
|
||||
switch ensureParents := dataMap["ensure_parents"].(type) {
|
||||
case bool:
|
||||
dir.EnsureParents = ensureParents
|
||||
case nil:
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("UnmarshalTOML: ensure_parents must be a bool")
|
||||
}
|
||||
|
||||
// try converting to fsnode.Directory to validate all values
|
||||
_, err := dir.ToFsNodeDirectory()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*d = dir
|
||||
return nil
|
||||
}
|
||||
|
||||
// Custom JSON unmarshalling for DirectoryCustomization with validation
|
||||
func (d *DirectoryCustomization) UnmarshalJSON(data []byte) error {
|
||||
type directoryCustomization DirectoryCustomization
|
||||
|
||||
var dirPrivate directoryCustomization
|
||||
if err := json.Unmarshal(data, &dirPrivate); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dir := DirectoryCustomization(dirPrivate)
|
||||
if uid, ok := dir.User.(float64); ok {
|
||||
// check if uid can be converted to int64
|
||||
if uid != float64(int64(uid)) {
|
||||
return fmt.Errorf("invalid user %f: must be an integer", uid)
|
||||
}
|
||||
dir.User = int64(uid)
|
||||
}
|
||||
if gid, ok := dir.Group.(float64); ok {
|
||||
// check if gid can be converted to int64
|
||||
if gid != float64(int64(gid)) {
|
||||
return fmt.Errorf("invalid group %f: must be an integer", gid)
|
||||
}
|
||||
dir.Group = int64(gid)
|
||||
}
|
||||
// try converting to fsnode.Directory to validate all values
|
||||
_, err := dir.ToFsNodeDirectory()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*d = dir
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToFsNodeDirectory converts the DirectoryCustomization to an fsnode.Directory
|
||||
func (d DirectoryCustomization) ToFsNodeDirectory() (*fsnode.Directory, error) {
|
||||
var mode *os.FileMode
|
||||
if d.Mode != "" {
|
||||
err := validateModeString(d.Mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
modeNum, err := strconv.ParseUint(d.Mode, 8, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid mode %s: %v", d.Mode, err)
|
||||
}
|
||||
mode = common.ToPtr(os.FileMode(modeNum))
|
||||
}
|
||||
|
||||
return fsnode.NewDirectory(d.Path, mode, d.User, d.Group, d.EnsureParents)
|
||||
}
|
||||
|
||||
// DirectoryCustomizationsToFsNodeDirectories converts a slice of DirectoryCustomizations
|
||||
// to a slice of fsnode.Directories
|
||||
func DirectoryCustomizationsToFsNodeDirectories(dirs []DirectoryCustomization) ([]*fsnode.Directory, error) {
|
||||
if len(dirs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var fsDirs []*fsnode.Directory
|
||||
var errors []error
|
||||
for _, dir := range dirs {
|
||||
fsDir, err := dir.ToFsNodeDirectory()
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
fsDirs = append(fsDirs, fsDir)
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return nil, fmt.Errorf("invalid directory customizations: %v", errors)
|
||||
}
|
||||
|
||||
return fsDirs, nil
|
||||
}
|
||||
|
||||
// FileCustomization represents a file to be created in the image
|
||||
type FileCustomization struct {
|
||||
// Absolute path to the file
|
||||
Path string `json:"path" toml:"path"`
|
||||
// Owner of the directory specified as a string (user name), int64 (UID) or nil
|
||||
User interface{} `json:"user,omitempty" toml:"user,omitempty"`
|
||||
// Owner of the directory specified as a string (group name), int64 (UID) or nil
|
||||
Group interface{} `json:"group,omitempty" toml:"group,omitempty"`
|
||||
// Permissions of the file specified as an octal number
|
||||
Mode string `json:"mode,omitempty" toml:"mode,omitempty"`
|
||||
// Data is the file content in plain text
|
||||
Data string `json:"data,omitempty" toml:"data,omitempty"`
|
||||
|
||||
// URI references the given URI, this makes the manifest alone
|
||||
// no-longer portable (but future offline manifest bundles
|
||||
// will fix that). It will still be reproducible as the
|
||||
// manifest will include all the hashes of the content so any
|
||||
// change will make the build fail.
|
||||
//
|
||||
// Initially only single files are supported, but this can be
|
||||
// expanded to dirs (which will just be added recursively) and
|
||||
// http{,s}.
|
||||
URI string `json:"uri,omitempty" toml:"uri,omitempty"`
|
||||
}
|
||||
|
||||
// Custom TOML unmarshalling for FileCustomization with validation
|
||||
func (f *FileCustomization) UnmarshalTOML(data interface{}) error {
|
||||
var file FileCustomization
|
||||
|
||||
dataMap, _ := data.(map[string]interface{})
|
||||
|
||||
switch path := dataMap["path"].(type) {
|
||||
case string:
|
||||
file.Path = path
|
||||
default:
|
||||
return fmt.Errorf("UnmarshalTOML: path must be a string")
|
||||
}
|
||||
|
||||
switch user := dataMap["user"].(type) {
|
||||
case string:
|
||||
file.User = user
|
||||
case int64:
|
||||
file.User = user
|
||||
case nil:
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("UnmarshalTOML: user must be a string or an integer")
|
||||
}
|
||||
|
||||
switch group := dataMap["group"].(type) {
|
||||
case string:
|
||||
file.Group = group
|
||||
case int64:
|
||||
file.Group = group
|
||||
case nil:
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("UnmarshalTOML: group must be a string or an integer")
|
||||
}
|
||||
|
||||
switch mode := dataMap["mode"].(type) {
|
||||
case string:
|
||||
file.Mode = mode
|
||||
case nil:
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("UnmarshalTOML: mode must be a string")
|
||||
}
|
||||
|
||||
switch data := dataMap["data"].(type) {
|
||||
case string:
|
||||
file.Data = data
|
||||
case nil:
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("UnmarshalTOML: data must be a string")
|
||||
}
|
||||
|
||||
switch uri := dataMap["uri"].(type) {
|
||||
case string:
|
||||
file.URI = uri
|
||||
case nil:
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("UnmarshalTOML: uri must be a string")
|
||||
}
|
||||
|
||||
// try converting to fsnode.File to validate all values
|
||||
_, err := file.ToFsNodeFile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*f = file
|
||||
return nil
|
||||
}
|
||||
|
||||
// Custom JSON unmarshalling for FileCustomization with validation
|
||||
func (f *FileCustomization) UnmarshalJSON(data []byte) error {
|
||||
type fileCustomization FileCustomization
|
||||
|
||||
var filePrivate fileCustomization
|
||||
if err := json.Unmarshal(data, &filePrivate); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file := FileCustomization(filePrivate)
|
||||
if uid, ok := file.User.(float64); ok {
|
||||
// check if uid can be converted to int64
|
||||
if uid != float64(int64(uid)) {
|
||||
return fmt.Errorf("invalid user %f: must be an integer", uid)
|
||||
}
|
||||
file.User = int64(uid)
|
||||
}
|
||||
if gid, ok := file.Group.(float64); ok {
|
||||
// check if gid can be converted to int64
|
||||
if gid != float64(int64(gid)) {
|
||||
return fmt.Errorf("invalid group %f: must be an integer", gid)
|
||||
}
|
||||
file.Group = int64(gid)
|
||||
}
|
||||
// try converting to fsnode.File to validate all values
|
||||
_, err := file.ToFsNodeFile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*f = file
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToFsNodeFile converts the FileCustomization to an fsnode.File
|
||||
func (f FileCustomization) ToFsNodeFile() (*fsnode.File, error) {
|
||||
if f.Data != "" && f.URI != "" {
|
||||
return nil, fmt.Errorf("cannot specify both data %q and URI %q", f.Data, f.URI)
|
||||
}
|
||||
|
||||
var data []byte
|
||||
if f.Data != "" {
|
||||
data = []byte(f.Data)
|
||||
}
|
||||
|
||||
var mode *os.FileMode
|
||||
if f.Mode != "" {
|
||||
err := validateModeString(f.Mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
modeNum, err := strconv.ParseUint(f.Mode, 8, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid mode %s: %v", f.Mode, err)
|
||||
}
|
||||
mode = common.ToPtr(os.FileMode(modeNum))
|
||||
}
|
||||
|
||||
if f.URI != "" {
|
||||
return fsnode.NewFileForURI(f.Path, mode, f.User, f.Group, f.URI)
|
||||
}
|
||||
return fsnode.NewFile(f.Path, mode, f.User, f.Group, data)
|
||||
}
|
||||
|
||||
// FileCustomizationsToFsNodeFiles converts a slice of FileCustomization to a slice of *fsnode.File
|
||||
func FileCustomizationsToFsNodeFiles(files []FileCustomization) ([]*fsnode.File, error) {
|
||||
if len(files) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var fsFiles []*fsnode.File
|
||||
var errors []error
|
||||
for _, file := range files {
|
||||
fsFile, err := file.ToFsNodeFile()
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
fsFiles = append(fsFiles, fsFile)
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return nil, fmt.Errorf("invalid file customizations: %v", errors)
|
||||
}
|
||||
|
||||
return fsFiles, nil
|
||||
}
|
||||
|
||||
// ValidateDirFileCustomizations validates the given Directory and File customizations.
|
||||
// If the customizations are invalid, an error is returned. Otherwise, nil is returned.
|
||||
//
|
||||
// It currently ensures that:
|
||||
// - No file path is a prefix of another file or directory path
|
||||
// - There are no duplicate file or directory paths in the customizations
|
||||
func ValidateDirFileCustomizations(dirs []DirectoryCustomization, files []FileCustomization) error {
|
||||
fsNodesMap := make(map[string]interface{}, len(dirs)+len(files))
|
||||
nodesPaths := make([]string, 0, len(dirs)+len(files))
|
||||
|
||||
// First check for duplicate paths
|
||||
duplicatePaths := make([]string, 0)
|
||||
for _, dir := range dirs {
|
||||
if _, ok := fsNodesMap[dir.Path]; ok {
|
||||
duplicatePaths = append(duplicatePaths, dir.Path)
|
||||
}
|
||||
fsNodesMap[dir.Path] = dir
|
||||
nodesPaths = append(nodesPaths, dir.Path)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if _, ok := fsNodesMap[file.Path]; ok {
|
||||
duplicatePaths = append(duplicatePaths, file.Path)
|
||||
}
|
||||
fsNodesMap[file.Path] = file
|
||||
nodesPaths = append(nodesPaths, file.Path)
|
||||
}
|
||||
|
||||
// There is no point in continuing if there are duplicate paths,
|
||||
// since the fsNodesMap will not be valid.
|
||||
if len(duplicatePaths) > 0 {
|
||||
return fmt.Errorf("duplicate files / directory customization paths: %v", duplicatePaths)
|
||||
}
|
||||
|
||||
invalidFSNodes := make([]string, 0)
|
||||
checkedPaths := make(map[string]bool)
|
||||
// Sort the paths so that we always check the longest paths first. This
|
||||
// ensures that we don't check a parent path before we check the child
|
||||
// path. Reverse sort the slice based on directory depth.
|
||||
sort.Slice(nodesPaths, func(i, j int) bool {
|
||||
return strings.Count(nodesPaths[i], "/") > strings.Count(nodesPaths[j], "/")
|
||||
})
|
||||
|
||||
for _, nodePath := range nodesPaths {
|
||||
// Skip paths that we have already checked
|
||||
if checkedPaths[nodePath] {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check all parent paths of the current path. If any of them have
|
||||
// already been checked, then we do not need to check them again.
|
||||
// This is because we always check the longest paths first. If a parent
|
||||
// path exists in the filesystem nodes map and it is a File,
|
||||
// then it is an error because it is a parent of a Directory or File.
|
||||
// Parent paths can be only Directories.
|
||||
parentPath := nodePath
|
||||
for {
|
||||
parentPath = path.Dir(parentPath)
|
||||
|
||||
// "." is returned only when the path is relative and we reached
|
||||
// the root directory. This should never happen because File
|
||||
// and Directory customization paths are validated as part of
|
||||
// the unmarshalling process from JSON and TOML.
|
||||
if parentPath == "." {
|
||||
panic("filesystem node has relative path set.")
|
||||
}
|
||||
|
||||
if parentPath == "/" {
|
||||
break
|
||||
}
|
||||
|
||||
if checkedPaths[parentPath] {
|
||||
break
|
||||
}
|
||||
|
||||
// If the node is not a Directory, then it is an error because
|
||||
// it is a parent of a Directory or File.
|
||||
if node, ok := fsNodesMap[parentPath]; ok {
|
||||
switch node.(type) {
|
||||
case DirectoryCustomization:
|
||||
break
|
||||
case FileCustomization:
|
||||
invalidFSNodes = append(invalidFSNodes, nodePath)
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected filesystem node customization type: %T", node))
|
||||
}
|
||||
}
|
||||
|
||||
checkedPaths[parentPath] = true
|
||||
}
|
||||
|
||||
checkedPaths[nodePath] = true
|
||||
}
|
||||
|
||||
if len(invalidFSNodes) > 0 {
|
||||
return fmt.Errorf("the following filesystem nodes are parents of another node and are not directories: %s", invalidFSNodes)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckFileCustomizationsPolicy checks if the given File customizations are allowed by the path policy.
|
||||
// If any of the customizations are not allowed by the path policy, an error is returned. Otherwise, nil is returned.
|
||||
func CheckFileCustomizationsPolicy(files []FileCustomization, pathPolicy *pathpolicy.PathPolicies) error {
|
||||
var invalidPaths []string
|
||||
for _, file := range files {
|
||||
if err := pathPolicy.Check(file.Path); err != nil {
|
||||
invalidPaths = append(invalidPaths, file.Path)
|
||||
}
|
||||
}
|
||||
|
||||
if len(invalidPaths) > 0 {
|
||||
return fmt.Errorf("the following custom files are not allowed: %+q", invalidPaths)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckDirectoryCustomizationsPolicy checks if the given Directory customizations are allowed by the path policy.
|
||||
// If any of the customizations are not allowed by the path policy, an error is returned. Otherwise, nil is returned.
|
||||
func CheckDirectoryCustomizationsPolicy(dirs []DirectoryCustomization, pathPolicy *pathpolicy.PathPolicies) error {
|
||||
var invalidPaths []string
|
||||
for _, dir := range dirs {
|
||||
if err := pathPolicy.Check(dir.Path); err != nil {
|
||||
invalidPaths = append(invalidPaths, dir.Path)
|
||||
}
|
||||
}
|
||||
|
||||
if len(invalidPaths) > 0 {
|
||||
return fmt.Errorf("the following custom directories are not allowed: %+q", invalidPaths)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
17
vendor/github.com/osbuild/images/pkg/blueprint/installer_customizations.go
generated
vendored
17
vendor/github.com/osbuild/images/pkg/blueprint/installer_customizations.go
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
package blueprint
|
||||
|
||||
type InstallerCustomization struct {
|
||||
Unattended bool `json:"unattended,omitempty" toml:"unattended,omitempty"`
|
||||
SudoNopasswd []string `json:"sudo-nopasswd,omitempty" toml:"sudo-nopasswd,omitempty"`
|
||||
Kickstart *Kickstart `json:"kickstart,omitempty" toml:"kickstart,omitempty"`
|
||||
Modules *AnacondaModules `json:"modules,omitempty" toml:"modules,omitempty"`
|
||||
}
|
||||
|
||||
type Kickstart struct {
|
||||
Contents string `json:"contents" toml:"contents"`
|
||||
}
|
||||
|
||||
type AnacondaModules struct {
|
||||
Enable []string `json:"enable,omitempty" toml:"enable,omitempty"`
|
||||
Disable []string `json:"disable,omitempty" toml:"disable,omitempty"`
|
||||
}
|
||||
154
vendor/github.com/osbuild/images/pkg/blueprint/repository_customizations.go
generated
vendored
154
vendor/github.com/osbuild/images/pkg/blueprint/repository_customizations.go
generated
vendored
|
|
@ -1,154 +0,0 @@
|
|||
package blueprint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/osbuild/images/internal/common"
|
||||
"github.com/osbuild/images/pkg/customizations/fsnode"
|
||||
"github.com/osbuild/images/pkg/rpmmd"
|
||||
)
|
||||
|
||||
type RepositoryCustomization struct {
|
||||
Id string `json:"id" toml:"id"`
|
||||
BaseURLs []string `json:"baseurls,omitempty" toml:"baseurls,omitempty"`
|
||||
GPGKeys []string `json:"gpgkeys,omitempty" toml:"gpgkeys,omitempty"`
|
||||
Metalink string `json:"metalink,omitempty" toml:"metalink,omitempty"`
|
||||
Mirrorlist string `json:"mirrorlist,omitempty" toml:"mirrorlist,omitempty"`
|
||||
Name string `json:"name,omitempty" toml:"name,omitempty"`
|
||||
Priority *int `json:"priority,omitempty" toml:"priority,omitempty"`
|
||||
Enabled *bool `json:"enabled,omitempty" toml:"enabled,omitempty"`
|
||||
GPGCheck *bool `json:"gpgcheck,omitempty" toml:"gpgcheck,omitempty"`
|
||||
RepoGPGCheck *bool `json:"repo_gpgcheck,omitempty" toml:"repo_gpgcheck,omitempty"`
|
||||
SSLVerify *bool `json:"sslverify,omitempty" toml:"sslverify,omitempty"`
|
||||
ModuleHotfixes *bool `json:"module_hotfixes,omitempty" toml:"module_hotfixes,omitempty"`
|
||||
Filename string `json:"filename,omitempty" toml:"filename,omitempty"`
|
||||
|
||||
// When set the repository will be used during the depsolve of
|
||||
// payload repositories to install packages from it.
|
||||
InstallFrom bool `json:"install_from" toml:"install_from"`
|
||||
}
|
||||
|
||||
const repoFilenameRegex = "^[\\w.-]{1,250}\\.repo$"
|
||||
|
||||
func validateCustomRepository(repo *RepositoryCustomization) error {
|
||||
if repo.Id == "" {
|
||||
return fmt.Errorf("Repository ID is required")
|
||||
}
|
||||
|
||||
filenameRegex := regexp.MustCompile(repoFilenameRegex)
|
||||
if !filenameRegex.MatchString(repo.getFilename()) {
|
||||
return fmt.Errorf("Repository filename %q is invalid", repo.getFilename())
|
||||
}
|
||||
|
||||
if len(repo.BaseURLs) == 0 && repo.Mirrorlist == "" && repo.Metalink == "" {
|
||||
return fmt.Errorf("Repository base URL, mirrorlist or metalink is required")
|
||||
}
|
||||
|
||||
if repo.GPGCheck != nil && *repo.GPGCheck && len(repo.GPGKeys) == 0 {
|
||||
return fmt.Errorf("Repository gpg check is set to true but no gpg keys are provided")
|
||||
}
|
||||
|
||||
for _, key := range repo.GPGKeys {
|
||||
// check for a valid GPG key prefix & contains GPG suffix
|
||||
keyIsGPGKey := strings.HasPrefix(key, "-----BEGIN PGP PUBLIC KEY BLOCK-----") && strings.Contains(key, "-----END PGP PUBLIC KEY BLOCK-----")
|
||||
|
||||
// check for a valid URL
|
||||
keyIsURL := false
|
||||
_, err := url.ParseRequestURI(key)
|
||||
if err == nil {
|
||||
keyIsURL = true
|
||||
}
|
||||
|
||||
if !keyIsGPGKey && !keyIsURL {
|
||||
return fmt.Errorf("Repository gpg key is not a valid URL or a valid gpg key")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rc *RepositoryCustomization) getFilename() string {
|
||||
if rc.Filename == "" {
|
||||
return fmt.Sprintf("%s.repo", rc.Id)
|
||||
}
|
||||
if !strings.HasSuffix(rc.Filename, ".repo") {
|
||||
return fmt.Sprintf("%s.repo", rc.Filename)
|
||||
}
|
||||
return rc.Filename
|
||||
}
|
||||
|
||||
func RepoCustomizationsInstallFromOnly(repos []RepositoryCustomization) []rpmmd.RepoConfig {
|
||||
var res []rpmmd.RepoConfig
|
||||
for _, repo := range repos {
|
||||
if !repo.InstallFrom {
|
||||
continue
|
||||
}
|
||||
res = append(res, repo.customRepoToRepoConfig())
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func RepoCustomizationsToRepoConfigAndGPGKeyFiles(repos []RepositoryCustomization) (map[string][]rpmmd.RepoConfig, []*fsnode.File, error) {
|
||||
if len(repos) == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
repoMap := make(map[string][]rpmmd.RepoConfig, len(repos))
|
||||
var gpgKeyFiles []*fsnode.File
|
||||
for _, repo := range repos {
|
||||
filename := repo.getFilename()
|
||||
convertedRepo := repo.customRepoToRepoConfig()
|
||||
|
||||
// convert any inline gpgkeys to fsnode.File and
|
||||
// replace the gpgkey with the file path
|
||||
for idx, gpgkey := range repo.GPGKeys {
|
||||
if _, ok := url.ParseRequestURI(gpgkey); ok != nil {
|
||||
// create the file path
|
||||
path := fmt.Sprintf("/etc/pki/rpm-gpg/RPM-GPG-KEY-%s-%d", repo.Id, idx)
|
||||
// replace the gpgkey with the file path
|
||||
convertedRepo.GPGKeys[idx] = fmt.Sprintf("file://%s", path)
|
||||
// create the fsnode for the gpgkey keyFile
|
||||
keyFile, err := fsnode.NewFile(path, nil, nil, nil, []byte(gpgkey))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
gpgKeyFiles = append(gpgKeyFiles, keyFile)
|
||||
}
|
||||
}
|
||||
|
||||
repoMap[filename] = append(repoMap[filename], convertedRepo)
|
||||
}
|
||||
|
||||
return repoMap, gpgKeyFiles, nil
|
||||
}
|
||||
|
||||
func (repo RepositoryCustomization) customRepoToRepoConfig() rpmmd.RepoConfig {
|
||||
urls := make([]string, len(repo.BaseURLs))
|
||||
copy(urls, repo.BaseURLs)
|
||||
|
||||
keys := make([]string, len(repo.GPGKeys))
|
||||
copy(keys, repo.GPGKeys)
|
||||
|
||||
repoConfig := rpmmd.RepoConfig{
|
||||
Id: repo.Id,
|
||||
BaseURLs: urls,
|
||||
GPGKeys: keys,
|
||||
Name: repo.Name,
|
||||
Metalink: repo.Metalink,
|
||||
MirrorList: repo.Mirrorlist,
|
||||
CheckGPG: repo.GPGCheck,
|
||||
CheckRepoGPG: repo.RepoGPGCheck,
|
||||
Priority: repo.Priority,
|
||||
ModuleHotfixes: repo.ModuleHotfixes,
|
||||
Enabled: repo.Enabled,
|
||||
}
|
||||
|
||||
if repo.SSLVerify != nil {
|
||||
repoConfig.IgnoreSSL = common.ToPtr(!*repo.SSLVerify)
|
||||
}
|
||||
|
||||
return repoConfig
|
||||
}
|
||||
36
vendor/github.com/osbuild/images/pkg/blueprint/rhsm_customizations.go
generated
vendored
36
vendor/github.com/osbuild/images/pkg/blueprint/rhsm_customizations.go
generated
vendored
|
|
@ -1,36 +0,0 @@
|
|||
package blueprint
|
||||
|
||||
// Subscription Manager [rhsm] configuration
|
||||
type SubManRHSMConfig struct {
|
||||
ManageRepos *bool `json:"manage_repos,omitempty" toml:"manage_repos,omitempty"`
|
||||
AutoEnableYumPlugins *bool `json:"auto_enable_yum_plugins,omitempty" toml:"auto_enable_yum_plugins,omitempty"`
|
||||
}
|
||||
|
||||
// Subscription Manager [rhsmcertd] configuration
|
||||
type SubManRHSMCertdConfig struct {
|
||||
AutoRegistration *bool `json:"auto_registration,omitempty" toml:"auto_registration,omitempty"`
|
||||
}
|
||||
|
||||
// Subscription Manager 'rhsm.conf' configuration
|
||||
type SubManConfig struct {
|
||||
RHSMConfig *SubManRHSMConfig `json:"rhsm,omitempty" toml:"rhsm,omitempty"`
|
||||
RHSMCertdConfig *SubManRHSMCertdConfig `json:"rhsmcertd,omitempty" toml:"rhsmcertd,omitempty"`
|
||||
}
|
||||
|
||||
type DNFPluginConfig struct {
|
||||
Enabled *bool `json:"enabled,omitempty" toml:"enabled,omitempty"`
|
||||
}
|
||||
|
||||
type SubManDNFPluginsConfig struct {
|
||||
ProductID *DNFPluginConfig `json:"product_id,omitempty" toml:"product_id,omitempty"`
|
||||
SubscriptionManager *DNFPluginConfig `json:"subscription_manager,omitempty" toml:"subscription_manager,omitempty"`
|
||||
}
|
||||
|
||||
type RHSMConfig struct {
|
||||
DNFPlugins *SubManDNFPluginsConfig `json:"dnf_plugins,omitempty" toml:"dnf_plugins,omitempty"`
|
||||
SubscriptionManager *SubManConfig `json:"subscription_manager,omitempty" toml:"subscription_manager,omitempty"`
|
||||
}
|
||||
|
||||
type RHSMCustomization struct {
|
||||
Config *RHSMConfig `json:"config,omitempty" toml:"config,omitempty"`
|
||||
}
|
||||
10
vendor/github.com/osbuild/images/pkg/blueprint/rpm_customizations.go
generated
vendored
10
vendor/github.com/osbuild/images/pkg/blueprint/rpm_customizations.go
generated
vendored
|
|
@ -1,10 +0,0 @@
|
|||
package blueprint
|
||||
|
||||
type RPMImportKeys struct {
|
||||
// File paths in the image to import keys from
|
||||
Files []string `json:"files,omitempty" toml:"files,omitempty"`
|
||||
}
|
||||
|
||||
type RPMCustomization struct {
|
||||
ImportKeys *RPMImportKeys `json:"import_keys,omitempty" toml:"import_keys,omitempty"`
|
||||
}
|
||||
24
vendor/github.com/osbuild/images/pkg/blueprint/toml_json_bridge.go
generated
vendored
24
vendor/github.com/osbuild/images/pkg/blueprint/toml_json_bridge.go
generated
vendored
|
|
@ -1,24 +0,0 @@
|
|||
package blueprint
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// XXX: move to interal/common ?
|
||||
func unmarshalTOMLviaJSON(u json.Unmarshaler, data any) error {
|
||||
// This is the most efficient way to reuse code when unmarshaling
|
||||
// structs in toml, it leaks json errors which is a bit sad but
|
||||
// because the toml unmarshaler gives us not "[]byte" but an
|
||||
// already pre-processed "any" we cannot just unmarshal into our
|
||||
// "fooMarshaling" struct and reuse the result so we resort to
|
||||
// this workaround (but toml will go away long term anyway).
|
||||
dataJSON, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error unmarshaling TOML data %v: %w", data, err)
|
||||
}
|
||||
if err := u.UnmarshalJSON(dataJSON); err != nil {
|
||||
return fmt.Errorf("error decoding TOML %v: %w", data, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
2
vendor/github.com/osbuild/images/pkg/customizations/fdo/fdo.go
generated
vendored
2
vendor/github.com/osbuild/images/pkg/customizations/fdo/fdo.go
generated
vendored
|
|
@ -1,6 +1,6 @@
|
|||
package fdo
|
||||
|
||||
import "github.com/osbuild/images/pkg/blueprint"
|
||||
import "github.com/osbuild/blueprint/pkg/blueprint"
|
||||
|
||||
type Options struct {
|
||||
ManufacturingServerURL string
|
||||
|
|
|
|||
2
vendor/github.com/osbuild/images/pkg/customizations/ignition/ignition.go
generated
vendored
2
vendor/github.com/osbuild/images/pkg/customizations/ignition/ignition.go
generated
vendored
|
|
@ -4,7 +4,7 @@ import (
|
|||
"encoding/base64"
|
||||
"errors"
|
||||
|
||||
"github.com/osbuild/images/pkg/blueprint"
|
||||
"github.com/osbuild/blueprint/pkg/blueprint"
|
||||
)
|
||||
|
||||
type FirstBootOptions struct {
|
||||
|
|
|
|||
2
vendor/github.com/osbuild/images/pkg/customizations/kickstart/kickstart.go
generated
vendored
2
vendor/github.com/osbuild/images/pkg/customizations/kickstart/kickstart.go
generated
vendored
|
|
@ -3,7 +3,7 @@ package kickstart
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/osbuild/images/pkg/blueprint"
|
||||
"github.com/osbuild/blueprint/pkg/blueprint"
|
||||
"github.com/osbuild/images/pkg/customizations/users"
|
||||
)
|
||||
|
||||
|
|
|
|||
2
vendor/github.com/osbuild/images/pkg/customizations/oscap/oscap.go
generated
vendored
2
vendor/github.com/osbuild/images/pkg/customizations/oscap/oscap.go
generated
vendored
|
|
@ -5,7 +5,7 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/osbuild/images/pkg/blueprint"
|
||||
"github.com/osbuild/blueprint/pkg/blueprint"
|
||||
)
|
||||
|
||||
type Profile string
|
||||
|
|
|
|||
2
vendor/github.com/osbuild/images/pkg/customizations/subscription/subscription.go
generated
vendored
2
vendor/github.com/osbuild/images/pkg/customizations/subscription/subscription.go
generated
vendored
|
|
@ -1,8 +1,8 @@
|
|||
package subscription
|
||||
|
||||
import (
|
||||
"github.com/osbuild/blueprint/pkg/blueprint"
|
||||
"github.com/osbuild/images/internal/common"
|
||||
"github.com/osbuild/images/pkg/blueprint"
|
||||
)
|
||||
|
||||
// The ImageOptions specify subscription-specific image options
|
||||
|
|
|
|||
2
vendor/github.com/osbuild/images/pkg/customizations/users/users.go
generated
vendored
2
vendor/github.com/osbuild/images/pkg/customizations/users/users.go
generated
vendored
|
|
@ -1,6 +1,6 @@
|
|||
package users
|
||||
|
||||
import "github.com/osbuild/images/pkg/blueprint"
|
||||
import "github.com/osbuild/blueprint/pkg/blueprint"
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
|
|
|
|||
2
vendor/github.com/osbuild/images/pkg/disk/partition_table.go
generated
vendored
2
vendor/github.com/osbuild/images/pkg/disk/partition_table.go
generated
vendored
|
|
@ -7,9 +7,9 @@ import (
|
|||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/osbuild/blueprint/pkg/blueprint"
|
||||
"github.com/osbuild/images/internal/common"
|
||||
"github.com/osbuild/images/pkg/arch"
|
||||
"github.com/osbuild/images/pkg/blueprint"
|
||||
"github.com/osbuild/images/pkg/datasizes"
|
||||
"github.com/osbuild/images/pkg/disk/partition"
|
||||
"github.com/osbuild/images/pkg/platform"
|
||||
|
|
|
|||
2
vendor/github.com/osbuild/images/pkg/distro/distro.go
generated
vendored
2
vendor/github.com/osbuild/images/pkg/distro/distro.go
generated
vendored
|
|
@ -3,7 +3,7 @@ package distro
|
|||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/osbuild/images/pkg/blueprint"
|
||||
"github.com/osbuild/blueprint/pkg/blueprint"
|
||||
"github.com/osbuild/images/pkg/customizations/subscription"
|
||||
"github.com/osbuild/images/pkg/disk"
|
||||
"github.com/osbuild/images/pkg/disk/partition"
|
||||
|
|
|
|||
2
vendor/github.com/osbuild/images/pkg/distro/generic/images.go
generated
vendored
2
vendor/github.com/osbuild/images/pkg/distro/generic/images.go
generated
vendored
|
|
@ -5,8 +5,8 @@ import (
|
|||
"math/rand"
|
||||
"strings"
|
||||
|
||||
"github.com/osbuild/blueprint/pkg/blueprint"
|
||||
"github.com/osbuild/images/internal/workload"
|
||||
"github.com/osbuild/images/pkg/blueprint"
|
||||
"github.com/osbuild/images/pkg/container"
|
||||
"github.com/osbuild/images/pkg/customizations/anaconda"
|
||||
"github.com/osbuild/images/pkg/customizations/bootc"
|
||||
|
|
|
|||
2
vendor/github.com/osbuild/images/pkg/distro/generic/imagetype.go
generated
vendored
2
vendor/github.com/osbuild/images/pkg/distro/generic/imagetype.go
generated
vendored
|
|
@ -6,9 +6,9 @@ import (
|
|||
"math/rand"
|
||||
"slices"
|
||||
|
||||
"github.com/osbuild/blueprint/pkg/blueprint"
|
||||
"github.com/osbuild/images/internal/common"
|
||||
"github.com/osbuild/images/internal/workload"
|
||||
"github.com/osbuild/images/pkg/blueprint"
|
||||
"github.com/osbuild/images/pkg/container"
|
||||
"github.com/osbuild/images/pkg/datasizes"
|
||||
"github.com/osbuild/images/pkg/disk"
|
||||
|
|
|
|||
2
vendor/github.com/osbuild/images/pkg/distro/generic/options.go
generated
vendored
2
vendor/github.com/osbuild/images/pkg/distro/generic/options.go
generated
vendored
|
|
@ -5,9 +5,9 @@ import (
|
|||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/osbuild/blueprint/pkg/blueprint"
|
||||
"github.com/osbuild/images/internal/common"
|
||||
"github.com/osbuild/images/pkg/arch"
|
||||
"github.com/osbuild/images/pkg/blueprint"
|
||||
"github.com/osbuild/images/pkg/customizations/oscap"
|
||||
"github.com/osbuild/images/pkg/distro"
|
||||
"github.com/osbuild/images/pkg/policies"
|
||||
|
|
|
|||
2
vendor/github.com/osbuild/images/pkg/distro/test_distro/distro.go
generated
vendored
2
vendor/github.com/osbuild/images/pkg/distro/test_distro/distro.go
generated
vendored
|
|
@ -5,7 +5,7 @@ import (
|
|||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/osbuild/images/pkg/blueprint"
|
||||
"github.com/osbuild/blueprint/pkg/blueprint"
|
||||
"github.com/osbuild/images/pkg/disk"
|
||||
"github.com/osbuild/images/pkg/distro"
|
||||
"github.com/osbuild/images/pkg/manifest"
|
||||
|
|
|
|||
5
vendor/modules.txt
vendored
5
vendor/modules.txt
vendored
|
|
@ -957,11 +957,11 @@ github.com/oracle/oci-go-sdk/v54/identity
|
|||
github.com/oracle/oci-go-sdk/v54/objectstorage
|
||||
github.com/oracle/oci-go-sdk/v54/objectstorage/transfer
|
||||
github.com/oracle/oci-go-sdk/v54/workrequests
|
||||
# github.com/osbuild/blueprint v1.11.0
|
||||
# github.com/osbuild/blueprint v1.12.0
|
||||
## explicit; go 1.23.9
|
||||
github.com/osbuild/blueprint/internal/common
|
||||
github.com/osbuild/blueprint/pkg/blueprint
|
||||
# github.com/osbuild/images v0.171.0
|
||||
# github.com/osbuild/images v0.172.0
|
||||
## explicit; go 1.23.9
|
||||
github.com/osbuild/images/data/dependencies
|
||||
github.com/osbuild/images/data/distrodefs
|
||||
|
|
@ -971,7 +971,6 @@ github.com/osbuild/images/internal/environment
|
|||
github.com/osbuild/images/internal/workload
|
||||
github.com/osbuild/images/pkg/arch
|
||||
github.com/osbuild/images/pkg/artifact
|
||||
github.com/osbuild/images/pkg/blueprint
|
||||
github.com/osbuild/images/pkg/cert
|
||||
github.com/osbuild/images/pkg/cloud/azure
|
||||
github.com/osbuild/images/pkg/container
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue