split: replace internal packages with images library
Remove all the internal package that are now in the github.com/osbuild/images package and vendor it. A new function in internal/blueprint/ converts from an osbuild-composer blueprint to an images blueprint. This is necessary for keeping the blueprint implementation in both packages. In the future, the images package will change the blueprint (and most likely rename it) and it will only be part of the osbuild-composer internals and interface. The Convert() function will be responsible for converting the blueprint into the new configuration object.
This commit is contained in:
parent
d59199670f
commit
0e4a9e586f
446 changed files with 5690 additions and 13312 deletions
|
|
@ -1,31 +0,0 @@
|
|||
package artifact
|
||||
|
||||
type Artifact struct {
|
||||
export string
|
||||
filename string
|
||||
mimeType string
|
||||
}
|
||||
|
||||
func New(export, filename string, mimeType *string) *Artifact {
|
||||
artifact := &Artifact{
|
||||
export: export,
|
||||
filename: filename,
|
||||
mimeType: "application/octet-stream",
|
||||
}
|
||||
if mimeType != nil {
|
||||
artifact.mimeType = *mimeType
|
||||
}
|
||||
return artifact
|
||||
}
|
||||
|
||||
func (a *Artifact) Export() string {
|
||||
return a.export
|
||||
}
|
||||
|
||||
func (a *Artifact) Filename() string {
|
||||
return a.filename
|
||||
}
|
||||
|
||||
func (a *Artifact) MIMEType() string {
|
||||
return a.mimeType
|
||||
}
|
||||
|
|
@ -5,9 +5,10 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/crypt"
|
||||
"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.
|
||||
|
|
@ -182,3 +183,150 @@ func (b *Blueprint) CryptPasswords() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert(bp Blueprint) iblueprint.Blueprint {
|
||||
pkgs := make([]iblueprint.Package, len(bp.Packages))
|
||||
for idx := range bp.Packages {
|
||||
pkgs[idx] = iblueprint.Package(bp.Packages[idx])
|
||||
}
|
||||
|
||||
modules := make([]iblueprint.Package, len(bp.Modules))
|
||||
for idx := range bp.Modules {
|
||||
modules[idx] = iblueprint.Package(bp.Modules[idx])
|
||||
}
|
||||
|
||||
groups := make([]iblueprint.Group, len(bp.Groups))
|
||||
for idx := range bp.Groups {
|
||||
groups[idx] = iblueprint.Group(bp.Groups[idx])
|
||||
}
|
||||
|
||||
containers := make([]iblueprint.Container, len(bp.Containers))
|
||||
for idx := range bp.Containers {
|
||||
containers[idx] = iblueprint.Container(bp.Containers[idx])
|
||||
}
|
||||
|
||||
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(*oscap)
|
||||
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 sshkeys := c.SSHKey; sshkeys != nil {
|
||||
isshkeys := make([]iblueprint.SSHKeyCustomization, len(sshkeys))
|
||||
for idx := range sshkeys {
|
||||
isshkeys[idx] = iblueprint.SSHKeyCustomization(sshkeys[idx])
|
||||
}
|
||||
customizations.SSHKey = isshkeys
|
||||
}
|
||||
if users := c.User; users != nil {
|
||||
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 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
|
||||
}
|
||||
}
|
||||
|
||||
ibp := iblueprint.Blueprint{
|
||||
Name: bp.Name,
|
||||
Description: bp.Description,
|
||||
Version: bp.Version,
|
||||
Packages: pkgs,
|
||||
Modules: modules,
|
||||
Groups: groups,
|
||||
Containers: containers,
|
||||
Customizations: &customizations,
|
||||
Distro: bp.Distro,
|
||||
}
|
||||
|
||||
return ibp
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import (
|
|||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/osbuild/images/pkg/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/fsnode"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
type RepositoryCustomization struct {
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/osbuild/images/pkg/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/fsnode"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import (
|
|||
"net/http"
|
||||
// "strings"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/images/pkg/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/weldr"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import (
|
|||
"net/http"
|
||||
// "strings"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/images/pkg/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/weldr"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/distro/test_distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/distroregistry"
|
||||
"github.com/osbuild/images/pkg/distro/test_distro"
|
||||
"github.com/osbuild/images/pkg/distroregistry"
|
||||
"github.com/osbuild/images/pkg/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/dnfjson"
|
||||
dnfjson_mock "github.com/osbuild/osbuild-composer/internal/mocks/dnfjson"
|
||||
rpmmd_mock "github.com/osbuild/osbuild-composer/internal/mocks/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/reporegistry"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/weldr"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package cloudapi
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/distroregistry"
|
||||
"github.com/osbuild/images/pkg/distroregistry"
|
||||
"github.com/osbuild/osbuild-composer/internal/worker"
|
||||
|
||||
v2 "github.com/osbuild/osbuild-composer/internal/cloudapi/v2"
|
||||
|
|
|
|||
|
|
@ -15,15 +15,15 @@ import (
|
|||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"github.com/osbuild/images/pkg/distro"
|
||||
"github.com/osbuild/images/pkg/manifest"
|
||||
"github.com/osbuild/images/pkg/osbuild"
|
||||
"github.com/osbuild/images/pkg/ostree"
|
||||
"github.com/osbuild/images/pkg/rhsm/facts"
|
||||
"github.com/osbuild/images/pkg/rpmmd"
|
||||
"github.com/osbuild/images/pkg/subscription"
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/ostree"
|
||||
"github.com/osbuild/osbuild-composer/internal/rhsm/facts"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/subscription"
|
||||
"github.com/osbuild/osbuild-composer/internal/target"
|
||||
"github.com/osbuild/osbuild-composer/internal/worker"
|
||||
"github.com/osbuild/osbuild-composer/internal/worker/clienterrors"
|
||||
|
|
|
|||
|
|
@ -19,14 +19,14 @@ import (
|
|||
|
||||
"github.com/osbuild/osbuild-composer/pkg/jobqueue"
|
||||
|
||||
"github.com/osbuild/images/pkg/container"
|
||||
"github.com/osbuild/images/pkg/distro"
|
||||
"github.com/osbuild/images/pkg/distroregistry"
|
||||
"github.com/osbuild/images/pkg/manifest"
|
||||
"github.com/osbuild/images/pkg/ostree"
|
||||
"github.com/osbuild/osbuild-composer/internal/auth"
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/container"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/distroregistry"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/ostree"
|
||||
"github.com/osbuild/osbuild-composer/internal/prometheus"
|
||||
"github.com/osbuild/osbuild-composer/internal/target"
|
||||
"github.com/osbuild/osbuild-composer/internal/worker"
|
||||
|
|
@ -117,7 +117,8 @@ func (s *Server) enqueueCompose(distribution distro.Distro, bp blueprint.Bluepri
|
|||
}
|
||||
ir := irs[0]
|
||||
|
||||
manifestSource, _, err := ir.imageType.Manifest(&bp, ir.imageOptions, ir.repositories, manifestSeed)
|
||||
ibp := blueprint.Convert(bp)
|
||||
manifestSource, _, err := ir.imageType.Manifest(&ibp, ir.imageOptions, ir.repositories, manifestSeed)
|
||||
if err != nil {
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
|
||||
}
|
||||
|
|
@ -237,7 +238,8 @@ func (s *Server) enqueueKojiCompose(taskID uint64, server, name, version, releas
|
|||
var kojiFilenames []string
|
||||
var buildIDs []uuid.UUID
|
||||
for _, ir := range irs {
|
||||
manifestSource, _, err := ir.imageType.Manifest(&bp, ir.imageOptions, ir.repositories, manifestSeed)
|
||||
ibp := blueprint.Convert(bp)
|
||||
manifestSource, _, err := ir.imageType.Manifest(&ibp, ir.imageOptions, ir.repositories, manifestSeed)
|
||||
if err != nil {
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
|
||||
}
|
||||
|
|
|
|||
564
internal/cloudapi/v2/server.go.orig
Normal file
564
internal/cloudapi/v2/server.go.orig
Normal file
|
|
@ -0,0 +1,564 @@
|
|||
package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/getkin/kin-openapi/routers"
|
||||
legacyrouter "github.com/getkin/kin-openapi/routers/legacy"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/pkg/jobqueue"
|
||||
|
||||
<<<<<<< HEAD
|
||||
"github.com/osbuild/osbuild-composer/internal/auth"
|
||||
=======
|
||||
"github.com/osbuild/images/pkg/container"
|
||||
"github.com/osbuild/images/pkg/distro"
|
||||
"github.com/osbuild/images/pkg/distroregistry"
|
||||
"github.com/osbuild/images/pkg/manifest"
|
||||
"github.com/osbuild/images/pkg/ostree"
|
||||
>>>>>>> 294a8e564 (split: wip)
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/prometheus"
|
||||
"github.com/osbuild/osbuild-composer/internal/target"
|
||||
"github.com/osbuild/osbuild-composer/internal/worker"
|
||||
"github.com/osbuild/osbuild-composer/internal/worker/clienterrors"
|
||||
)
|
||||
|
||||
// Server represents the state of the cloud Server
|
||||
type Server struct {
|
||||
workers *worker.Server
|
||||
distros *distroregistry.Registry
|
||||
config ServerConfig
|
||||
router routers.Router
|
||||
|
||||
goroutinesCtx context.Context
|
||||
goroutinesCtxCancel context.CancelFunc
|
||||
goroutinesGroup sync.WaitGroup
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
TenantProviderFields []string
|
||||
JWTEnabled bool
|
||||
}
|
||||
|
||||
func NewServer(workers *worker.Server, distros *distroregistry.Registry, config ServerConfig) *Server {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
spec, err := GetSwagger()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
loader := openapi3.NewLoader()
|
||||
if err := spec.Validate(loader.Context); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
router, err := legacyrouter.NewRouter(spec)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
server := &Server{
|
||||
workers: workers,
|
||||
distros: distros,
|
||||
config: config,
|
||||
router: router,
|
||||
|
||||
goroutinesCtx: ctx,
|
||||
goroutinesCtxCancel: cancel,
|
||||
}
|
||||
return server
|
||||
}
|
||||
|
||||
func (s *Server) Handler(path string) http.Handler {
|
||||
e := echo.New()
|
||||
e.Binder = binder{}
|
||||
e.HTTPErrorHandler = s.HTTPErrorHandler
|
||||
e.Pre(common.OperationIDMiddleware)
|
||||
e.Use(middleware.Recover())
|
||||
e.Logger = common.Logger()
|
||||
|
||||
handler := apiHandlers{
|
||||
server: s,
|
||||
}
|
||||
|
||||
mws := []echo.MiddlewareFunc{
|
||||
prometheus.StatusMiddleware(prometheus.ComposerSubsystem),
|
||||
}
|
||||
if s.config.JWTEnabled {
|
||||
mws = append(mws, auth.TenantChannelMiddleware(s.config.TenantProviderFields, HTTPError(ErrorTenantNotFound)))
|
||||
}
|
||||
mws = append(mws,
|
||||
prometheus.HTTPDurationMiddleware(prometheus.ComposerSubsystem),
|
||||
prometheus.MetricsMiddleware, s.ValidateRequest)
|
||||
RegisterHandlers(e.Group(path, mws...), &handler)
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (s *Server) Shutdown() {
|
||||
s.goroutinesCtxCancel()
|
||||
s.goroutinesGroup.Wait()
|
||||
}
|
||||
|
||||
func (s *Server) enqueueCompose(distribution distro.Distro, bp blueprint.Blueprint, manifestSeed int64, irs []imageRequest, channel string) (uuid.UUID, error) {
|
||||
var id uuid.UUID
|
||||
if len(irs) != 1 {
|
||||
return id, HTTPError(ErrorInvalidNumberOfImageBuilds)
|
||||
}
|
||||
ir := irs[0]
|
||||
|
||||
ibp := blueprint.Convert(bp)
|
||||
manifestSource, _, err := ir.imageType.Manifest(&ibp, ir.imageOptions, ir.repositories, manifestSeed)
|
||||
if err != nil {
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
|
||||
}
|
||||
|
||||
depsolveJobID, err := s.workers.EnqueueDepsolve(&worker.DepsolveJob{
|
||||
PackageSets: manifestSource.GetPackageSetChains(),
|
||||
ModulePlatformID: distribution.ModulePlatformID(),
|
||||
Arch: ir.arch.Name(),
|
||||
Releasever: distribution.Releasever(),
|
||||
}, channel)
|
||||
if err != nil {
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
|
||||
}
|
||||
dependencies := []uuid.UUID{depsolveJobID}
|
||||
|
||||
var containerResolveJobID uuid.UUID
|
||||
containerSources := manifestSource.GetContainerSourceSpecs()
|
||||
if len(containerSources) > 1 {
|
||||
// only one pipeline can embed containers
|
||||
pipelines := make([]string, 0, len(containerSources))
|
||||
for name := range containerSources {
|
||||
pipelines = append(pipelines, name)
|
||||
}
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, fmt.Errorf("manifest returned %d pipelines with containers (at most 1 is supported): %s", len(containerSources), strings.Join(pipelines, ", ")))
|
||||
}
|
||||
|
||||
for _, sources := range containerSources {
|
||||
workerResolveSpecs := make([]worker.ContainerSpec, len(sources))
|
||||
for idx, source := range sources {
|
||||
workerResolveSpecs[idx] = worker.ContainerSpec{
|
||||
Source: source.Source,
|
||||
Name: source.Name,
|
||||
TLSVerify: source.TLSVerify,
|
||||
}
|
||||
}
|
||||
|
||||
job := worker.ContainerResolveJob{
|
||||
Arch: ir.arch.Name(),
|
||||
Specs: workerResolveSpecs,
|
||||
}
|
||||
|
||||
jobId, err := s.workers.EnqueueContainerResolveJob(&job, channel)
|
||||
if err != nil {
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
|
||||
}
|
||||
|
||||
containerResolveJobID = jobId
|
||||
dependencies = append(dependencies, containerResolveJobID)
|
||||
break // there can be only one
|
||||
}
|
||||
|
||||
var ostreeResolveJobID uuid.UUID
|
||||
commitSources := manifestSource.GetOSTreeSourceSpecs()
|
||||
if len(commitSources) > 1 {
|
||||
// only one pipeline can specify an ostree commit for content
|
||||
pipelines := make([]string, 0, len(commitSources))
|
||||
for name := range commitSources {
|
||||
pipelines = append(pipelines, name)
|
||||
}
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, fmt.Errorf("manifest returned %d pipelines with ostree commits (at most 1 is supported): %s", len(commitSources), strings.Join(pipelines, ", ")))
|
||||
}
|
||||
for _, sources := range commitSources {
|
||||
workerResolveSpecs := make([]worker.OSTreeResolveSpec, len(sources))
|
||||
for idx, source := range sources {
|
||||
// ostree.SourceSpec is directly convertible to worker.OSTreeResolveSpec
|
||||
workerResolveSpecs[idx] = worker.OSTreeResolveSpec(source)
|
||||
}
|
||||
jobID, err := s.workers.EnqueueOSTreeResolveJob(&worker.OSTreeResolveJob{Specs: workerResolveSpecs}, channel)
|
||||
if err != nil {
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
|
||||
}
|
||||
|
||||
ostreeResolveJobID = jobID
|
||||
dependencies = append(dependencies, ostreeResolveJobID)
|
||||
break // there can be only one
|
||||
}
|
||||
|
||||
manifestJobID, err := s.workers.EnqueueManifestJobByID(&worker.ManifestJobByID{}, dependencies, channel)
|
||||
if err != nil {
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
|
||||
}
|
||||
|
||||
id, err = s.workers.EnqueueOSBuildAsDependency(ir.arch.Name(), &worker.OSBuildJob{
|
||||
Targets: []*target.Target{ir.target},
|
||||
PipelineNames: &worker.PipelineNames{
|
||||
Build: ir.imageType.BuildPipelines(),
|
||||
Payload: ir.imageType.PayloadPipelines(),
|
||||
},
|
||||
}, []uuid.UUID{manifestJobID}, channel)
|
||||
if err != nil {
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
|
||||
}
|
||||
|
||||
s.goroutinesGroup.Add(1)
|
||||
go func() {
|
||||
serializeManifest(s.goroutinesCtx, manifestSource, s.workers, depsolveJobID, containerResolveJobID, ostreeResolveJobID, manifestJobID, manifestSeed)
|
||||
defer s.goroutinesGroup.Done()
|
||||
}()
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (s *Server) enqueueKojiCompose(taskID uint64, server, name, version, release string, distribution distro.Distro, bp blueprint.Blueprint, manifestSeed int64, irs []imageRequest, channel string) (uuid.UUID, error) {
|
||||
var id uuid.UUID
|
||||
kojiDirectory := "osbuild-cg/osbuild-composer-koji-" + uuid.New().String()
|
||||
|
||||
initID, err := s.workers.EnqueueKojiInit(&worker.KojiInitJob{
|
||||
Server: server,
|
||||
Name: name,
|
||||
Version: version,
|
||||
Release: release,
|
||||
}, channel)
|
||||
if err != nil {
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
|
||||
}
|
||||
|
||||
var kojiFilenames []string
|
||||
var buildIDs []uuid.UUID
|
||||
for _, ir := range irs {
|
||||
ibp := blueprint.Convert(bp)
|
||||
manifestSource, _, err := ir.imageType.Manifest(&ibp, ir.imageOptions, ir.repositories, manifestSeed)
|
||||
if err != nil {
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
|
||||
}
|
||||
|
||||
depsolveJobID, err := s.workers.EnqueueDepsolve(&worker.DepsolveJob{
|
||||
PackageSets: manifestSource.GetPackageSetChains(),
|
||||
ModulePlatformID: distribution.ModulePlatformID(),
|
||||
Arch: ir.arch.Name(),
|
||||
Releasever: distribution.Releasever(),
|
||||
}, channel)
|
||||
if err != nil {
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
|
||||
}
|
||||
dependencies := []uuid.UUID{depsolveJobID}
|
||||
|
||||
var containerResolveJobID uuid.UUID
|
||||
containerSources := manifestSource.GetContainerSourceSpecs()
|
||||
if len(containerSources) > 1 {
|
||||
// only one pipeline can embed containers
|
||||
pipelines := make([]string, 0, len(containerSources))
|
||||
for name := range containerSources {
|
||||
pipelines = append(pipelines, name)
|
||||
}
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, fmt.Errorf("manifest returned %d pipelines with containers (at most 1 is supported): %s", len(containerSources), strings.Join(pipelines, ", ")))
|
||||
}
|
||||
|
||||
for _, sources := range containerSources {
|
||||
workerResolveSpecs := make([]worker.ContainerSpec, len(sources))
|
||||
for idx, source := range sources {
|
||||
workerResolveSpecs[idx] = worker.ContainerSpec{
|
||||
Source: source.Source,
|
||||
Name: source.Name,
|
||||
TLSVerify: source.TLSVerify,
|
||||
}
|
||||
}
|
||||
|
||||
job := worker.ContainerResolveJob{
|
||||
Arch: ir.arch.Name(),
|
||||
Specs: make([]worker.ContainerSpec, len(bp.Containers)),
|
||||
}
|
||||
|
||||
jobId, err := s.workers.EnqueueContainerResolveJob(&job, channel)
|
||||
if err != nil {
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
|
||||
}
|
||||
|
||||
containerResolveJobID = jobId
|
||||
dependencies = append(dependencies, containerResolveJobID)
|
||||
break // there can be only one
|
||||
}
|
||||
|
||||
var ostreeResolveJobID uuid.UUID
|
||||
commitSources := manifestSource.GetOSTreeSourceSpecs()
|
||||
if len(commitSources) > 1 {
|
||||
// only one pipeline can specify an ostree commit for content
|
||||
pipelines := make([]string, 0, len(commitSources))
|
||||
for name := range commitSources {
|
||||
pipelines = append(pipelines, name)
|
||||
}
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, fmt.Errorf("manifest returned %d pipelines with ostree commits (at most 1 is supported): %s", len(commitSources), strings.Join(pipelines, ", ")))
|
||||
}
|
||||
for _, sources := range commitSources {
|
||||
workerResolveSpecs := make([]worker.OSTreeResolveSpec, len(sources))
|
||||
for idx, source := range sources {
|
||||
// ostree.SourceSpec is directly convertible to worker.OSTreeResolveSpec
|
||||
workerResolveSpecs[idx] = worker.OSTreeResolveSpec(source)
|
||||
}
|
||||
jobID, err := s.workers.EnqueueOSTreeResolveJob(&worker.OSTreeResolveJob{Specs: workerResolveSpecs}, channel)
|
||||
if err != nil {
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
|
||||
}
|
||||
|
||||
ostreeResolveJobID = jobID
|
||||
dependencies = append(dependencies, ostreeResolveJobID)
|
||||
break // there can be only one
|
||||
}
|
||||
|
||||
manifestJobID, err := s.workers.EnqueueManifestJobByID(&worker.ManifestJobByID{}, dependencies, channel)
|
||||
if err != nil {
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
|
||||
}
|
||||
kojiFilename := fmt.Sprintf(
|
||||
"%s-%s-%s.%s%s",
|
||||
name,
|
||||
version,
|
||||
release,
|
||||
ir.arch.Name(),
|
||||
splitExtension(ir.imageType.Filename()),
|
||||
)
|
||||
|
||||
kojiTarget := target.NewKojiTarget(&target.KojiTargetOptions{
|
||||
Server: server,
|
||||
UploadDirectory: kojiDirectory,
|
||||
})
|
||||
kojiTarget.OsbuildArtifact.ExportFilename = ir.imageType.Filename()
|
||||
kojiTarget.OsbuildArtifact.ExportName = ir.imageType.Exports()[0]
|
||||
kojiTarget.ImageName = kojiFilename
|
||||
|
||||
targets := []*target.Target{kojiTarget}
|
||||
// add any cloud upload target if defined
|
||||
if ir.target != nil {
|
||||
targets = append(targets, ir.target)
|
||||
}
|
||||
|
||||
buildID, err := s.workers.EnqueueOSBuildAsDependency(ir.arch.Name(), &worker.OSBuildJob{
|
||||
PipelineNames: &worker.PipelineNames{
|
||||
Build: ir.imageType.BuildPipelines(),
|
||||
Payload: ir.imageType.PayloadPipelines(),
|
||||
},
|
||||
Targets: targets,
|
||||
ManifestDynArgsIdx: common.ToPtr(1),
|
||||
}, []uuid.UUID{initID, manifestJobID}, channel)
|
||||
if err != nil {
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
|
||||
}
|
||||
kojiFilenames = append(kojiFilenames, kojiFilename)
|
||||
buildIDs = append(buildIDs, buildID)
|
||||
|
||||
// copy the image request while passing it into the goroutine to prevent data races
|
||||
s.goroutinesGroup.Add(1)
|
||||
go func(ir imageRequest) {
|
||||
serializeManifest(s.goroutinesCtx, manifestSource, s.workers, depsolveJobID, containerResolveJobID, ostreeResolveJobID, manifestJobID, manifestSeed)
|
||||
defer s.goroutinesGroup.Done()
|
||||
}(ir)
|
||||
}
|
||||
id, err = s.workers.EnqueueKojiFinalize(&worker.KojiFinalizeJob{
|
||||
Server: server,
|
||||
Name: name,
|
||||
Version: version,
|
||||
Release: release,
|
||||
KojiFilenames: kojiFilenames,
|
||||
KojiDirectory: kojiDirectory,
|
||||
TaskID: taskID,
|
||||
StartTime: uint64(time.Now().Unix()),
|
||||
}, initID, buildIDs, channel)
|
||||
if err != nil {
|
||||
return id, HTTPErrorWithInternal(ErrorEnqueueingJob, err)
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func serializeManifest(ctx context.Context, manifestSource *manifest.Manifest, workers *worker.Server, depsolveJobID, containerResolveJobID, ostreeResolveJobID, manifestJobID uuid.UUID, seed int64) {
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Minute*5)
|
||||
defer cancel()
|
||||
|
||||
// wait until job is in a pending state
|
||||
var token uuid.UUID
|
||||
var dynArgs []json.RawMessage
|
||||
var err error
|
||||
logWithId := logrus.WithField("jobId", manifestJobID)
|
||||
for {
|
||||
_, token, _, _, dynArgs, err = workers.RequestJobById(ctx, "", manifestJobID)
|
||||
if err == jobqueue.ErrNotPending {
|
||||
logWithId.Debug("Manifest job not pending, waiting for depsolve job to finish")
|
||||
time.Sleep(time.Millisecond * 50)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
logWithId.Warning("Manifest job dependencies took longer than 5 minutes to finish, or the server is shutting down, returning to avoid dangling routines")
|
||||
break
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
logWithId.Errorf("Error requesting manifest job: %v", err)
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
jobResult := &worker.ManifestJobByIDResult{
|
||||
Manifest: nil,
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if jobResult.JobError != nil {
|
||||
logWithId.Errorf("Error in manifest job %v: %v", jobResult.JobError.Reason, err)
|
||||
}
|
||||
|
||||
result, err := json.Marshal(jobResult)
|
||||
if err != nil {
|
||||
logWithId.Errorf("Error marshalling manifest job results: %v", err)
|
||||
}
|
||||
|
||||
err = workers.FinishJob(token, result)
|
||||
if err != nil {
|
||||
logWithId.Errorf("Error finishing manifest job: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if len(dynArgs) == 0 {
|
||||
reason := "No dynamic arguments"
|
||||
jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorNoDynamicArgs, reason, nil)
|
||||
return
|
||||
}
|
||||
|
||||
var depsolveResults worker.DepsolveJobResult
|
||||
err = json.Unmarshal(dynArgs[0], &depsolveResults)
|
||||
if err != nil {
|
||||
reason := "Error parsing dynamic arguments"
|
||||
jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorParsingDynamicArgs, reason, nil)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = workers.DepsolveJobInfo(depsolveJobID, &depsolveResults)
|
||||
if err != nil {
|
||||
reason := "Error reading depsolve status"
|
||||
jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorReadingJobStatus, reason, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if jobErr := depsolveResults.JobError; jobErr != nil {
|
||||
if jobErr.ID == clienterrors.ErrorDNFDepsolveError || jobErr.ID == clienterrors.ErrorDNFMarkingErrors {
|
||||
jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorDepsolveDependency, "Error in depsolve job dependency input, bad package set requested", nil)
|
||||
return
|
||||
}
|
||||
jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorDepsolveDependency, "Error in depsolve job dependency", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if len(depsolveResults.PackageSpecs) == 0 {
|
||||
jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorEmptyPackageSpecs, "Received empty package specs", nil)
|
||||
return
|
||||
}
|
||||
|
||||
var containerSpecs map[string][]container.Spec
|
||||
if containerResolveJobID != uuid.Nil {
|
||||
// Container resolve job
|
||||
var result worker.ContainerResolveJobResult
|
||||
_, err := workers.ContainerResolveJobInfo(containerResolveJobID, &result)
|
||||
|
||||
if err != nil {
|
||||
reason := "Error reading container resolve job status"
|
||||
jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorReadingJobStatus, reason, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if jobErr := result.JobError; jobErr != nil {
|
||||
jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorContainerDependency, "Error in container resolve job dependency", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// NOTE: The container resolve job doesn't hold the pipeline name for
|
||||
// the container embedding, so we need to get it from the manifest
|
||||
// content field. There should be only one.
|
||||
var containerEmbedPipeline string
|
||||
for name := range manifestSource.GetContainerSourceSpecs() {
|
||||
containerEmbedPipeline = name
|
||||
break
|
||||
}
|
||||
|
||||
pipelineSpecs := make([]container.Spec, len(result.Specs))
|
||||
for idx, resultSpec := range result.Specs {
|
||||
pipelineSpecs[idx] = container.Spec{
|
||||
Source: resultSpec.Source,
|
||||
Digest: resultSpec.Digest,
|
||||
LocalName: resultSpec.Name,
|
||||
TLSVerify: resultSpec.TLSVerify,
|
||||
ImageID: resultSpec.ImageID,
|
||||
ListDigest: resultSpec.ListDigest,
|
||||
}
|
||||
|
||||
}
|
||||
containerSpecs = map[string][]container.Spec{
|
||||
containerEmbedPipeline: pipelineSpecs,
|
||||
}
|
||||
}
|
||||
|
||||
var ostreeCommitSpecs map[string][]ostree.CommitSpec
|
||||
if ostreeResolveJobID != uuid.Nil {
|
||||
var result worker.OSTreeResolveJobResult
|
||||
_, err := workers.OSTreeResolveJobInfo(ostreeResolveJobID, &result)
|
||||
|
||||
if err != nil {
|
||||
reason := "Error reading ostree resolve job status"
|
||||
logrus.Errorf("%s: %v", reason, err)
|
||||
jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorReadingJobStatus, reason, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if jobErr := result.JobError; jobErr != nil {
|
||||
jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorOSTreeDependency, "Error in ostree resolve job dependency", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// NOTE: The ostree resolve job doesn't hold the pipeline name for the
|
||||
// ostree commits, so we need to get it from the manifest content
|
||||
// field. There should be only one.
|
||||
var ostreeCommitPipeline string
|
||||
for name := range manifestSource.GetOSTreeSourceSpecs() {
|
||||
ostreeCommitPipeline = name
|
||||
break
|
||||
}
|
||||
|
||||
commitSpecs := make([]ostree.CommitSpec, len(result.Specs))
|
||||
for idx, resultSpec := range result.Specs {
|
||||
commitSpecs[idx] = ostree.CommitSpec{
|
||||
Ref: resultSpec.Ref,
|
||||
URL: resultSpec.URL,
|
||||
Checksum: resultSpec.Checksum,
|
||||
}
|
||||
if resultSpec.RHSM {
|
||||
// NOTE: Older workers don't set the Secrets string in the result
|
||||
// spec so let's add it here for backwards compatibility. This
|
||||
// should be removed after a few versions when all workers have
|
||||
// been updated.
|
||||
resultSpec.Secrets = "org.osbuild.rhsm.consumer"
|
||||
}
|
||||
}
|
||||
ostreeCommitSpecs = map[string][]ostree.CommitSpec{
|
||||
ostreeCommitPipeline: commitSpecs,
|
||||
}
|
||||
}
|
||||
|
||||
ms, err := manifestSource.Serialize(depsolveResults.PackageSpecs, containerSpecs, ostreeCommitSpecs)
|
||||
|
||||
jobResult.Manifest = ms
|
||||
}
|
||||
|
|
@ -6,8 +6,8 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/osbuild/images/pkg/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
func TestSplitExtension(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ import (
|
|||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/osbuild/images/pkg/distro/test_distro"
|
||||
"github.com/osbuild/images/pkg/osbuild"
|
||||
v2 "github.com/osbuild/osbuild-composer/internal/cloudapi/v2"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro/test_distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/target"
|
||||
"github.com/osbuild/osbuild-composer/internal/test"
|
||||
"github.com/osbuild/osbuild-composer/internal/worker"
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ import (
|
|||
|
||||
"github.com/osbuild/osbuild-composer/pkg/jobqueue"
|
||||
|
||||
"github.com/osbuild/images/pkg/distro/test_distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/cloudapi/v2"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro/test_distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/test"
|
||||
"github.com/osbuild/osbuild-composer/internal/worker"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,13 +12,13 @@ import (
|
|||
|
||||
"github.com/osbuild/osbuild-composer/pkg/jobqueue"
|
||||
|
||||
"github.com/osbuild/images/pkg/distro/test_distro"
|
||||
"github.com/osbuild/images/pkg/osbuild"
|
||||
"github.com/osbuild/images/pkg/ostree/mock_ostree_repo"
|
||||
"github.com/osbuild/images/pkg/rpmmd"
|
||||
v2 "github.com/osbuild/osbuild-composer/internal/cloudapi/v2"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro/test_distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/jobqueue/fsjobqueue"
|
||||
distro_mock "github.com/osbuild/osbuild-composer/internal/mocks/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/ostree/mock_ostree_repo"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/target"
|
||||
"github.com/osbuild/osbuild-composer/internal/test"
|
||||
"github.com/osbuild/osbuild-composer/internal/worker"
|
||||
|
|
|
|||
|
|
@ -1,506 +0,0 @@
|
|||
// package container implements a client for a container
|
||||
// registry. It can be used to upload container images.
|
||||
package container
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
_ "github.com/containers/image/v5/docker/archive"
|
||||
_ "github.com/containers/image/v5/oci/archive"
|
||||
_ "github.com/containers/image/v5/oci/layout"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
|
||||
"github.com/containers/common/pkg/retry"
|
||||
"github.com/containers/image/v5/copy"
|
||||
"github.com/containers/image/v5/docker"
|
||||
"github.com/containers/image/v5/docker/reference"
|
||||
"github.com/containers/image/v5/manifest"
|
||||
"github.com/containers/image/v5/signature"
|
||||
"github.com/containers/image/v5/transports"
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/opencontainers/go-digest"
|
||||
|
||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultUserAgent = "osbuild-composer/1.0"
|
||||
DefaultPolicyPath = "/etc/containers/policy.json"
|
||||
)
|
||||
|
||||
// GetDefaultAuthFile returns the authentication file to use for the
|
||||
// current environment.
|
||||
//
|
||||
// This is basically a re-implementation of `getPathToAuthWithOS` from
|
||||
// containers/image/pkg/docker/config/config.go[1], but we ensure that
|
||||
// the returned path is either accessible. This is needed since any
|
||||
// other error than os.ErrNotExist will lead to an overall failure and
|
||||
// thus prevent any operation even with public resources.
|
||||
//
|
||||
// [1] https://github.com/containers/image/blob/55ea76c7db702ed1af60924a0b57c8da533d9e5a/pkg/docker/config/config.go#L506
|
||||
func GetDefaultAuthFile() string {
|
||||
|
||||
checkAccess := func(path string) bool {
|
||||
err := unix.Access(path, unix.R_OK)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
if authFile := os.Getenv("REGISTRY_AUTH_FILE"); authFile != "" {
|
||||
if checkAccess(authFile) {
|
||||
return authFile
|
||||
}
|
||||
}
|
||||
|
||||
if runtimeDir := os.Getenv("XDG_RUNTIME_DIR"); runtimeDir != "" {
|
||||
if checkAccess(runtimeDir) {
|
||||
return filepath.Join(runtimeDir, "containers", "auth.json")
|
||||
}
|
||||
}
|
||||
|
||||
if rundir := filepath.FromSlash("/run/containers"); checkAccess(rundir) {
|
||||
return filepath.Join(rundir, strconv.Itoa(os.Getuid()), "auth.json")
|
||||
}
|
||||
|
||||
return filepath.FromSlash("/var/empty/containers-auth.json")
|
||||
}
|
||||
|
||||
// ApplyDefaultPath checks if the target includes a domain and if it doesn't adds the default ones
|
||||
// to the returned string. If also returns a bool indicating whether the defaults were applied
|
||||
func ApplyDefaultDomainPath(target, defaultDomain, defaultPath string) (string, bool) {
|
||||
appliedDefaults := false
|
||||
i := strings.IndexRune(target, '/')
|
||||
if i == -1 || (!strings.ContainsAny(target[:i], ".:") && target[:i] != "localhost") {
|
||||
if defaultDomain != "" {
|
||||
base := defaultDomain
|
||||
if defaultPath != "" {
|
||||
base = fmt.Sprintf("%s/%s", base, defaultPath)
|
||||
}
|
||||
target = fmt.Sprintf("%s/%s", base, target)
|
||||
appliedDefaults = true
|
||||
}
|
||||
}
|
||||
|
||||
return target, appliedDefaults
|
||||
}
|
||||
|
||||
// A Client to interact with the given Target object at a
|
||||
// container registry, like e.g. uploading an image to.
|
||||
// All mentioned defaults are only set when using the
|
||||
// NewClient constructor.
|
||||
type Client struct {
|
||||
Target reference.Named // the target object to interact with
|
||||
|
||||
ReportWriter io.Writer // used for writing status reports, defaults to os.Stdout
|
||||
|
||||
PrecomputeDigests bool // precompute digest in order to avoid uploads
|
||||
MaxRetries int // how often to retry http requests
|
||||
|
||||
UserAgent string // user agent string to use for requests, defaults to DefaultUserAgent
|
||||
|
||||
// internal state
|
||||
policy *signature.Policy
|
||||
sysCtx *types.SystemContext
|
||||
}
|
||||
|
||||
// NewClient constructs a new Client for target with default options.
|
||||
// It will add the "latest" tag if target does not contain it.
|
||||
func NewClient(target string) (*Client, error) {
|
||||
|
||||
ref, err := reference.ParseNormalizedNamed(target)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse '%s': %w", target, err)
|
||||
}
|
||||
|
||||
var policy *signature.Policy
|
||||
if _, err := os.Stat(DefaultPolicyPath); err == nil {
|
||||
policy, err = signature.NewPolicyFromFile(DefaultPolicyPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
policy = &signature.Policy{
|
||||
Default: []signature.PolicyRequirement{
|
||||
signature.NewPRInsecureAcceptAnything(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
client := Client{
|
||||
Target: reference.TagNameOnly(ref),
|
||||
|
||||
ReportWriter: os.Stdout,
|
||||
PrecomputeDigests: true,
|
||||
|
||||
UserAgent: DefaultUserAgent,
|
||||
|
||||
sysCtx: &types.SystemContext{
|
||||
RegistriesDirPath: "",
|
||||
SystemRegistriesConfPath: "",
|
||||
BigFilesTemporaryDir: "/var/tmp",
|
||||
|
||||
OSChoice: "linux",
|
||||
|
||||
AuthFilePath: GetDefaultAuthFile(),
|
||||
},
|
||||
policy: policy,
|
||||
}
|
||||
|
||||
return &client, nil
|
||||
}
|
||||
|
||||
// SetAuthFilePath sets the location of the `containers-auth.json(5)` file.
|
||||
func (cl *Client) SetAuthFilePath(path string) {
|
||||
cl.sysCtx.AuthFilePath = path
|
||||
}
|
||||
|
||||
// GetAuthFilePath gets the location of the `containers-auth.json(5)` file.
|
||||
func (cl *Client) GetAuthFilePath() string {
|
||||
return cl.sysCtx.AuthFilePath
|
||||
}
|
||||
|
||||
func (cl *Client) SetArchitectureChoice(arch string) {
|
||||
// Translate some well-known Composer architecture strings
|
||||
// into the corresponding container ones
|
||||
|
||||
variant := ""
|
||||
|
||||
switch arch {
|
||||
case "x86_64":
|
||||
arch = "amd64"
|
||||
|
||||
case "aarch64":
|
||||
arch = "arm64"
|
||||
if variant == "" {
|
||||
variant = "v8"
|
||||
}
|
||||
|
||||
case "armhfp":
|
||||
arch = "arm"
|
||||
if variant == "" {
|
||||
variant = "v7"
|
||||
}
|
||||
|
||||
//ppc64le and s390x are the same
|
||||
}
|
||||
|
||||
cl.sysCtx.ArchitectureChoice = arch
|
||||
cl.sysCtx.VariantChoice = variant
|
||||
}
|
||||
|
||||
func (cl *Client) SetVariantChoice(variant string) {
|
||||
cl.sysCtx.VariantChoice = variant
|
||||
}
|
||||
|
||||
// SetCredentials will set username and password for Client
|
||||
func (cl *Client) SetCredentials(username, password string) {
|
||||
|
||||
if cl.sysCtx.DockerAuthConfig == nil {
|
||||
cl.sysCtx.DockerAuthConfig = &types.DockerAuthConfig{}
|
||||
}
|
||||
|
||||
cl.sysCtx.DockerAuthConfig.Username = username
|
||||
cl.sysCtx.DockerAuthConfig.Password = password
|
||||
}
|
||||
|
||||
func (cl *Client) SetDockerCertPath(path string) {
|
||||
cl.sysCtx.DockerCertPath = path
|
||||
}
|
||||
|
||||
// SetSkipTLSVerify controls if TLS verification happens when
|
||||
// making requests. If nil is passed it falls back to the default.
|
||||
func (cl *Client) SetTLSVerify(verify *bool) {
|
||||
if verify == nil {
|
||||
cl.sysCtx.DockerInsecureSkipTLSVerify = types.OptionalBoolUndefined
|
||||
} else {
|
||||
cl.sysCtx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!*verify)
|
||||
}
|
||||
}
|
||||
|
||||
// GetSkipTLSVerify returns current TLS verification state.
|
||||
func (cl *Client) GetTLSVerify() *bool {
|
||||
|
||||
skip := cl.sysCtx.DockerInsecureSkipTLSVerify
|
||||
|
||||
if skip == types.OptionalBoolUndefined {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NB: we invert the state, i.e. verify == (skip == false)
|
||||
return common.ToPtr(skip == types.OptionalBoolFalse)
|
||||
}
|
||||
|
||||
// SkipTLSVerify is a convenience helper that internally calls
|
||||
// SetTLSVerify with false
|
||||
func (cl *Client) SkipTLSVerify() {
|
||||
cl.SetTLSVerify(common.ToPtr(false))
|
||||
}
|
||||
|
||||
func parseImageName(name string) (types.ImageReference, error) {
|
||||
|
||||
parts := strings.SplitN(name, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("invalid image name '%s'", name)
|
||||
}
|
||||
|
||||
transport := transports.Get(parts[0])
|
||||
if transport == nil {
|
||||
return nil, fmt.Errorf("unknown transport '%s'", parts[0])
|
||||
}
|
||||
|
||||
return transport.ParseReference(parts[1])
|
||||
}
|
||||
|
||||
// UploadImage takes an container image located at from and uploads it
|
||||
// to the Target of Client. If tag is set, i.e. not the empty string,
|
||||
// it will replace any previously set tag or digest of the target.
|
||||
// Returns the digest of the manifest that was written to the server.
|
||||
func (cl *Client) UploadImage(ctx context.Context, from, tag string) (digest.Digest, error) {
|
||||
|
||||
targetCtx := *cl.sysCtx
|
||||
targetCtx.DockerRegistryPushPrecomputeDigests = cl.PrecomputeDigests
|
||||
|
||||
policyContext, err := signature.NewPolicyContext(cl.policy)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
srcRef, err := parseImageName(from)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid source name '%s': %w", from, err)
|
||||
}
|
||||
|
||||
target := cl.Target
|
||||
|
||||
if tag != "" {
|
||||
target = reference.TrimNamed(target)
|
||||
target, err = reference.WithTag(target, tag)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating reference with tag '%s': %w", tag, err)
|
||||
}
|
||||
}
|
||||
|
||||
destRef, err := docker.NewReference(target)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
retryOpts := retry.RetryOptions{
|
||||
MaxRetry: cl.MaxRetries,
|
||||
}
|
||||
|
||||
var manifestDigest digest.Digest
|
||||
|
||||
err = retry.RetryIfNecessary(ctx, func() error {
|
||||
manifestBytes, err := copy.Image(ctx, policyContext, destRef, srcRef, ©.Options{
|
||||
RemoveSignatures: false,
|
||||
SignBy: "",
|
||||
SignPassphrase: "",
|
||||
ReportWriter: cl.ReportWriter,
|
||||
SourceCtx: cl.sysCtx,
|
||||
DestinationCtx: &targetCtx,
|
||||
ForceManifestMIMEType: "",
|
||||
ImageListSelection: copy.CopyAllImages,
|
||||
PreserveDigests: false,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manifestDigest, err = manifest.Digest(manifestBytes)
|
||||
|
||||
return err
|
||||
|
||||
}, &retryOpts)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return manifestDigest, nil
|
||||
}
|
||||
|
||||
// A RawManifest contains the raw manifest Data and its MimeType
|
||||
type RawManifest struct {
|
||||
Data []byte
|
||||
MimeType string
|
||||
}
|
||||
|
||||
// Digest computes the digest from the raw manifest data
|
||||
func (m RawManifest) Digest() (digest.Digest, error) {
|
||||
return manifest.Digest(m.Data)
|
||||
}
|
||||
|
||||
// GetManifest fetches the raw manifest data from the server. If digest is not empty
|
||||
// it will override any given tag for the Client's Target.
|
||||
func (cl *Client) GetManifest(ctx context.Context, digest digest.Digest) (r RawManifest, err error) {
|
||||
target := cl.Target
|
||||
|
||||
if digest != "" {
|
||||
t := reference.TrimNamed(cl.Target)
|
||||
t, err = reference.WithDigest(t, digest)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
target = t
|
||||
}
|
||||
|
||||
ref, err := docker.NewReference(target)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
src, err := ref.NewImageSource(ctx, cl.sysCtx)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if e := src.Close(); e != nil {
|
||||
err = fmt.Errorf("could not close image: %w", e)
|
||||
}
|
||||
}()
|
||||
|
||||
retryOpts := retry.RetryOptions{
|
||||
MaxRetry: cl.MaxRetries,
|
||||
}
|
||||
|
||||
if err = retry.RetryIfNecessary(ctx, func() error {
|
||||
r.Data, r.MimeType, err = src.GetManifest(ctx, nil)
|
||||
return err
|
||||
}, &retryOpts); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type manifestList interface {
|
||||
ChooseInstance(ctx *types.SystemContext) (digest.Digest, error)
|
||||
}
|
||||
|
||||
type resolvedIds struct {
|
||||
Manifest digest.Digest
|
||||
Config digest.Digest
|
||||
ListManifest digest.Digest
|
||||
}
|
||||
|
||||
func (cl *Client) resolveManifestList(ctx context.Context, list manifestList) (resolvedIds, error) {
|
||||
digest, err := list.ChooseInstance(cl.sysCtx)
|
||||
if err != nil {
|
||||
return resolvedIds{}, err
|
||||
}
|
||||
|
||||
raw, err := cl.GetManifest(ctx, digest)
|
||||
|
||||
if err != nil {
|
||||
return resolvedIds{}, fmt.Errorf("error getting manifest: %w", err)
|
||||
}
|
||||
|
||||
ids, err := cl.resolveRawManifest(ctx, raw)
|
||||
if err != nil {
|
||||
return resolvedIds{}, err
|
||||
}
|
||||
|
||||
return ids, err
|
||||
}
|
||||
|
||||
func (cl *Client) resolveRawManifest(ctx context.Context, rm RawManifest) (resolvedIds, error) {
|
||||
|
||||
var imageID digest.Digest
|
||||
|
||||
switch rm.MimeType {
|
||||
case manifest.DockerV2ListMediaType:
|
||||
list, err := manifest.Schema2ListFromManifest(rm.Data)
|
||||
if err != nil {
|
||||
return resolvedIds{}, err
|
||||
}
|
||||
|
||||
// Save digest of the manifest list as well.
|
||||
ids, err := cl.resolveManifestList(ctx, list)
|
||||
if err != nil {
|
||||
return resolvedIds{}, err
|
||||
}
|
||||
// NOTE: Comment in Digest() source says this should never fail. Ignore the error.
|
||||
ids.ListManifest, _ = rm.Digest()
|
||||
return ids, nil
|
||||
|
||||
case imgspecv1.MediaTypeImageIndex:
|
||||
index, err := manifest.OCI1IndexFromManifest(rm.Data)
|
||||
if err != nil {
|
||||
return resolvedIds{}, err
|
||||
}
|
||||
|
||||
// Save digest of the manifest list as well.
|
||||
ids, err := cl.resolveManifestList(ctx, index)
|
||||
if err != nil {
|
||||
return resolvedIds{}, err
|
||||
}
|
||||
// NOTE: Comment in Digest() source says this should never fail. Ignore the error.
|
||||
ids.ListManifest, _ = rm.Digest()
|
||||
return ids, nil
|
||||
|
||||
case imgspecv1.MediaTypeImageManifest:
|
||||
m, err := manifest.OCI1FromManifest(rm.Data)
|
||||
if err != nil {
|
||||
return resolvedIds{}, nil
|
||||
}
|
||||
imageID = m.ConfigInfo().Digest
|
||||
|
||||
case manifest.DockerV2Schema2MediaType:
|
||||
m, err := manifest.Schema2FromManifest(rm.Data)
|
||||
|
||||
if err != nil {
|
||||
return resolvedIds{}, nil
|
||||
}
|
||||
|
||||
imageID = m.ConfigInfo().Digest
|
||||
|
||||
default:
|
||||
return resolvedIds{}, fmt.Errorf("unsupported manifest format '%s'", rm.MimeType)
|
||||
}
|
||||
|
||||
dg, err := rm.Digest()
|
||||
|
||||
if err != nil {
|
||||
return resolvedIds{}, err
|
||||
}
|
||||
|
||||
return resolvedIds{
|
||||
Manifest: dg,
|
||||
Config: imageID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Resolve the Client's Target to the manifest digest and the corresponding image id
|
||||
// which is the digest of the configuration object. It uses the architecture and
|
||||
// variant specified via SetArchitectureChoice or the corresponding defaults for
|
||||
// the host.
|
||||
func (cl *Client) Resolve(ctx context.Context, name string) (Spec, error) {
|
||||
|
||||
raw, err := cl.GetManifest(ctx, "")
|
||||
|
||||
if err != nil {
|
||||
return Spec{}, fmt.Errorf("error getting manifest: %w", err)
|
||||
}
|
||||
|
||||
ids, err := cl.resolveRawManifest(ctx, raw)
|
||||
if err != nil {
|
||||
return Spec{}, err
|
||||
}
|
||||
|
||||
spec := NewSpec(cl.Target, ids.Manifest, ids.Config, cl.GetTLSVerify(), ids.ListManifest.String(), name)
|
||||
|
||||
return spec, nil
|
||||
}
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
package container_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/container"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
//
|
||||
|
||||
func TestClientResolve(t *testing.T) {
|
||||
|
||||
registry := NewTestRegistry()
|
||||
defer registry.Close()
|
||||
|
||||
repo := registry.AddRepo("library/osbuild")
|
||||
listDigest := repo.AddImage(
|
||||
[]Blob{NewDataBlobFromBase64(rootLayer)},
|
||||
[]string{"amd64", "ppc64le"},
|
||||
"cool container",
|
||||
time.Time{})
|
||||
|
||||
ref := registry.GetRef("library/osbuild")
|
||||
client, err := container.NewClient(ref)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, client)
|
||||
|
||||
client.SkipTLSVerify()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
client.SetArchitectureChoice("amd64")
|
||||
spec, err := client.Resolve(ctx, "")
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, container.Spec{
|
||||
Source: ref,
|
||||
Digest: "sha256:f29b6cd42a94a574583439addcd6694e6224f0e4b32044c9e3aee4c4856c2a50",
|
||||
ImageID: "sha256:c2ecf25cf190e76b12b07436ad5140d4ba53d8a136d498705e57a006837a720f",
|
||||
TLSVerify: client.GetTLSVerify(),
|
||||
LocalName: client.Target.String(),
|
||||
ListDigest: listDigest,
|
||||
}, spec)
|
||||
|
||||
client.SetArchitectureChoice("ppc64le")
|
||||
spec, err = client.Resolve(ctx, "")
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, container.Spec{
|
||||
Source: ref,
|
||||
Digest: "sha256:d49eebefb6c7ce5505594bef652bd4adc36f413861bd44209d9b9486310b1264",
|
||||
ImageID: "sha256:d2ab8fea7f08a22f03b30c13c6ea443121f25e87202a7496e93736efa6fe345a",
|
||||
TLSVerify: client.GetTLSVerify(),
|
||||
LocalName: client.Target.String(),
|
||||
ListDigest: listDigest,
|
||||
}, spec)
|
||||
|
||||
// don't have that architecture
|
||||
client.SetArchitectureChoice("s390x")
|
||||
_, err = client.Resolve(ctx, "")
|
||||
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestClientAuthFilePath(t *testing.T) {
|
||||
|
||||
client, err := container.NewClient("quay.io/osbuild/osbuild")
|
||||
assert.NoError(t, err)
|
||||
|
||||
authFilePath := client.GetAuthFilePath()
|
||||
assert.NotEmpty(t, authFilePath)
|
||||
assert.Equal(t, authFilePath, container.GetDefaultAuthFile())
|
||||
|
||||
// make sure the file is accessible
|
||||
_, err = os.ReadFile(authFilePath)
|
||||
assert.True(t, err == nil || os.IsNotExist(err))
|
||||
|
||||
t.Run("XDG_RUNTIME_DIR", func(t *testing.T) {
|
||||
runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
|
||||
|
||||
if runtimeDir == "" {
|
||||
t.Skip("XDG_RUNTIME_DIR not set, skipping test")
|
||||
return
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
os.Setenv("XDG_RUNTIME_DIR", runtimeDir)
|
||||
})
|
||||
|
||||
os.Unsetenv("XDG_RUNTIME_DIR")
|
||||
|
||||
authFilePath := container.GetDefaultAuthFile()
|
||||
assert.NotEmpty(t, authFilePath)
|
||||
_, err = os.ReadFile(authFilePath)
|
||||
assert.True(t, err == nil || os.IsNotExist(err))
|
||||
})
|
||||
|
||||
}
|
||||
|
|
@ -1,385 +0,0 @@
|
|||
package container_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/opencontainers/go-digest"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/container"
|
||||
|
||||
"github.com/containers/image/v5/docker/reference"
|
||||
"github.com/containers/image/v5/manifest"
|
||||
)
|
||||
|
||||
const rootLayer = `H4sIAAAJbogA/+SWUYqDMBCG53lP4V5g9x8dzRX2Bvtc0VIhEIhKe/wSKxgU6ktjC/O9hMzAQDL8
|
||||
/8yltdb9DLeB0gEGKhHCg/UJsBAL54zKFBAC54ZzyrCUSMfYDydPgHfu6R/s5VePilOfzF/of/bv
|
||||
vG2+lqhyFNGPddP53yjyegCBKcuNROZ77AmBoP+CmbIyqpEM5fqf+3/ubJtsCuz7P1b+L1Du/4f5
|
||||
v+vrsVPu/Vq9P3ANk//d+x/MZv8TKNf/Qfqf9v9v5fLXK3/lKEc5ypm4AwAA//8DAE6E6nIAEgAA
|
||||
`
|
||||
|
||||
// The following code implements a toy container registry to test with
|
||||
|
||||
// Blob interface
|
||||
type Blob interface {
|
||||
GetSize() int64
|
||||
GetMediaType() string
|
||||
GetDigest() digest.Digest
|
||||
|
||||
Reader() io.Reader
|
||||
}
|
||||
|
||||
// dataBlob //
|
||||
type dataBlob struct {
|
||||
Data []byte
|
||||
MediaType string
|
||||
}
|
||||
|
||||
func NewDataBlobFromBase64(text string) dataBlob {
|
||||
data, err := base64.StdEncoding.DecodeString(text)
|
||||
|
||||
if err != nil {
|
||||
panic("decoding of text failed")
|
||||
}
|
||||
|
||||
return dataBlob{
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Blob interface implementation
|
||||
func (b dataBlob) GetSize() int64 {
|
||||
return int64(len(b.Data))
|
||||
}
|
||||
|
||||
func (b dataBlob) GetMediaType() string {
|
||||
if b.MediaType != "" {
|
||||
return b.MediaType
|
||||
}
|
||||
|
||||
return manifest.DockerV2Schema2LayerMediaType
|
||||
}
|
||||
|
||||
func (b dataBlob) GetDigest() digest.Digest {
|
||||
return digest.FromBytes(b.Data)
|
||||
}
|
||||
|
||||
func (b dataBlob) Reader() io.Reader {
|
||||
return bytes.NewReader(b.Data)
|
||||
}
|
||||
|
||||
func MakeDescriptorForBlob(b Blob) manifest.Schema2Descriptor {
|
||||
return manifest.Schema2Descriptor{
|
||||
MediaType: b.GetMediaType(),
|
||||
Size: b.GetSize(),
|
||||
Digest: b.GetDigest(),
|
||||
}
|
||||
}
|
||||
|
||||
// Repo //
|
||||
type Repo struct {
|
||||
blobs map[string]Blob
|
||||
manifests map[string]*manifest.Schema2
|
||||
images map[string]*manifest.Schema2List
|
||||
tags map[string]string
|
||||
}
|
||||
|
||||
func NewRepo() *Repo {
|
||||
return &Repo{
|
||||
blobs: make(map[string]Blob),
|
||||
manifests: make(map[string]*manifest.Schema2),
|
||||
tags: make(map[string]string),
|
||||
images: make(map[string]*manifest.Schema2List),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Repo) AddBlob(b Blob) manifest.Schema2Descriptor {
|
||||
desc := MakeDescriptorForBlob(b)
|
||||
r.blobs[desc.Digest.String()] = b
|
||||
return desc
|
||||
}
|
||||
|
||||
func (r *Repo) AddObject(v interface{}, mediaType string) manifest.Schema2Descriptor {
|
||||
data, err := json.MarshalIndent(v, "", " ")
|
||||
if err != nil {
|
||||
panic("could not marshal image object")
|
||||
}
|
||||
|
||||
blob := dataBlob{
|
||||
Data: data,
|
||||
MediaType: mediaType,
|
||||
}
|
||||
|
||||
return r.AddBlob(blob)
|
||||
}
|
||||
|
||||
func (r *Repo) AddManifest(mf *manifest.Schema2) manifest.Schema2Descriptor {
|
||||
desc := r.AddObject(mf, mf.MediaType)
|
||||
|
||||
r.manifests[desc.Digest.String()] = mf
|
||||
|
||||
return desc
|
||||
}
|
||||
|
||||
func (r *Repo) AddImage(layers []Blob, arches []string, comment string, ctime time.Time) string {
|
||||
|
||||
blobs := make([]manifest.Schema2Descriptor, len(layers))
|
||||
|
||||
for i, layer := range layers {
|
||||
blobs[i] = r.AddBlob(layer)
|
||||
}
|
||||
|
||||
manifests := make([]manifest.Schema2ManifestDescriptor, len(arches))
|
||||
|
||||
for i, arch := range arches {
|
||||
img := manifest.Schema2V1Image{
|
||||
Architecture: arch,
|
||||
OS: "linux",
|
||||
Author: "osbuild",
|
||||
Comment: comment,
|
||||
Created: ctime,
|
||||
}
|
||||
|
||||
// Add the config object
|
||||
config := r.AddObject(img, manifest.DockerV2Schema2ConfigMediaType)
|
||||
|
||||
// make and add the manifest object
|
||||
schema := manifest.Schema2FromComponents(config, blobs)
|
||||
mf := r.AddManifest(schema)
|
||||
|
||||
desc := manifest.Schema2ManifestDescriptor{
|
||||
Schema2Descriptor: mf,
|
||||
Platform: manifest.Schema2PlatformSpec{
|
||||
Architecture: arch,
|
||||
OS: "linux",
|
||||
},
|
||||
}
|
||||
|
||||
manifests[i] = desc
|
||||
}
|
||||
|
||||
list := manifest.Schema2ListFromComponents(manifests)
|
||||
desc := r.AddObject(list, list.MediaType)
|
||||
checksum := desc.Digest.String()
|
||||
|
||||
r.images[checksum] = list
|
||||
r.tags["latest"] = checksum
|
||||
|
||||
return checksum
|
||||
}
|
||||
|
||||
func (r *Repo) AddTag(checksum, tag string) {
|
||||
|
||||
if _, ok := r.images[checksum]; !ok {
|
||||
panic("cannot tag: image not found: " + checksum)
|
||||
}
|
||||
|
||||
r.tags[tag] = checksum
|
||||
}
|
||||
|
||||
func WriteBlob(blob Blob, w http.ResponseWriter) {
|
||||
w.Header().Add("Content-Type", blob.GetMediaType())
|
||||
w.Header().Add("Content-Length", fmt.Sprintf("%d", blob.GetSize()))
|
||||
w.Header().Add("Docker-Content-Digest", blob.GetDigest().String())
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
reader := blob.Reader()
|
||||
|
||||
_, err := io.Copy(w, reader)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error writing blob: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func BlobIsManifest(blob Blob) bool {
|
||||
mt := blob.GetMediaType()
|
||||
return mt == manifest.DockerV2Schema2MediaType || mt == manifest.DockerV2ListMediaType
|
||||
}
|
||||
|
||||
func (r *Repo) ServeManifest(ref string, w http.ResponseWriter, req *http.Request) {
|
||||
if checksum, ok := r.tags[ref]; ok {
|
||||
ref = checksum
|
||||
}
|
||||
|
||||
blob, ok := r.blobs[ref]
|
||||
if !ok || !BlobIsManifest(blob) {
|
||||
fmt.Fprintf(os.Stderr, "manifest %s not found", ref)
|
||||
http.NotFound(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
WriteBlob(blob, w)
|
||||
}
|
||||
|
||||
func (r *Repo) ServeBlob(ref string, w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
blob, ok := r.blobs[ref]
|
||||
|
||||
if !ok {
|
||||
fmt.Fprintf(os.Stderr, "blob %s not found", ref)
|
||||
http.NotFound(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
WriteBlob(blob, w)
|
||||
}
|
||||
|
||||
// Registry //
|
||||
|
||||
type Registry struct {
|
||||
server *httptest.Server
|
||||
repos map[string]*Repo
|
||||
}
|
||||
|
||||
func (reg *Registry) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
parts := strings.SplitN(req.URL.Path, "?", 1)
|
||||
paths := strings.Split(strings.Trim(parts[0], "/"), "/")
|
||||
|
||||
// Possbile routes
|
||||
// [1] version-check: /v2/
|
||||
// [2] blobs: /v2/<repo_name>/blobs/<digest>
|
||||
// [3] manifest: /v2/<repo_name>/manifests/<ref>
|
||||
//
|
||||
// we need at least 4 path components and path has to start with "/v2"
|
||||
|
||||
if len(paths) < 1 || paths[0] != "v2" {
|
||||
http.NotFound(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
// [1] version check
|
||||
if len(paths) == 1 {
|
||||
w.WriteHeader(200)
|
||||
return
|
||||
} else if len(paths) < 4 {
|
||||
http.NotFound(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
// we asserted that we have at least 4 path components
|
||||
ref := paths[len(paths)-1]
|
||||
cmd := paths[len(paths)-2]
|
||||
|
||||
repoName := strings.Join(paths[1:len(paths)-2], "/")
|
||||
|
||||
repo, ok := reg.repos[repoName]
|
||||
if !ok {
|
||||
fmt.Fprintf(os.Stderr, "repo %s not found", repoName)
|
||||
http.NotFound(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
if cmd == "manifests" {
|
||||
repo.ServeManifest(ref, w, req)
|
||||
} else if cmd == "blobs" {
|
||||
repo.ServeBlob(ref, w, req)
|
||||
} else {
|
||||
http.NotFound(w, req)
|
||||
}
|
||||
}
|
||||
|
||||
func NewTestRegistry() *Registry {
|
||||
|
||||
reg := &Registry{
|
||||
repos: make(map[string]*Repo),
|
||||
}
|
||||
reg.server = httptest.NewTLSServer(reg)
|
||||
|
||||
return reg
|
||||
}
|
||||
|
||||
func (reg *Registry) AddRepo(name string) *Repo {
|
||||
repo := NewRepo()
|
||||
reg.repos[name] = repo
|
||||
return repo
|
||||
}
|
||||
|
||||
func (reg *Registry) GetRef(repo string) string {
|
||||
return fmt.Sprintf("%s/%s", reg.server.Listener.Addr().String(), repo)
|
||||
}
|
||||
|
||||
func (reg *Registry) Resolve(target, arch string) (container.Spec, error) {
|
||||
|
||||
ref, err := reference.ParseNormalizedNamed(target)
|
||||
if err != nil {
|
||||
return container.Spec{}, fmt.Errorf("failed to parse '%s': %w", target, err)
|
||||
}
|
||||
|
||||
domain := reference.Domain(ref)
|
||||
|
||||
tag := "latest"
|
||||
var checksum string
|
||||
|
||||
if tagged, ok := ref.(reference.NamedTagged); ok {
|
||||
tag = tagged.Tag()
|
||||
}
|
||||
|
||||
if digested, ok := ref.(reference.Digested); ok {
|
||||
checksum = string(digested.Digest())
|
||||
}
|
||||
|
||||
if domain != reg.server.Listener.Addr().String() {
|
||||
return container.Spec{}, fmt.Errorf("unknown domain")
|
||||
}
|
||||
|
||||
ref = reference.TrimNamed(ref)
|
||||
path := reference.Path(ref)
|
||||
|
||||
repo, ok := reg.repos[path]
|
||||
if !ok {
|
||||
return container.Spec{}, fmt.Errorf("unknown repo")
|
||||
}
|
||||
|
||||
if checksum == "" {
|
||||
checksum, ok = repo.tags[tag]
|
||||
if !ok {
|
||||
return container.Spec{}, fmt.Errorf("unknown tag")
|
||||
}
|
||||
}
|
||||
|
||||
lst, ok := repo.images[checksum]
|
||||
listDigest := checksum
|
||||
|
||||
if ok {
|
||||
checksum = ""
|
||||
|
||||
for _, m := range lst.Manifests {
|
||||
if m.Platform.Architecture == arch {
|
||||
checksum = m.Digest.String()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if checksum == "" {
|
||||
return container.Spec{}, fmt.Errorf("unsupported architecture")
|
||||
}
|
||||
}
|
||||
|
||||
mf, ok := repo.manifests[checksum]
|
||||
if !ok {
|
||||
return container.Spec{}, fmt.Errorf("unknown digest")
|
||||
}
|
||||
|
||||
return container.Spec{
|
||||
Source: ref.String(),
|
||||
Digest: checksum,
|
||||
ImageID: mf.ConfigDescriptor.Digest.String(),
|
||||
LocalName: target,
|
||||
TLSVerify: common.ToPtr(false),
|
||||
ListDigest: listDigest,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (reg *Registry) Close() {
|
||||
reg.server.Close()
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type resolveResult struct {
|
||||
spec Spec
|
||||
err error
|
||||
}
|
||||
|
||||
type Resolver struct {
|
||||
jobs int
|
||||
queue chan resolveResult
|
||||
|
||||
ctx context.Context
|
||||
|
||||
Arch string
|
||||
AuthFilePath string
|
||||
}
|
||||
|
||||
type SourceSpec struct {
|
||||
Source string
|
||||
Name string
|
||||
TLSVerify *bool
|
||||
}
|
||||
|
||||
func NewResolver(arch string) Resolver {
|
||||
return Resolver{
|
||||
ctx: context.Background(),
|
||||
queue: make(chan resolveResult, 2),
|
||||
Arch: arch,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resolver) Add(spec SourceSpec) {
|
||||
client, err := NewClient(spec.Source)
|
||||
r.jobs += 1
|
||||
|
||||
if err != nil {
|
||||
r.queue <- resolveResult{err: err}
|
||||
return
|
||||
}
|
||||
|
||||
client.SetTLSVerify(spec.TLSVerify)
|
||||
client.SetArchitectureChoice(r.Arch)
|
||||
if r.AuthFilePath != "" {
|
||||
client.SetAuthFilePath(r.AuthFilePath)
|
||||
}
|
||||
|
||||
go func() {
|
||||
spec, err := client.Resolve(r.ctx, spec.Name)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("'%s': %w", spec.Source, err)
|
||||
}
|
||||
r.queue <- resolveResult{spec: spec, err: err}
|
||||
}()
|
||||
}
|
||||
|
||||
func (r *Resolver) Finish() ([]Spec, error) {
|
||||
|
||||
specs := make([]Spec, 0, r.jobs)
|
||||
errs := make([]string, 0, r.jobs)
|
||||
for r.jobs > 0 {
|
||||
result := <-r.queue
|
||||
r.jobs -= 1
|
||||
|
||||
if result.err == nil {
|
||||
specs = append(specs, result.spec)
|
||||
} else {
|
||||
errs = append(errs, result.err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
detail := strings.Join(errs, "; ")
|
||||
return specs, fmt.Errorf("failed to resolve container: %s", detail)
|
||||
}
|
||||
|
||||
return specs, nil
|
||||
}
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
package container_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/container"
|
||||
)
|
||||
|
||||
type lessCompare func(i, j int) bool
|
||||
|
||||
func makeSpecSorter(specs []container.Spec) lessCompare {
|
||||
return func(i, j int) bool {
|
||||
return specs[i].Digest < specs[j].Digest
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolver(t *testing.T) {
|
||||
|
||||
registry := NewTestRegistry()
|
||||
defer registry.Close()
|
||||
|
||||
repo := registry.AddRepo("library/osbuild")
|
||||
ref := registry.GetRef("library/osbuild")
|
||||
|
||||
refs := make([]string, 10)
|
||||
for i := 0; i < len(refs); i++ {
|
||||
checksum := repo.AddImage(
|
||||
[]Blob{NewDataBlobFromBase64(rootLayer)},
|
||||
[]string{"amd64", "ppc64le"},
|
||||
fmt.Sprintf("image %d", i),
|
||||
time.Time{})
|
||||
|
||||
tag := fmt.Sprintf("%d", i)
|
||||
repo.AddTag(checksum, tag)
|
||||
refs[i] = fmt.Sprintf("%s:%s", ref, tag)
|
||||
}
|
||||
|
||||
resolver := container.NewResolver("amd64")
|
||||
|
||||
for _, r := range refs {
|
||||
resolver.Add(container.SourceSpec{r, "", common.ToPtr(false)})
|
||||
}
|
||||
|
||||
have, err := resolver.Finish()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, have)
|
||||
|
||||
assert.Len(t, have, len(refs))
|
||||
|
||||
want := make([]container.Spec, len(refs))
|
||||
for i, r := range refs {
|
||||
spec, err := registry.Resolve(r, "amd64")
|
||||
assert.NoError(t, err)
|
||||
want[i] = spec
|
||||
}
|
||||
|
||||
sort.Slice(have, makeSpecSorter(have))
|
||||
sort.Slice(want, makeSpecSorter(want))
|
||||
|
||||
assert.ElementsMatch(t, have, want)
|
||||
}
|
||||
|
||||
func TestResolverFail(t *testing.T) {
|
||||
resolver := container.NewResolver("amd64")
|
||||
|
||||
resolver.Add(container.SourceSpec{"invalid-reference@${IMAGE_DIGEST}", "", common.ToPtr(false)})
|
||||
|
||||
specs, err := resolver.Finish()
|
||||
assert.Error(t, err)
|
||||
assert.Len(t, specs, 0)
|
||||
|
||||
registry := NewTestRegistry()
|
||||
defer registry.Close()
|
||||
|
||||
resolver.Add(container.SourceSpec{registry.GetRef("repo"), "", common.ToPtr(false)})
|
||||
specs, err = resolver.Finish()
|
||||
assert.Error(t, err)
|
||||
assert.Len(t, specs, 0)
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"github.com/containers/image/v5/docker/reference"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// A Spec is the specification of how to get a specific
|
||||
// container from a Source and under what LocalName to
|
||||
// store it in an image. The container is identified by
|
||||
// at the Source via Digest and ImageID. The latter one
|
||||
// should remain the same in the target image as well.
|
||||
type Spec struct {
|
||||
Source string // does not include the manifest digest
|
||||
Digest string // digest of the manifest at the Source
|
||||
TLSVerify *bool // controls TLS verification
|
||||
ImageID string // container image identifier
|
||||
LocalName string // name to use inside the image
|
||||
ListDigest string // digest of the list manifest at the Source (optional)
|
||||
}
|
||||
|
||||
// NewSpec creates a new Spec from the essential information.
|
||||
// It also converts is the transition point from container
|
||||
// specific types (digest.Digest) to generic types (string).
|
||||
func NewSpec(source reference.Named, digest, imageID digest.Digest, tlsVerify *bool, listDigest string, localName string) Spec {
|
||||
if localName == "" {
|
||||
localName = source.String()
|
||||
}
|
||||
return Spec{
|
||||
Source: source.Name(),
|
||||
Digest: digest.String(),
|
||||
TLSVerify: tlsVerify,
|
||||
ImageID: imageID.String(),
|
||||
LocalName: localName,
|
||||
ListDigest: listDigest,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
package crypt
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CryptSHA512 encrypts the given password with SHA512 and a random salt.
|
||||
//
|
||||
// Note that this function is not deterministic.
|
||||
func CryptSHA512(phrase string) (string, error) {
|
||||
const SHA512SaltLength = 16
|
||||
|
||||
salt, err := genSalt(SHA512SaltLength)
|
||||
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
hashSettings := "$6$" + salt
|
||||
return crypt(phrase, hashSettings)
|
||||
}
|
||||
|
||||
func genSalt(length int) (string, error) {
|
||||
saltChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
|
||||
|
||||
b := make([]byte, length)
|
||||
|
||||
for i := range b {
|
||||
runeIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(saltChars))))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
b[i] = saltChars[runeIndex.Int64()]
|
||||
}
|
||||
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
// PasswordIsCrypted returns true if the password appears to be an encrypted
|
||||
// one, according to a very simple heuristic.
|
||||
//
|
||||
// Any string starting with one of $2$, $6$ or $5$ is considered to be
|
||||
// encrypted. Any other string is consdirede to be unencrypted.
|
||||
//
|
||||
// This functionality is taken from pylorax.
|
||||
func PasswordIsCrypted(s string) bool {
|
||||
// taken from lorax src: src/pylorax/api/compose.py:533
|
||||
prefixes := [...]string{"$2b$", "$6$", "$5$"}
|
||||
|
||||
for _, prefix := range prefixes {
|
||||
if strings.HasPrefix(s, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
//go:build !darwin
|
||||
|
||||
// Copied from https://github.com/amoghe/go-crypt/blob/b3e291286513a0c993f7c4dd7060d327d2d56143/crypt_r.go
|
||||
// Original sources are under MIT license:
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2015 Akshay Moghe
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// Package crypt provides wrappers around functions available in crypt.h
|
||||
//
|
||||
// It wraps around the GNU specific extension (crypt_r) when it is available
|
||||
// (i.e. where GOOS=linux). This makes the go function reentrant (and thus
|
||||
// callable from concurrent goroutines).
|
||||
package crypt
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -lcrypt
|
||||
|
||||
// this is needed for Ubuntu
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <crypt.h>
|
||||
|
||||
char *gnu_ext_crypt(char *pass, char *salt) {
|
||||
char *enc = NULL;
|
||||
char *ret = NULL;
|
||||
struct crypt_data data;
|
||||
data.initialized = 0;
|
||||
|
||||
enc = crypt_r(pass, salt, &data);
|
||||
if(enc == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = (char *)malloc((strlen(enc)+1) * sizeof(char)); // for trailing null
|
||||
strcpy(ret, enc);
|
||||
ret[strlen(enc)]= '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// Crypt provides a wrapper around the glibc crypt_r() function.
|
||||
// For the meaning of the arguments, refer to the package README.
|
||||
func crypt(pass, salt string) (string, error) {
|
||||
c_pass := C.CString(pass)
|
||||
defer C.free(unsafe.Pointer(c_pass))
|
||||
|
||||
c_salt := C.CString(salt)
|
||||
defer C.free(unsafe.Pointer(c_salt))
|
||||
|
||||
c_enc, err := C.gnu_ext_crypt(c_pass, c_salt)
|
||||
if c_enc == nil {
|
||||
return "", err
|
||||
}
|
||||
defer C.free(unsafe.Pointer(c_enc))
|
||||
|
||||
// Return nil error if the string is non-nil.
|
||||
// As per the errno.h manpage, functions are allowed to set errno
|
||||
// on success. Caller should ignore errno on success.
|
||||
return C.GoString(c_enc), nil
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
//go:build darwin
|
||||
|
||||
package crypt
|
||||
|
||||
func crypt(pass, salt string) (string, error) {
|
||||
panic("You must not run osbuild-composer on macOS!")
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
//go:build !darwin
|
||||
|
||||
package crypt
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_crypt_PasswordIsCrypted(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
password string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "bcrypt",
|
||||
password: "$2b$04$123465789012345678901uac5A8egfBuZVHMrDZsQzR96IqNBivCy",
|
||||
want: true,
|
||||
}, {
|
||||
name: "sha256",
|
||||
password: "$5$1234567890123456$v.2bOKKLlpmUSKn0rxJmgnh.e3wOKivAVNZmNrOsoA3",
|
||||
want: true,
|
||||
}, {
|
||||
name: "sha512",
|
||||
password: "$6$1234567890123456$d.pgKQFaiD8bRiExg5NesbGR/3u51YvxeYaQXPzx4C6oSYREw8VoReiuYZjx0V9OhGVTZFqhc6emAxT1RC5BV.",
|
||||
want: true,
|
||||
}, {
|
||||
name: "scrypt",
|
||||
password: "$7$123456789012345", //not actual hash output from scrypt
|
||||
want: false,
|
||||
}, {
|
||||
name: "plain",
|
||||
password: "password",
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if got := PasswordIsCrypted(test.password); got != test.want {
|
||||
t.Errorf("PasswordIsCrypted() =%v, want %v", got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCryptSHA512(t *testing.T) {
|
||||
retPassFirst, err := CryptSHA512("testPass")
|
||||
assert.NoError(t, err)
|
||||
retPassSecond, _ := CryptSHA512("testPass")
|
||||
expectedPassStart := "$6$"
|
||||
assert.Equal(t, expectedPassStart, retPassFirst[0:3])
|
||||
assert.NotEqual(t, retPassFirst, retPassSecond)
|
||||
}
|
||||
|
||||
func TestGenSalt(t *testing.T) {
|
||||
length := 10
|
||||
retSaltFirst, err := genSalt(length)
|
||||
assert.NoError(t, err)
|
||||
retSaltSecond, _ := genSalt(length)
|
||||
assert.NotEqual(t, retSaltFirst, retSaltSecond)
|
||||
}
|
||||
|
|
@ -1,158 +0,0 @@
|
|||
package disk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Btrfs struct {
|
||||
UUID string
|
||||
Label string
|
||||
Mountpoint string
|
||||
Subvolumes []BtrfsSubvolume
|
||||
}
|
||||
|
||||
func (b *Btrfs) IsContainer() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *Btrfs) Clone() Entity {
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
clone := &Btrfs{
|
||||
UUID: b.UUID,
|
||||
Label: b.Label,
|
||||
Mountpoint: b.Mountpoint,
|
||||
Subvolumes: make([]BtrfsSubvolume, len(b.Subvolumes)),
|
||||
}
|
||||
|
||||
for idx, subvol := range b.Subvolumes {
|
||||
entClone := subvol.Clone()
|
||||
svClone, cloneOk := entClone.(*BtrfsSubvolume)
|
||||
if !cloneOk {
|
||||
panic("BtrfsSubvolume.Clone() returned an Entity that cannot be converted to *BtrfsSubvolume; this is a programming error")
|
||||
}
|
||||
clone.Subvolumes[idx] = *svClone
|
||||
}
|
||||
|
||||
return clone
|
||||
}
|
||||
|
||||
func (b *Btrfs) GetItemCount() uint {
|
||||
return uint(len(b.Subvolumes))
|
||||
}
|
||||
|
||||
func (b *Btrfs) GetChild(n uint) Entity {
|
||||
return &b.Subvolumes[n]
|
||||
}
|
||||
func (b *Btrfs) CreateMountpoint(mountpoint string, size uint64) (Entity, error) {
|
||||
name := mountpoint
|
||||
if name == "/" {
|
||||
name = "root"
|
||||
}
|
||||
subvolume := BtrfsSubvolume{
|
||||
Size: size,
|
||||
Mountpoint: mountpoint,
|
||||
GroupID: 0,
|
||||
UUID: b.UUID, // subvolumes inherit UUID of main volume
|
||||
Name: name,
|
||||
}
|
||||
|
||||
b.Subvolumes = append(b.Subvolumes, subvolume)
|
||||
return &b.Subvolumes[len(b.Subvolumes)-1], nil
|
||||
}
|
||||
|
||||
func (b *Btrfs) AlignUp(size uint64) uint64 {
|
||||
return size // No extra alignment necessary for subvolumes
|
||||
}
|
||||
|
||||
func (b *Btrfs) GenUUID(rng *rand.Rand) {
|
||||
if b.UUID == "" {
|
||||
b.UUID = uuid.Must(newRandomUUIDFromReader(rng)).String()
|
||||
}
|
||||
}
|
||||
|
||||
type BtrfsSubvolume struct {
|
||||
Name string
|
||||
Size uint64
|
||||
Mountpoint string
|
||||
GroupID uint64
|
||||
|
||||
MntOps string
|
||||
|
||||
// UUID of the parent volume
|
||||
UUID string
|
||||
}
|
||||
|
||||
func (subvol *BtrfsSubvolume) IsContainer() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (bs *BtrfsSubvolume) Clone() Entity {
|
||||
if bs == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &BtrfsSubvolume{
|
||||
Name: bs.Name,
|
||||
Size: bs.Size,
|
||||
Mountpoint: bs.Mountpoint,
|
||||
GroupID: bs.GroupID,
|
||||
MntOps: bs.MntOps,
|
||||
UUID: bs.UUID,
|
||||
}
|
||||
}
|
||||
|
||||
func (bs *BtrfsSubvolume) GetSize() uint64 {
|
||||
if bs == nil {
|
||||
return 0
|
||||
}
|
||||
return bs.Size
|
||||
}
|
||||
|
||||
func (bs *BtrfsSubvolume) EnsureSize(s uint64) bool {
|
||||
if s > bs.Size {
|
||||
bs.Size = s
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (bs *BtrfsSubvolume) GetMountpoint() string {
|
||||
if bs == nil {
|
||||
return ""
|
||||
}
|
||||
return bs.Mountpoint
|
||||
}
|
||||
|
||||
func (bs *BtrfsSubvolume) GetFSType() string {
|
||||
return "btrfs"
|
||||
}
|
||||
|
||||
func (bs *BtrfsSubvolume) GetFSSpec() FSSpec {
|
||||
if bs == nil {
|
||||
return FSSpec{}
|
||||
}
|
||||
return FSSpec{
|
||||
UUID: bs.UUID,
|
||||
Label: bs.Name,
|
||||
}
|
||||
}
|
||||
|
||||
func (bs *BtrfsSubvolume) GetFSTabOptions() FSTabOptions {
|
||||
if bs == nil {
|
||||
return FSTabOptions{}
|
||||
}
|
||||
|
||||
ops := strings.Join([]string{bs.MntOps, fmt.Sprintf("subvol=%s", bs.Name)}, ",")
|
||||
return FSTabOptions{
|
||||
MntOps: ops,
|
||||
Freq: 0,
|
||||
PassNo: 0,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
// Disk package contains abstract data-types to define disk-related entities.
|
||||
//
|
||||
// The disk package is a collection of interfaces and structs that can be used
|
||||
// to represent an disk image with its layout. Various concrete types, such as
|
||||
// PartitionTable, Partition and Filesystem types are defined to model a given
|
||||
// disk layout. These implement a collection of interfaces that can be used to
|
||||
// navigate and operate on the various possible combinations of entities in a
|
||||
// generic way. The entity data model is very generic so that it can represent
|
||||
// all possible layouts, which can be arbitrarily complex, since technologies
|
||||
// like logical volume management, LUKS2 container and file systems, that can
|
||||
// have sub-volumes, allow for complex layouts.
|
||||
// Entity and Container are the two main interfaces that are used to model the
|
||||
// tree structure of a disk image layout. The other entity interfaces, such as
|
||||
// Sizeable and Mountable, then describe various properties and capabilities
|
||||
// of a given entity.
|
||||
|
||||
package disk
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"math/rand"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
// Default sector size in bytes
|
||||
DefaultSectorSize = 512
|
||||
|
||||
DefaultGrainBytes = uint64(1048576) // 1 MiB
|
||||
|
||||
// UUIDs
|
||||
BIOSBootPartitionGUID = "21686148-6449-6E6F-744E-656564454649"
|
||||
BIOSBootPartitionUUID = "FAC7F1FB-3E8D-4137-A512-961DE09A5549"
|
||||
|
||||
FilesystemDataGUID = "0FC63DAF-8483-4772-8E79-3D69D8477DE4"
|
||||
FilesystemDataUUID = "CB07C243-BC44-4717-853E-28852021225B"
|
||||
|
||||
EFISystemPartitionGUID = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
|
||||
EFISystemPartitionUUID = "68B2905B-DF3E-4FB3-80FA-49D1E773AA33"
|
||||
EFIFilesystemUUID = "7B77-95E7"
|
||||
|
||||
LVMPartitionGUID = "E6D6D379-F507-44C2-A23C-238F2A3DF928"
|
||||
PRePartitionGUID = "9E1A2D38-C612-4316-AA26-8B49521E5A8B"
|
||||
|
||||
RootPartitionUUID = "6264D520-3FB9-423F-8AB8-7A0A8E3D3562"
|
||||
|
||||
// Extended Boot Loader Partition
|
||||
XBootLDRPartitionGUID = "BC13C2FF-59E6-4262-A352-B275FD6F7172"
|
||||
)
|
||||
|
||||
// Entity is the base interface for all disk-related entities.
|
||||
type Entity interface {
|
||||
// IsContainer indicates if the implementing type can
|
||||
// contain any other entities.
|
||||
IsContainer() bool
|
||||
|
||||
// Clone returns a deep copy of the entity.
|
||||
Clone() Entity
|
||||
}
|
||||
|
||||
// Container is the interface for entities that can contain other entities.
|
||||
// Together with the base Entity interface this allows to model a generic
|
||||
// entity tree of theoretically arbitrary depth and width.
|
||||
type Container interface {
|
||||
Entity
|
||||
|
||||
// GetItemCount returns the number of actual child entities.
|
||||
GetItemCount() uint
|
||||
|
||||
// GetChild returns the child entity at the given index.
|
||||
GetChild(n uint) Entity
|
||||
}
|
||||
|
||||
// Sizeable is implemented by entities that carry size information.
|
||||
type Sizeable interface {
|
||||
// EnsureSize will resize the entity to the given size in case
|
||||
// it is currently smaller. Returns if the size was changed.
|
||||
EnsureSize(size uint64) bool
|
||||
|
||||
// GetSize returns the size of the entity in bytes.
|
||||
GetSize() uint64
|
||||
}
|
||||
|
||||
// A Mountable entity is an entity that can be mounted.
|
||||
type Mountable interface {
|
||||
|
||||
// GetMountPoint returns the path of the mount point.
|
||||
GetMountpoint() string
|
||||
|
||||
// GetFSType returns the file system type, e.g. 'xfs'.
|
||||
GetFSType() string
|
||||
|
||||
// GetFSSpec returns the file system spec information.
|
||||
GetFSSpec() FSSpec
|
||||
|
||||
// GetFSTabOptions returns options for mounting the entity.
|
||||
GetFSTabOptions() FSTabOptions
|
||||
}
|
||||
|
||||
// A MountpointCreator is a container that is able to create new volumes.
|
||||
//
|
||||
// CreateMountpoint creates a new mountpoint with the given size and
|
||||
// returns the entity that represents the new mountpoint.
|
||||
type MountpointCreator interface {
|
||||
CreateMountpoint(mountpoint string, size uint64) (Entity, error)
|
||||
|
||||
// AlignUp will align the given bytes according to the
|
||||
// requirements of the container type.
|
||||
AlignUp(size uint64) uint64
|
||||
}
|
||||
|
||||
// A UniqueEntity is an entity that can be uniquely identified via a UUID.
|
||||
//
|
||||
// GenUUID generates a UUID for the entity if it does not yet have one.
|
||||
type UniqueEntity interface {
|
||||
Entity
|
||||
GenUUID(rng *rand.Rand)
|
||||
}
|
||||
|
||||
// VolumeContainer is a specific container that contains volume entities
|
||||
type VolumeContainer interface {
|
||||
|
||||
// MetadataSize returns the size of the container's metadata (in
|
||||
// bytes), i.e. the storage space that needs to be reserved for
|
||||
// the container itself, in contrast to the data it contains.
|
||||
MetadataSize() uint64
|
||||
}
|
||||
|
||||
// FSSpec for a filesystem (UUID and Label); the first field of fstab(5)
|
||||
type FSSpec struct {
|
||||
UUID string
|
||||
Label string
|
||||
}
|
||||
|
||||
type FSTabOptions struct {
|
||||
// The fourth field of fstab(5); fs_mntops
|
||||
MntOps string
|
||||
// The fifth field of fstab(5); fs_freq
|
||||
Freq uint64
|
||||
// The sixth field of fstab(5); fs_passno
|
||||
PassNo uint64
|
||||
}
|
||||
|
||||
// uuid generator helpers
|
||||
|
||||
// GeneratesnewRandomUUIDFromReader generates a new random UUID (version
|
||||
// 4 using) via the given random number generator.
|
||||
func newRandomUUIDFromReader(r io.Reader) (uuid.UUID, error) {
|
||||
var id uuid.UUID
|
||||
_, err := io.ReadFull(r, id[:])
|
||||
if err != nil {
|
||||
return uuid.Nil, err
|
||||
}
|
||||
id[6] = (id[6] & 0x0f) | 0x40 // Version 4
|
||||
id[8] = (id[8] & 0x3f) | 0x80 // Variant is 10
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// NewVolIDFromRand creates a random 32 bit hex string to use as a
|
||||
// volume ID for FAT filesystems
|
||||
func NewVolIDFromRand(r *rand.Rand) string {
|
||||
volid := make([]byte, 4)
|
||||
len, _ := r.Read(volid)
|
||||
if len != 4 {
|
||||
panic("expected four random bytes")
|
||||
}
|
||||
return hex.EncodeToString(volid)
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,85 +0,0 @@
|
|||
package disk
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// Filesystem related functions
|
||||
type Filesystem struct {
|
||||
Type string
|
||||
// ID of the filesystem, vfat doesn't use traditional UUIDs, therefore this
|
||||
// is just a string.
|
||||
UUID string
|
||||
Label string
|
||||
Mountpoint string
|
||||
// The fourth field of fstab(5); fs_mntops
|
||||
FSTabOptions string
|
||||
// The fifth field of fstab(5); fs_freq
|
||||
FSTabFreq uint64
|
||||
// The sixth field of fstab(5); fs_passno
|
||||
FSTabPassNo uint64
|
||||
}
|
||||
|
||||
func (fs *Filesystem) IsContainer() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Clone the filesystem structure
|
||||
func (fs *Filesystem) Clone() Entity {
|
||||
if fs == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &Filesystem{
|
||||
Type: fs.Type,
|
||||
UUID: fs.UUID,
|
||||
Label: fs.Label,
|
||||
Mountpoint: fs.Mountpoint,
|
||||
FSTabOptions: fs.FSTabOptions,
|
||||
FSTabFreq: fs.FSTabFreq,
|
||||
FSTabPassNo: fs.FSTabPassNo,
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *Filesystem) GetMountpoint() string {
|
||||
if fs == nil {
|
||||
return ""
|
||||
}
|
||||
return fs.Mountpoint
|
||||
}
|
||||
|
||||
func (fs *Filesystem) GetFSType() string {
|
||||
if fs == nil {
|
||||
return ""
|
||||
}
|
||||
return fs.Type
|
||||
}
|
||||
|
||||
func (fs *Filesystem) GetFSSpec() FSSpec {
|
||||
if fs == nil {
|
||||
return FSSpec{}
|
||||
}
|
||||
return FSSpec{
|
||||
UUID: fs.UUID,
|
||||
Label: fs.Label,
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *Filesystem) GetFSTabOptions() FSTabOptions {
|
||||
if fs == nil {
|
||||
return FSTabOptions{}
|
||||
}
|
||||
return FSTabOptions{
|
||||
MntOps: fs.FSTabOptions,
|
||||
Freq: fs.FSTabFreq,
|
||||
PassNo: fs.FSTabPassNo,
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *Filesystem) GenUUID(rng *rand.Rand) {
|
||||
if fs.UUID == "" {
|
||||
fs.UUID = uuid.Must(newRandomUUIDFromReader(rng)).String()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
package disk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Argon2id struct {
|
||||
Iterations uint
|
||||
Memory uint
|
||||
Parallelism uint
|
||||
}
|
||||
|
||||
type ClevisBind struct {
|
||||
Pin string
|
||||
Policy string
|
||||
RemovePassphrase bool
|
||||
}
|
||||
type LUKSContainer struct {
|
||||
Passphrase string
|
||||
UUID string
|
||||
Cipher string
|
||||
Label string
|
||||
Subsystem string
|
||||
SectorSize uint64
|
||||
|
||||
// password-based key derivation function
|
||||
PBKDF Argon2id
|
||||
|
||||
Clevis *ClevisBind
|
||||
|
||||
Payload Entity
|
||||
}
|
||||
|
||||
func (lc *LUKSContainer) IsContainer() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (lc *LUKSContainer) GetItemCount() uint {
|
||||
if lc.Payload == nil {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (lc *LUKSContainer) GetChild(n uint) Entity {
|
||||
if n != 0 {
|
||||
panic(fmt.Sprintf("invalid child index for LUKSContainer: %d != 0", n))
|
||||
}
|
||||
return lc.Payload
|
||||
}
|
||||
|
||||
func (lc *LUKSContainer) Clone() Entity {
|
||||
if lc == nil {
|
||||
return nil
|
||||
}
|
||||
clc := &LUKSContainer{
|
||||
Passphrase: lc.Passphrase,
|
||||
UUID: lc.UUID,
|
||||
Cipher: lc.Cipher,
|
||||
Label: lc.Label,
|
||||
Subsystem: lc.Subsystem,
|
||||
SectorSize: lc.SectorSize,
|
||||
PBKDF: Argon2id{
|
||||
Iterations: lc.PBKDF.Iterations,
|
||||
Memory: lc.PBKDF.Memory,
|
||||
Parallelism: lc.PBKDF.Parallelism,
|
||||
},
|
||||
Payload: lc.Payload.Clone(),
|
||||
}
|
||||
if lc.Clevis != nil {
|
||||
clc.Clevis = &ClevisBind{
|
||||
Pin: lc.Clevis.Pin,
|
||||
Policy: lc.Clevis.Policy,
|
||||
RemovePassphrase: lc.Clevis.RemovePassphrase,
|
||||
}
|
||||
}
|
||||
return clc
|
||||
}
|
||||
|
||||
func (lc *LUKSContainer) GenUUID(rng *rand.Rand) {
|
||||
if lc == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if lc.UUID == "" {
|
||||
lc.UUID = uuid.Must(newRandomUUIDFromReader(rng)).String()
|
||||
}
|
||||
}
|
||||
|
||||
func (lc *LUKSContainer) MetadataSize() uint64 {
|
||||
if lc == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
// 16 MiB is the default size for the LUKS2 header
|
||||
return 16 * 1024 * 1024
|
||||
}
|
||||
|
|
@ -1,205 +0,0 @@
|
|||
package disk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
)
|
||||
|
||||
// Default physical extent size in bytes: logical volumes
|
||||
// created inside the VG will be aligned to this.
|
||||
const LVMDefaultExtentSize = 4 * common.MebiByte
|
||||
|
||||
type LVMVolumeGroup struct {
|
||||
Name string
|
||||
Description string
|
||||
|
||||
LogicalVolumes []LVMLogicalVolume
|
||||
}
|
||||
|
||||
func (vg *LVMVolumeGroup) IsContainer() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (vg *LVMVolumeGroup) Clone() Entity {
|
||||
if vg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
clone := &LVMVolumeGroup{
|
||||
Name: vg.Name,
|
||||
Description: vg.Description,
|
||||
LogicalVolumes: make([]LVMLogicalVolume, len(vg.LogicalVolumes)),
|
||||
}
|
||||
|
||||
for idx, lv := range vg.LogicalVolumes {
|
||||
ent := lv.Clone()
|
||||
|
||||
// lv.Clone() will return nil only if the logical volume is nil
|
||||
if ent == nil {
|
||||
panic(fmt.Sprintf("logical volume %d in a LVM volume group is nil; this is a programming error", idx))
|
||||
}
|
||||
|
||||
lv, cloneOk := ent.(*LVMLogicalVolume)
|
||||
if !cloneOk {
|
||||
panic("LVMLogicalVolume.Clone() returned an Entity that cannot be converted to *LVMLogicalVolume; this is a programming error")
|
||||
}
|
||||
|
||||
clone.LogicalVolumes[idx] = *lv
|
||||
}
|
||||
|
||||
return clone
|
||||
}
|
||||
|
||||
func (vg *LVMVolumeGroup) GetItemCount() uint {
|
||||
if vg == nil {
|
||||
return 0
|
||||
}
|
||||
return uint(len(vg.LogicalVolumes))
|
||||
}
|
||||
|
||||
func (vg *LVMVolumeGroup) GetChild(n uint) Entity {
|
||||
if vg == nil {
|
||||
panic("LVMVolumeGroup.GetChild: nil entity")
|
||||
}
|
||||
return &vg.LogicalVolumes[n]
|
||||
}
|
||||
|
||||
func (vg *LVMVolumeGroup) CreateMountpoint(mountpoint string, size uint64) (Entity, error) {
|
||||
|
||||
filesystem := Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: mountpoint,
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
}
|
||||
|
||||
return vg.CreateLogicalVolume(mountpoint, size, &filesystem)
|
||||
}
|
||||
|
||||
func (vg *LVMVolumeGroup) CreateLogicalVolume(lvName string, size uint64, payload Entity) (Entity, error) {
|
||||
if vg == nil {
|
||||
panic("LVMVolumeGroup.CreateLogicalVolume: nil entity")
|
||||
}
|
||||
|
||||
names := make(map[string]bool, len(vg.LogicalVolumes))
|
||||
for _, lv := range vg.LogicalVolumes {
|
||||
names[lv.Name] = true
|
||||
}
|
||||
|
||||
base := lvname(lvName)
|
||||
var exists bool
|
||||
name := base
|
||||
|
||||
// Make sure that we don't collide with an existing volume, e.g. 'home/test'
|
||||
// and /home/test_test would collide. We try 100 times and then give up. This
|
||||
// is mimicking what blivet does. See blivet/blivet.py#L1060 commit 2eb4bd4
|
||||
for i := 0; i < 100; i++ {
|
||||
exists = names[name]
|
||||
if !exists {
|
||||
break
|
||||
}
|
||||
|
||||
name = fmt.Sprintf("%s%02d", base, i)
|
||||
}
|
||||
|
||||
if exists {
|
||||
return nil, fmt.Errorf("could not create logical volume: name collision")
|
||||
}
|
||||
|
||||
lv := LVMLogicalVolume{
|
||||
Name: name,
|
||||
Size: vg.AlignUp(size),
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
vg.LogicalVolumes = append(vg.LogicalVolumes, lv)
|
||||
|
||||
return &vg.LogicalVolumes[len(vg.LogicalVolumes)-1], nil
|
||||
}
|
||||
|
||||
func (vg *LVMVolumeGroup) AlignUp(size uint64) uint64 {
|
||||
|
||||
if size%LVMDefaultExtentSize != 0 {
|
||||
size += LVMDefaultExtentSize - size%LVMDefaultExtentSize
|
||||
}
|
||||
|
||||
return size
|
||||
}
|
||||
|
||||
func (vg *LVMVolumeGroup) MetadataSize() uint64 {
|
||||
if vg == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
// LVM2 allows for a lot of customizations that will affect the size
|
||||
// of the metadata and its location and thus the start of the physical
|
||||
// extent. For now we assume the default which results in a start of
|
||||
// the physical extent 1 MiB
|
||||
return 1024 * 1024
|
||||
}
|
||||
|
||||
type LVMLogicalVolume struct {
|
||||
Name string
|
||||
Size uint64
|
||||
Payload Entity
|
||||
}
|
||||
|
||||
func (lv *LVMLogicalVolume) IsContainer() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (lv *LVMLogicalVolume) Clone() Entity {
|
||||
if lv == nil {
|
||||
return nil
|
||||
}
|
||||
return &LVMLogicalVolume{
|
||||
Name: lv.Name,
|
||||
Size: lv.Size,
|
||||
Payload: lv.Payload.Clone(),
|
||||
}
|
||||
}
|
||||
|
||||
func (lv *LVMLogicalVolume) GetItemCount() uint {
|
||||
if lv == nil || lv.Payload == nil {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (lv *LVMLogicalVolume) GetChild(n uint) Entity {
|
||||
if n != 0 || lv == nil {
|
||||
panic(fmt.Sprintf("invalid child index for LVMLogicalVolume: %d != 0", n))
|
||||
}
|
||||
return lv.Payload
|
||||
}
|
||||
|
||||
func (lv *LVMLogicalVolume) GetSize() uint64 {
|
||||
if lv == nil {
|
||||
return 0
|
||||
}
|
||||
return lv.Size
|
||||
}
|
||||
|
||||
func (lv *LVMLogicalVolume) EnsureSize(s uint64) bool {
|
||||
if lv == nil {
|
||||
panic("LVMLogicalVolume.EnsureSize: nil entity")
|
||||
}
|
||||
if s > lv.Size {
|
||||
lv.Size = s
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// lvname returns a name for a logical volume based on the mountpoint.
|
||||
func lvname(path string) string {
|
||||
if path == "/" {
|
||||
return "rootlv"
|
||||
}
|
||||
|
||||
path = strings.TrimLeft(path, "/")
|
||||
return strings.ReplaceAll(path, "/", "_") + "lv"
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package disk
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLVMVCreateMountpoint(t *testing.T) {
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
vg := &LVMVolumeGroup{
|
||||
Name: "root",
|
||||
Description: "root volume group",
|
||||
}
|
||||
|
||||
entity, err := vg.CreateMountpoint("/", 0)
|
||||
assert.NoError(err)
|
||||
rootlv := entity.(*LVMLogicalVolume)
|
||||
assert.Equal("rootlv", rootlv.Name)
|
||||
|
||||
_, err = vg.CreateMountpoint("/home_test", 0)
|
||||
assert.NoError(err)
|
||||
|
||||
entity, err = vg.CreateMountpoint("/home/test", 0)
|
||||
assert.NoError(err)
|
||||
|
||||
dedup := entity.(*LVMLogicalVolume)
|
||||
assert.Equal("home_testlv00", dedup.Name)
|
||||
|
||||
// Lets collide it
|
||||
for i := 0; i < 98; i++ {
|
||||
_, err = vg.CreateMountpoint("/home/test", 0)
|
||||
assert.NoError(err)
|
||||
}
|
||||
|
||||
_, err = vg.CreateMountpoint("/home/test", 0)
|
||||
assert.Error(err)
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
package disk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Partition struct {
|
||||
Start uint64 // Start of the partition in bytes
|
||||
Size uint64 // Size of the partition in bytes
|
||||
Type string // Partition type, e.g. 0x83 for MBR or a UUID for gpt
|
||||
Bootable bool // `Legacy BIOS bootable` (GPT) or `active` (DOS) flag
|
||||
|
||||
// ID of the partition, dos doesn't use traditional UUIDs, therefore this
|
||||
// is just a string.
|
||||
UUID string
|
||||
|
||||
// If nil, the partition is raw; It doesn't contain a payload.
|
||||
Payload Entity
|
||||
}
|
||||
|
||||
func (p *Partition) IsContainer() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *Partition) Clone() Entity {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
partition := &Partition{
|
||||
Start: p.Start,
|
||||
Size: p.Size,
|
||||
Type: p.Type,
|
||||
Bootable: p.Bootable,
|
||||
UUID: p.UUID,
|
||||
}
|
||||
|
||||
if p.Payload != nil {
|
||||
partition.Payload = p.Payload.Clone()
|
||||
}
|
||||
|
||||
return partition
|
||||
}
|
||||
|
||||
func (pt *Partition) GetItemCount() uint {
|
||||
if pt == nil || pt.Payload == nil {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (p *Partition) GetChild(n uint) Entity {
|
||||
if n != 0 {
|
||||
panic(fmt.Sprintf("invalid child index for Partition: %d != 0", n))
|
||||
}
|
||||
return p.Payload
|
||||
}
|
||||
|
||||
func (p *Partition) GetSize() uint64 {
|
||||
return p.Size
|
||||
}
|
||||
|
||||
// Ensure the partition has at least the given size. Will do nothing
|
||||
// if the partition is already larger. Returns if the size changed.
|
||||
func (p *Partition) EnsureSize(s uint64) bool {
|
||||
if s > p.Size {
|
||||
p.Size = s
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Partition) IsBIOSBoot() bool {
|
||||
if p == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return p.Type == BIOSBootPartitionGUID
|
||||
}
|
||||
|
||||
func (p *Partition) IsPReP() bool {
|
||||
if p == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return p.Type == "41" || p.Type == PRePartitionGUID
|
||||
}
|
||||
|
|
@ -1,691 +0,0 @@
|
|||
package disk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
)
|
||||
|
||||
type PartitionTable struct {
|
||||
Size uint64 // Size of the disk (in bytes).
|
||||
UUID string // Unique identifier of the partition table (GPT only).
|
||||
Type string // Partition table type, e.g. dos, gpt.
|
||||
Partitions []Partition
|
||||
|
||||
SectorSize uint64 // Sector size in bytes
|
||||
ExtraPadding uint64 // Extra space at the end of the partition table (sectors)
|
||||
}
|
||||
|
||||
func NewPartitionTable(basePT *PartitionTable, mountpoints []blueprint.FilesystemCustomization, imageSize uint64, lvmify bool, requiredSizes map[string]uint64, rng *rand.Rand) (*PartitionTable, error) {
|
||||
newPT := basePT.Clone().(*PartitionTable)
|
||||
|
||||
// first pass: enlarge existing mountpoints and collect new ones
|
||||
newMountpoints, _ := newPT.applyCustomization(mountpoints, false)
|
||||
|
||||
// if there is any new mountpoint and lvmify is enabled, ensure we have LVM layout
|
||||
if lvmify && len(newMountpoints) > 0 {
|
||||
err := newPT.ensureLVM()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// second pass: deal with new mountpoints and newly created ones, after switching to
|
||||
// the LVM layout, if requested, which might introduce new mount points, i.e. `/boot`
|
||||
_, err := newPT.applyCustomization(newMountpoints, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If no separate requiredSizes are given then we use our defaults
|
||||
if requiredSizes == nil {
|
||||
requiredSizes = map[string]uint64{
|
||||
"/": 1073741824,
|
||||
"/usr": 2147483648,
|
||||
}
|
||||
}
|
||||
|
||||
if len(requiredSizes) != 0 {
|
||||
newPT.EnsureDirectorySizes(requiredSizes)
|
||||
}
|
||||
|
||||
// Calculate partition table offsets and sizes
|
||||
newPT.relayout(imageSize)
|
||||
|
||||
// Generate new UUIDs for filesystems and partitions
|
||||
newPT.GenerateUUIDs(rng)
|
||||
|
||||
return newPT, nil
|
||||
}
|
||||
|
||||
func (pt *PartitionTable) IsContainer() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (pt *PartitionTable) Clone() Entity {
|
||||
if pt == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
clone := &PartitionTable{
|
||||
Size: pt.Size,
|
||||
UUID: pt.UUID,
|
||||
Type: pt.Type,
|
||||
Partitions: make([]Partition, len(pt.Partitions)),
|
||||
SectorSize: pt.SectorSize,
|
||||
ExtraPadding: pt.ExtraPadding,
|
||||
}
|
||||
|
||||
for idx, partition := range pt.Partitions {
|
||||
ent := partition.Clone()
|
||||
|
||||
// partition.Clone() will return nil only if the partition is nil
|
||||
if ent == nil {
|
||||
panic(fmt.Sprintf("partition %d in a Partition Table is nil; this is a programming error", idx))
|
||||
}
|
||||
|
||||
part, cloneOk := ent.(*Partition)
|
||||
if !cloneOk {
|
||||
panic("PartitionTable.Clone() returned an Entity that cannot be converted to *PartitionTable; this is a programming error")
|
||||
}
|
||||
|
||||
clone.Partitions[idx] = *part
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
// AlignUp will align the given bytes to next aligned grain if not already
|
||||
// aligned
|
||||
func (pt *PartitionTable) AlignUp(size uint64) uint64 {
|
||||
grain := DefaultGrainBytes
|
||||
if size%grain == 0 {
|
||||
// already aligned: return unchanged
|
||||
return size
|
||||
}
|
||||
return ((size + grain) / grain) * grain
|
||||
}
|
||||
|
||||
// Convert the given bytes to the number of sectors.
|
||||
func (pt *PartitionTable) BytesToSectors(size uint64) uint64 {
|
||||
sectorSize := pt.SectorSize
|
||||
if sectorSize == 0 {
|
||||
sectorSize = DefaultSectorSize
|
||||
}
|
||||
return size / sectorSize
|
||||
}
|
||||
|
||||
// Convert the given number of sectors to bytes.
|
||||
func (pt *PartitionTable) SectorsToBytes(size uint64) uint64 {
|
||||
sectorSize := pt.SectorSize
|
||||
if sectorSize == 0 {
|
||||
sectorSize = DefaultSectorSize
|
||||
}
|
||||
return size * sectorSize
|
||||
}
|
||||
|
||||
// Returns if the partition table contains a filesystem with the given
|
||||
// mount point.
|
||||
func (pt *PartitionTable) ContainsMountpoint(mountpoint string) bool {
|
||||
return len(entityPath(pt, mountpoint)) > 0
|
||||
}
|
||||
|
||||
// Generate all needed UUIDs for all the partiton and filesystems
|
||||
//
|
||||
// Will not overwrite existing UUIDs and only generate UUIDs for
|
||||
// partitions if the layout is GPT.
|
||||
func (pt *PartitionTable) GenerateUUIDs(rng *rand.Rand) {
|
||||
setuuid := func(ent Entity, path []Entity) error {
|
||||
if ui, ok := ent.(UniqueEntity); ok {
|
||||
ui.GenUUID(rng)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
_ = pt.ForEachEntity(setuuid)
|
||||
|
||||
// if this is a MBR partition table, there is no need to generate
|
||||
// uuids for the partitions themselves
|
||||
if pt.Type != "gpt" {
|
||||
return
|
||||
}
|
||||
|
||||
for idx, part := range pt.Partitions {
|
||||
if part.UUID == "" {
|
||||
pt.Partitions[idx].UUID = uuid.Must(newRandomUUIDFromReader(rng)).String()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pt *PartitionTable) GetItemCount() uint {
|
||||
return uint(len(pt.Partitions))
|
||||
}
|
||||
|
||||
func (pt *PartitionTable) GetChild(n uint) Entity {
|
||||
return &pt.Partitions[n]
|
||||
}
|
||||
|
||||
func (pt *PartitionTable) GetSize() uint64 {
|
||||
return pt.Size
|
||||
}
|
||||
|
||||
func (pt *PartitionTable) EnsureSize(s uint64) bool {
|
||||
if s > pt.Size {
|
||||
pt.Size = s
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (pt *PartitionTable) findDirectoryEntityPath(dir string) []Entity {
|
||||
if path := entityPath(pt, dir); path != nil {
|
||||
return path
|
||||
}
|
||||
|
||||
parent := filepath.Dir(dir)
|
||||
if dir == parent {
|
||||
// invalid dir or pt has no root
|
||||
return nil
|
||||
}
|
||||
|
||||
// move up the directory path and check again
|
||||
return pt.findDirectoryEntityPath(parent)
|
||||
}
|
||||
|
||||
// EnsureDirectorySizes takes a mapping of directory paths to sizes (in bytes)
|
||||
// and resizes the appropriate partitions such that they are at least the size
|
||||
// of the sum of their subdirectories plus their own sizes.
|
||||
// The function will panic if any of the directory paths are invalid.
|
||||
func (pt *PartitionTable) EnsureDirectorySizes(dirSizeMap map[string]uint64) {
|
||||
|
||||
type mntSize struct {
|
||||
entPath []Entity
|
||||
newSize uint64
|
||||
}
|
||||
|
||||
// add up the required size for each directory grouped by their mountpoints
|
||||
mntSizeMap := make(map[string]*mntSize)
|
||||
for dir, size := range dirSizeMap {
|
||||
entPath := pt.findDirectoryEntityPath(dir)
|
||||
if entPath == nil {
|
||||
panic(fmt.Sprintf("EnsureDirectorySizes: invalid dir path %q", dir))
|
||||
}
|
||||
mnt := entPath[0].(Mountable)
|
||||
mountpoint := mnt.GetMountpoint()
|
||||
if _, ok := mntSizeMap[mountpoint]; !ok {
|
||||
mntSizeMap[mountpoint] = &mntSize{entPath, 0}
|
||||
}
|
||||
es := mntSizeMap[mountpoint]
|
||||
es.newSize += size
|
||||
}
|
||||
|
||||
// resize all the entities in the map
|
||||
for _, es := range mntSizeMap {
|
||||
resizeEntityBranch(es.entPath, es.newSize)
|
||||
}
|
||||
}
|
||||
|
||||
func (pt *PartitionTable) CreateMountpoint(mountpoint string, size uint64) (Entity, error) {
|
||||
filesystem := Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: mountpoint,
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
}
|
||||
|
||||
partition := Partition{
|
||||
Size: size,
|
||||
Payload: &filesystem,
|
||||
}
|
||||
|
||||
n := len(pt.Partitions)
|
||||
var maxNo int
|
||||
|
||||
if pt.Type == "gpt" {
|
||||
switch mountpoint {
|
||||
case "/boot":
|
||||
partition.Type = XBootLDRPartitionGUID
|
||||
default:
|
||||
partition.Type = FilesystemDataGUID
|
||||
}
|
||||
maxNo = 128
|
||||
} else {
|
||||
maxNo = 4
|
||||
}
|
||||
|
||||
if n == maxNo {
|
||||
return nil, fmt.Errorf("maximum number of partitions reached (%d)", maxNo)
|
||||
}
|
||||
|
||||
pt.Partitions = append(pt.Partitions, partition)
|
||||
|
||||
return &pt.Partitions[len(pt.Partitions)-1], nil
|
||||
}
|
||||
|
||||
type EntityCallback func(e Entity, path []Entity) error
|
||||
|
||||
func forEachEntity(e Entity, path []Entity, cb EntityCallback) error {
|
||||
|
||||
childPath := append(path, e)
|
||||
err := cb(e, childPath)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c, ok := e.(Container)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
for idx := uint(0); idx < c.GetItemCount(); idx++ {
|
||||
child := c.GetChild(idx)
|
||||
err = forEachEntity(child, childPath, cb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ForEachEntity runs the provided callback function on each entity in
|
||||
// the PartitionTable.
|
||||
func (pt *PartitionTable) ForEachEntity(cb EntityCallback) error {
|
||||
return forEachEntity(pt, []Entity{}, cb)
|
||||
}
|
||||
|
||||
func (pt *PartitionTable) HeaderSize() uint64 {
|
||||
// always reserve one extra sector for the GPT header
|
||||
// this also ensure we have enough space for the MBR
|
||||
header := pt.SectorsToBytes(1)
|
||||
|
||||
if pt.Type == "dos" {
|
||||
return header
|
||||
}
|
||||
|
||||
// calculate the space we need for
|
||||
parts := len(pt.Partitions)
|
||||
|
||||
// reserve a minimum of 128 partition entires
|
||||
if parts < 128 {
|
||||
parts = 128
|
||||
}
|
||||
|
||||
// Assume that each partition entry is 128 bytes
|
||||
// which might not be the case if the partition
|
||||
// name exceeds 72 bytes
|
||||
header += uint64(parts * 128)
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
// Apply filesystem filesystem customization to the partiton table. If create is false
|
||||
// will only apply customizations to existing partitions and return unhandled, i.e new
|
||||
// ones. An error can only occur if create is set. Conversely, it will only return non
|
||||
// empty list of new mountpoints if create is false.
|
||||
// Does not relayout the table, i.e. a call to relayout might be needed.
|
||||
func (pt *PartitionTable) applyCustomization(mountpoints []blueprint.FilesystemCustomization, create bool) ([]blueprint.FilesystemCustomization, error) {
|
||||
|
||||
newMountpoints := []blueprint.FilesystemCustomization{}
|
||||
|
||||
for _, mnt := range mountpoints {
|
||||
size := clampFSSize(mnt.Mountpoint, mnt.MinSize)
|
||||
if path := entityPath(pt, mnt.Mountpoint); len(path) != 0 {
|
||||
size = alignEntityBranch(path, size)
|
||||
resizeEntityBranch(path, size)
|
||||
} else {
|
||||
if !create {
|
||||
newMountpoints = append(newMountpoints, mnt)
|
||||
} else if err := pt.createFilesystem(mnt.Mountpoint, size); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newMountpoints, nil
|
||||
}
|
||||
|
||||
// Dynamically calculate and update the start point for each of the existing
|
||||
// partitions. Adjusts the overall size of image to either the supplied
|
||||
// value in `size` or to the sum of all partitions if that is lager.
|
||||
// Will grow the root partition if there is any empty space.
|
||||
// Returns the updated start point.
|
||||
func (pt *PartitionTable) relayout(size uint64) uint64 {
|
||||
// always reserve one extra sector for the GPT header
|
||||
header := pt.HeaderSize()
|
||||
footer := uint64(0)
|
||||
|
||||
// The GPT header is also at the end of the partition table
|
||||
if pt.Type == "gpt" {
|
||||
footer = header
|
||||
}
|
||||
|
||||
start := pt.AlignUp(header)
|
||||
size = pt.AlignUp(size)
|
||||
|
||||
var rootIdx = -1
|
||||
for idx := range pt.Partitions {
|
||||
partition := &pt.Partitions[idx]
|
||||
if len(entityPath(partition, "/")) != 0 {
|
||||
rootIdx = idx
|
||||
continue
|
||||
}
|
||||
partition.Start = start
|
||||
partition.Size = pt.AlignUp(partition.Size)
|
||||
start += partition.Size
|
||||
}
|
||||
|
||||
if rootIdx < 0 {
|
||||
panic("no root filesystem found; this is a programming error")
|
||||
}
|
||||
|
||||
root := &pt.Partitions[rootIdx]
|
||||
root.Start = start
|
||||
|
||||
// add the extra padding specified in the partition table
|
||||
footer += pt.ExtraPadding
|
||||
|
||||
// If the sum of all partitions is bigger then the specified size,
|
||||
// we use that instead. Grow the partition table size if needed.
|
||||
end := pt.AlignUp(root.Start + footer + root.Size)
|
||||
if end > size {
|
||||
size = end
|
||||
}
|
||||
|
||||
if size > pt.Size {
|
||||
pt.Size = size
|
||||
}
|
||||
|
||||
// If there is space left in the partition table, grow root
|
||||
root.Size = pt.Size - root.Start
|
||||
|
||||
// Finally we shrink the last partition, i.e. the root partition,
|
||||
// to leave space for the footer, e.g. the secondary GPT header.
|
||||
root.Size -= footer
|
||||
|
||||
return start
|
||||
}
|
||||
|
||||
func (pt *PartitionTable) createFilesystem(mountpoint string, size uint64) error {
|
||||
rootPath := entityPath(pt, "/")
|
||||
if rootPath == nil {
|
||||
panic("no root mountpoint for PartitionTable")
|
||||
}
|
||||
|
||||
var vc MountpointCreator
|
||||
var entity Entity
|
||||
var idx int
|
||||
for idx, entity = range rootPath {
|
||||
var ok bool
|
||||
if vc, ok = entity.(MountpointCreator); ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if vc == nil {
|
||||
panic("could not find root volume container")
|
||||
}
|
||||
|
||||
newVol, err := vc.CreateMountpoint(mountpoint, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed creating volume: " + err.Error())
|
||||
}
|
||||
vcPath := append([]Entity{newVol}, rootPath[idx:]...)
|
||||
size = alignEntityBranch(vcPath, size)
|
||||
resizeEntityBranch(vcPath, size)
|
||||
return nil
|
||||
}
|
||||
|
||||
// entityPath stats at ent and searches for an Entity with a Mountpoint equal
|
||||
// to the target. Returns a slice of all the Entities leading to the Mountable
|
||||
// in reverse order. If no Entity has the target as a Mountpoint, returns nil.
|
||||
// If a slice is returned, the last element is always the starting Entity ent
|
||||
// and the first element is always a Mountable with a Mountpoint equal to the
|
||||
// target.
|
||||
func entityPath(ent Entity, target string) []Entity {
|
||||
switch e := ent.(type) {
|
||||
case Mountable:
|
||||
if target == e.GetMountpoint() {
|
||||
return []Entity{ent}
|
||||
}
|
||||
case Container:
|
||||
for idx := uint(0); idx < e.GetItemCount(); idx++ {
|
||||
child := e.GetChild(idx)
|
||||
path := entityPath(child, target)
|
||||
if path != nil {
|
||||
path = append(path, e)
|
||||
return path
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type MountableCallback func(mnt Mountable, path []Entity) error
|
||||
|
||||
func forEachMountable(c Container, path []Entity, cb MountableCallback) error {
|
||||
for idx := uint(0); idx < c.GetItemCount(); idx++ {
|
||||
child := c.GetChild(idx)
|
||||
childPath := append(path, child)
|
||||
var err error
|
||||
switch ent := child.(type) {
|
||||
case Mountable:
|
||||
err = cb(ent, childPath)
|
||||
case Container:
|
||||
err = forEachMountable(ent, childPath, cb)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ForEachMountable runs the provided callback function on each Mountable in
|
||||
// the PartitionTable.
|
||||
func (pt *PartitionTable) ForEachMountable(cb MountableCallback) error {
|
||||
return forEachMountable(pt, []Entity{pt}, cb)
|
||||
}
|
||||
|
||||
// FindMountable returns the Mountable entity with the given mountpoint in the
|
||||
// PartitionTable. Returns nil if no Entity has the target as a Mountpoint.
|
||||
func (pt *PartitionTable) FindMountable(mountpoint string) Mountable {
|
||||
path := entityPath(pt, mountpoint)
|
||||
|
||||
if len(path) == 0 {
|
||||
return nil
|
||||
}
|
||||
// first path element is guaranteed to be Mountable
|
||||
return path[0].(Mountable)
|
||||
}
|
||||
|
||||
func clampFSSize(mountpoint string, size uint64) uint64 {
|
||||
// set a minimum size of 1GB for all mountpoints
|
||||
// with the exception for '/boot' (= 500 MB)
|
||||
var minSize uint64 = 1073741824
|
||||
|
||||
if mountpoint == "/boot" {
|
||||
minSize = 524288000
|
||||
}
|
||||
|
||||
if minSize > size {
|
||||
return minSize
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func alignEntityBranch(path []Entity, size uint64) uint64 {
|
||||
if len(path) == 0 {
|
||||
return size
|
||||
}
|
||||
|
||||
element := path[0]
|
||||
|
||||
if c, ok := element.(MountpointCreator); ok {
|
||||
size = c.AlignUp(size)
|
||||
}
|
||||
|
||||
return alignEntityBranch(path[1:], size)
|
||||
}
|
||||
|
||||
// resizeEntityBranch resizes the first entity in the specified path to be at
|
||||
// least the specified size and then grows every entity up the path to the
|
||||
// PartitionTable accordingly.
|
||||
func resizeEntityBranch(path []Entity, size uint64) {
|
||||
if len(path) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
element := path[0]
|
||||
|
||||
if c, ok := element.(Container); ok {
|
||||
containerSize := uint64(0)
|
||||
for idx := uint(0); idx < c.GetItemCount(); idx++ {
|
||||
if s, ok := c.GetChild(idx).(Sizeable); ok {
|
||||
containerSize += s.GetSize()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if vc, ok := element.(VolumeContainer); ok {
|
||||
containerSize += vc.MetadataSize()
|
||||
}
|
||||
if containerSize > size {
|
||||
size = containerSize
|
||||
}
|
||||
}
|
||||
if sz, ok := element.(Sizeable); ok {
|
||||
if !sz.EnsureSize(size) {
|
||||
return
|
||||
}
|
||||
}
|
||||
resizeEntityBranch(path[1:], size)
|
||||
}
|
||||
|
||||
// GenUUID generates and sets UUIDs for all Partitions in the PartitionTable if
|
||||
// the layout is GPT.
|
||||
func (pt *PartitionTable) GenUUID(rng *rand.Rand) {
|
||||
if pt.UUID == "" {
|
||||
pt.UUID = uuid.Must(newRandomUUIDFromReader(rng)).String()
|
||||
}
|
||||
}
|
||||
|
||||
// ensureLVM will ensure that the root partition is on an LVM volume, i.e. if
|
||||
// it currently is not, it will wrap it in one
|
||||
func (pt *PartitionTable) ensureLVM() error {
|
||||
|
||||
rootPath := entityPath(pt, "/")
|
||||
if rootPath == nil {
|
||||
panic("no root mountpoint for PartitionTable")
|
||||
}
|
||||
|
||||
// we need a /boot partition to boot LVM, ensure one exists
|
||||
bootPath := entityPath(pt, "/boot")
|
||||
if bootPath == nil {
|
||||
_, err := pt.CreateMountpoint("/boot", 512*1024*1024)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rootPath = entityPath(pt, "/")
|
||||
}
|
||||
|
||||
parent := rootPath[1] // NB: entityPath has reversed order
|
||||
|
||||
if _, ok := parent.(*LVMLogicalVolume); ok {
|
||||
return nil
|
||||
} else if part, ok := parent.(*Partition); ok {
|
||||
filesystem := part.Payload
|
||||
|
||||
vg := &LVMVolumeGroup{
|
||||
Name: "rootvg",
|
||||
Description: "created via lvm2 and osbuild",
|
||||
}
|
||||
|
||||
_, err := vg.CreateLogicalVolume("root", part.Size, filesystem)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Could not create LV: %v", err))
|
||||
}
|
||||
|
||||
part.Payload = vg
|
||||
|
||||
// reset it so it will be grown later
|
||||
part.Size = 0
|
||||
|
||||
if pt.Type == "gpt" {
|
||||
part.Type = LVMPartitionGUID
|
||||
} else {
|
||||
part.Type = "8e"
|
||||
}
|
||||
|
||||
} else {
|
||||
panic("unsupported parent for LVM")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pt *PartitionTable) GetBuildPackages() []string {
|
||||
packages := []string{}
|
||||
|
||||
hasLVM := false
|
||||
hasBtrfs := false
|
||||
hasXFS := false
|
||||
hasFAT := false
|
||||
hasEXT4 := false
|
||||
hasLUKS := false
|
||||
|
||||
introspectPT := func(e Entity, path []Entity) error {
|
||||
switch ent := e.(type) {
|
||||
case *LVMLogicalVolume:
|
||||
hasLVM = true
|
||||
case *Btrfs:
|
||||
hasBtrfs = true
|
||||
case *Filesystem:
|
||||
switch ent.GetFSType() {
|
||||
case "vfat":
|
||||
hasFAT = true
|
||||
case "btrfs":
|
||||
hasBtrfs = true
|
||||
case "xfs":
|
||||
hasXFS = true
|
||||
case "ext4":
|
||||
hasEXT4 = true
|
||||
}
|
||||
case *LUKSContainer:
|
||||
hasLUKS = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
_ = pt.ForEachEntity(introspectPT)
|
||||
|
||||
// TODO: LUKS
|
||||
if hasLVM {
|
||||
packages = append(packages, "lvm2")
|
||||
}
|
||||
if hasBtrfs {
|
||||
packages = append(packages, "btrfs-progs")
|
||||
}
|
||||
if hasXFS {
|
||||
packages = append(packages, "xfsprogs")
|
||||
}
|
||||
if hasFAT {
|
||||
packages = append(packages, "dosfstools")
|
||||
}
|
||||
if hasEXT4 {
|
||||
packages = append(packages, "e2fsprogs")
|
||||
}
|
||||
if hasLUKS {
|
||||
packages = append(packages,
|
||||
"clevis",
|
||||
"clevis-luks",
|
||||
"cryptsetup",
|
||||
)
|
||||
}
|
||||
|
||||
return packages
|
||||
}
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
package distro
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/disk"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/ostree"
|
||||
"github.com/osbuild/osbuild-composer/internal/rhsm/facts"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/subscription"
|
||||
)
|
||||
|
||||
type BootMode uint64
|
||||
|
||||
const (
|
||||
BOOT_NONE BootMode = iota
|
||||
BOOT_LEGACY
|
||||
BOOT_UEFI
|
||||
BOOT_HYBRID
|
||||
)
|
||||
|
||||
func (m BootMode) String() string {
|
||||
switch m {
|
||||
case BOOT_NONE:
|
||||
return "none"
|
||||
case BOOT_LEGACY:
|
||||
return "legacy"
|
||||
case BOOT_UEFI:
|
||||
return "uefi"
|
||||
case BOOT_HYBRID:
|
||||
return "hybrid"
|
||||
default:
|
||||
panic("invalid boot mode")
|
||||
}
|
||||
}
|
||||
|
||||
// A Distro represents composer's notion of what a given distribution is.
|
||||
type Distro interface {
|
||||
// Returns the name of the distro.
|
||||
Name() string
|
||||
|
||||
// Returns the release version of the distro. This is used in repo
|
||||
// files on the host system and required for the subscription support.
|
||||
Releasever() string
|
||||
|
||||
// Returns the module platform id of the distro. This is used by DNF
|
||||
// for modularity support.
|
||||
ModulePlatformID() string
|
||||
|
||||
// Returns the ostree reference template
|
||||
OSTreeRef() string
|
||||
|
||||
// Returns a sorted list of the names of the architectures this distro
|
||||
// supports.
|
||||
ListArches() []string
|
||||
|
||||
// Returns an object representing the given architecture as support
|
||||
// by this distro.
|
||||
GetArch(arch string) (Arch, error)
|
||||
}
|
||||
|
||||
// An Arch represents a given distribution's support for a given architecture.
|
||||
type Arch interface {
|
||||
// Returns the name of the architecture.
|
||||
Name() string
|
||||
|
||||
// Returns a sorted list of the names of the image types this architecture
|
||||
// supports.
|
||||
ListImageTypes() []string
|
||||
|
||||
// Returns an object representing a given image format for this architecture,
|
||||
// on this distro.
|
||||
GetImageType(imageType string) (ImageType, error)
|
||||
|
||||
// Returns the parent distro
|
||||
Distro() Distro
|
||||
}
|
||||
|
||||
// An ImageType represents a given distribution's support for a given Image Type
|
||||
// for a given architecture.
|
||||
type ImageType interface {
|
||||
// Returns the name of the image type.
|
||||
Name() string
|
||||
|
||||
// Returns the parent architecture
|
||||
Arch() Arch
|
||||
|
||||
// Returns the canonical filename for the image type.
|
||||
Filename() string
|
||||
|
||||
// Retrns the MIME-type for the image type.
|
||||
MIMEType() string
|
||||
|
||||
// Returns the default OSTree ref for the image type.
|
||||
OSTreeRef() string
|
||||
|
||||
// Returns the proper image size for a given output format. If the input size
|
||||
// is 0 the default value for the format will be returned.
|
||||
Size(size uint64) uint64
|
||||
|
||||
// Returns the corresponding partion type ("gpt", "dos") or "" the image type
|
||||
// has no partition table. Only support for RHEL 8.5+
|
||||
PartitionType() string
|
||||
|
||||
// Returns the corresponding boot mode ("legacy", "uefi", "hybrid") or "none"
|
||||
BootMode() BootMode
|
||||
|
||||
// Returns the names of the pipelines that set up the build environment (buildroot).
|
||||
BuildPipelines() []string
|
||||
|
||||
// Returns the names of the pipelines that create the image.
|
||||
PayloadPipelines() []string
|
||||
|
||||
// Returns the package set names safe to install custom packages via custom repositories.
|
||||
PayloadPackageSets() []string
|
||||
|
||||
// Returns named arrays of package set names which should be depsolved in a chain.
|
||||
PackageSetsChains() map[string][]string
|
||||
|
||||
// Returns the names of the stages that will produce the build output.
|
||||
Exports() []string
|
||||
|
||||
// Returns an osbuild manifest, containing the sources and pipeline necessary
|
||||
// to build an image, given output format with all packages and customizations
|
||||
// specified in the given blueprint; it also returns any warnings (e.g.
|
||||
// deprecation notices) generated by the manifest.
|
||||
// The packageSpecSets must be labelled in the same way as the originating PackageSets.
|
||||
Manifest(bp *blueprint.Blueprint, options ImageOptions, repos []rpmmd.RepoConfig, seed int64) (*manifest.Manifest, []string, error)
|
||||
}
|
||||
|
||||
// The ImageOptions specify options for a specific image build
|
||||
type ImageOptions struct {
|
||||
Size uint64
|
||||
OSTree *ostree.ImageOptions
|
||||
Subscription *subscription.ImageOptions
|
||||
Facts *facts.ImageOptions
|
||||
}
|
||||
|
||||
type BasePartitionTableMap map[string]disk.PartitionTable
|
||||
|
||||
// Fallbacks: When a new method is added to an interface to provide to provide
|
||||
// information that isn't available for older implementations, the older
|
||||
// methods should return a fallback/default value by calling the appropriate
|
||||
// function from below.
|
||||
// Example: Exports() simply returns "assembler" for older image type
|
||||
// implementations that didn't produce v1 manifests that have named pipelines.
|
||||
func BuildPipelinesFallback() []string {
|
||||
return []string{"build"}
|
||||
}
|
||||
|
||||
func PayloadPipelinesFallback() []string {
|
||||
return []string{"os", "assembler"}
|
||||
}
|
||||
|
||||
func ExportsFallback() []string {
|
||||
return []string{"assembler"}
|
||||
}
|
||||
|
||||
func PayloadPackageSets() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
|
@ -1,566 +0,0 @@
|
|||
package distro_test
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/container"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro/distro_test_common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distroregistry"
|
||||
"github.com/osbuild/osbuild-composer/internal/ostree"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDistro_Manifest(t *testing.T) {
|
||||
|
||||
distro_test_common.TestDistro_Manifest(
|
||||
t,
|
||||
"../../test/data/manifests/",
|
||||
"*",
|
||||
distroregistry.NewDefault(),
|
||||
false, // This test case does not check for changes in the imageType package sets!
|
||||
"",
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
// Ensure that all package sets defined in the package set chains are defined for the image type
|
||||
func TestImageType_PackageSetsChains(t *testing.T) {
|
||||
distros := distroregistry.NewDefault()
|
||||
for _, distroName := range distros.List() {
|
||||
d := distros.GetDistro(distroName)
|
||||
for _, archName := range d.ListArches() {
|
||||
arch, err := d.GetArch(archName)
|
||||
require.Nil(t, err)
|
||||
for _, imageTypeName := range arch.ListImageTypes() {
|
||||
t.Run(fmt.Sprintf("%s/%s/%s", distroName, archName, imageTypeName), func(t *testing.T) {
|
||||
imageType, err := arch.GetImageType(imageTypeName)
|
||||
require.Nil(t, err)
|
||||
|
||||
// set up bare minimum args for image type
|
||||
var customizations *blueprint.Customizations
|
||||
if imageType.Name() == "edge-simplified-installer" {
|
||||
customizations = &blueprint.Customizations{
|
||||
InstallationDevice: "/dev/null",
|
||||
}
|
||||
}
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: customizations,
|
||||
}
|
||||
options := distro.ImageOptions{
|
||||
OSTree: &ostree.ImageOptions{
|
||||
URL: "https://example.com", // required by some image types
|
||||
},
|
||||
}
|
||||
manifest, _, err := imageType.Manifest(&bp, options, nil, 0)
|
||||
require.NoError(t, err)
|
||||
imagePkgSets := manifest.GetPackageSetChains()
|
||||
for packageSetName := range imageType.PackageSetsChains() {
|
||||
_, ok := imagePkgSets[packageSetName]
|
||||
if !ok {
|
||||
// in the new pipeline generation logic the name of the package
|
||||
// set chains are taken from the pipelines and do not match the
|
||||
// package set names.
|
||||
// TODO: redefine package set chains to make this unneccesary
|
||||
switch packageSetName {
|
||||
case "packages":
|
||||
_, ok = imagePkgSets["os"]
|
||||
if !ok {
|
||||
_, ok = imagePkgSets["ostree-tree"]
|
||||
}
|
||||
}
|
||||
}
|
||||
assert.Truef(t, ok, "package set %q defined in a package set chain is not present in the image package sets", packageSetName)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure all image types report the correct names for their pipelines.
|
||||
// Each image type contains a list of build and payload pipelines. They are
|
||||
// needed for knowing the names of pipelines from the static object without
|
||||
// having access to a manifest, which we need when parsing metadata from build
|
||||
// results.
|
||||
func TestImageTypePipelineNames(t *testing.T) {
|
||||
// types for parsing the opaque manifest with just the fields we care about
|
||||
type rpmStageOptions struct {
|
||||
GPGKeys []string `json:"gpgkeys"`
|
||||
}
|
||||
type stage struct {
|
||||
Type string `json:"type"`
|
||||
Options rpmStageOptions `json:"options"`
|
||||
}
|
||||
type pipeline struct {
|
||||
Name string `json:"name"`
|
||||
Stages []stage `json:"stages"`
|
||||
}
|
||||
type manifest struct {
|
||||
Pipelines []pipeline `json:"pipelines"`
|
||||
}
|
||||
|
||||
assert := assert.New(t)
|
||||
distros := distroregistry.NewDefault()
|
||||
for _, distroName := range distros.List() {
|
||||
d := distros.GetDistro(distroName)
|
||||
for _, archName := range d.ListArches() {
|
||||
arch, err := d.GetArch(archName)
|
||||
assert.Nil(err)
|
||||
for _, imageTypeName := range arch.ListImageTypes() {
|
||||
t.Run(fmt.Sprintf("%s/%s/%s", distroName, archName, imageTypeName), func(t *testing.T) {
|
||||
imageType, err := arch.GetImageType(imageTypeName)
|
||||
assert.Nil(err)
|
||||
|
||||
// set up bare minimum args for image type
|
||||
var customizations *blueprint.Customizations
|
||||
if imageType.Name() == "edge-simplified-installer" {
|
||||
customizations = &blueprint.Customizations{
|
||||
InstallationDevice: "/dev/null",
|
||||
}
|
||||
}
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: customizations,
|
||||
}
|
||||
options := distro.ImageOptions{}
|
||||
// this repo's gpg keys should get included in the os
|
||||
// pipeline's rpm stage
|
||||
repos := []rpmmd.RepoConfig{
|
||||
{
|
||||
Name: "payload",
|
||||
BaseURLs: []string{"http://payload.example.com"},
|
||||
PackageSets: imageType.PayloadPackageSets(),
|
||||
GPGKeys: []string{"payload-gpg-key"},
|
||||
CheckGPG: common.ToPtr(true),
|
||||
},
|
||||
}
|
||||
seed := int64(0)
|
||||
|
||||
// Add ostree options for image types that require them
|
||||
options.OSTree = &ostree.ImageOptions{
|
||||
URL: "https://example.com",
|
||||
}
|
||||
|
||||
// Pipelines that require package sets will fail if none
|
||||
// are defined. OS pipelines require a kernel.
|
||||
// Add kernel and filesystem to every pipeline so that the
|
||||
// manifest creation doesn't fail.
|
||||
allPipelines := append(imageType.BuildPipelines(), imageType.PayloadPipelines()...)
|
||||
minimalPackageSet := []rpmmd.PackageSpec{
|
||||
{Name: "kernel", Checksum: "sha256:a0c936696eb7d5ee3192bf53b9d281cecbb40ca9db520de72cb95817ad92ac72"},
|
||||
{Name: "filesystem", Checksum: "sha256:6b4bf18ba28ccbdd49f2716c9f33c9211155ff703fa6c195c78a07bd160da0eb"},
|
||||
}
|
||||
|
||||
packageSets := make(map[string][]rpmmd.PackageSpec, len(allPipelines))
|
||||
for _, plName := range allPipelines {
|
||||
packageSets[plName] = minimalPackageSet
|
||||
}
|
||||
|
||||
m, _, err := imageType.Manifest(&bp, options, repos, seed)
|
||||
assert.NoError(err)
|
||||
|
||||
containers := make(map[string][]container.Spec, 0)
|
||||
|
||||
ostreeSources := m.GetOSTreeSourceSpecs()
|
||||
commits := make(map[string][]ostree.CommitSpec, len(ostreeSources))
|
||||
for name, commitSources := range ostreeSources {
|
||||
commitSpecs := make([]ostree.CommitSpec, len(commitSources))
|
||||
for idx, commitSource := range commitSources {
|
||||
commitSpecs[idx] = ostree.CommitSpec{
|
||||
Ref: commitSource.Ref,
|
||||
URL: commitSource.URL,
|
||||
Checksum: fmt.Sprintf("%x", sha256.Sum256([]byte(commitSource.URL+commitSource.Ref))),
|
||||
}
|
||||
}
|
||||
commits[name] = commitSpecs
|
||||
}
|
||||
mf, err := m.Serialize(packageSets, containers, commits)
|
||||
assert.NoError(err)
|
||||
pm := new(manifest)
|
||||
err = json.Unmarshal(mf, pm)
|
||||
assert.NoError(err)
|
||||
|
||||
assert.Equal(len(allPipelines), len(pm.Pipelines))
|
||||
for idx := range pm.Pipelines {
|
||||
// manifest pipeline names should be identical to the ones
|
||||
// defined in the image type and in the same order
|
||||
assert.Equal(allPipelines[idx], pm.Pipelines[idx].Name)
|
||||
|
||||
if pm.Pipelines[idx].Name == "os" {
|
||||
rpmStagePresent := false
|
||||
for _, s := range pm.Pipelines[idx].Stages {
|
||||
if s.Type == "org.osbuild.rpm" {
|
||||
rpmStagePresent = true
|
||||
if imageTypeName != "azure-eap7-rhui" {
|
||||
// NOTE (akoutsou): Ideally, at some point we will
|
||||
// have a good way of reading what's supported by
|
||||
// each image type and we can skip or adapt tests
|
||||
// based on this information. For image types with
|
||||
// a preset workload, payload packages are ignored
|
||||
// and dropped and so are the payload
|
||||
// repo gpg keys.
|
||||
assert.Equal(repos[0].GPGKeys, s.Options.GPGKeys)
|
||||
}
|
||||
}
|
||||
}
|
||||
// make sure the gpg keys check was reached
|
||||
assert.True(rpmStagePresent)
|
||||
}
|
||||
}
|
||||
|
||||
// The last pipeline should match the export pipeline.
|
||||
// This might change in the future, but for now, let's make
|
||||
// sure they match.
|
||||
assert.Equal(imageType.Exports()[0], pm.Pipelines[len(pm.Pipelines)-1].Name)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure repositories are assigned to package sets properly.
|
||||
//
|
||||
// Each package set should include all the global repositories as well as any
|
||||
// pipeline/package-set specific repositories.
|
||||
func TestPipelineRepositories(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
type testCase struct {
|
||||
// Repo configs for pipeline generator
|
||||
repos []rpmmd.RepoConfig
|
||||
|
||||
// Expected result: map of pipelines to repo names (we only check names for the test).
|
||||
// Use the pipeline name * for global repos.
|
||||
result map[string][]stringSet
|
||||
}
|
||||
|
||||
testCases := map[string]testCase{
|
||||
"globalonly": { // only global repos: most common scenario
|
||||
repos: []rpmmd.RepoConfig{
|
||||
{
|
||||
Name: "global-1",
|
||||
BaseURLs: []string{"http://global-1.example.com"},
|
||||
},
|
||||
{
|
||||
Name: "global-2",
|
||||
BaseURLs: []string{"http://global-2.example.com"},
|
||||
},
|
||||
},
|
||||
result: map[string][]stringSet{
|
||||
"*": {newStringSet([]string{"global-1", "global-2"})},
|
||||
},
|
||||
},
|
||||
"global+build": { // global repos with build-specific repos: secondary common scenario
|
||||
repos: []rpmmd.RepoConfig{
|
||||
{
|
||||
Name: "global-11",
|
||||
BaseURLs: []string{"http://global-11.example.com"},
|
||||
},
|
||||
{
|
||||
Name: "global-12",
|
||||
BaseURLs: []string{"http://global-12.example.com"},
|
||||
},
|
||||
{
|
||||
Name: "build-1",
|
||||
BaseURLs: []string{"http://build-1.example.com"},
|
||||
PackageSets: []string{"build"},
|
||||
},
|
||||
{
|
||||
Name: "build-2",
|
||||
BaseURLs: []string{"http://build-2.example.com"},
|
||||
PackageSets: []string{"build"},
|
||||
},
|
||||
},
|
||||
result: map[string][]stringSet{
|
||||
"*": {newStringSet([]string{"global-11", "global-12"})},
|
||||
"build": {newStringSet([]string{"build-1", "build-2"})},
|
||||
},
|
||||
},
|
||||
"global+os": { // global repos with os-specific repos
|
||||
repos: []rpmmd.RepoConfig{
|
||||
{
|
||||
Name: "global-21",
|
||||
BaseURLs: []string{"http://global-11.example.com"},
|
||||
},
|
||||
{
|
||||
Name: "global-22",
|
||||
BaseURLs: []string{"http://global-12.example.com"},
|
||||
},
|
||||
{
|
||||
Name: "os-1",
|
||||
BaseURLs: []string{"http://os-1.example.com"},
|
||||
PackageSets: []string{"os"},
|
||||
},
|
||||
{
|
||||
Name: "os-2",
|
||||
BaseURLs: []string{"http://os-2.example.com"},
|
||||
PackageSets: []string{"os"},
|
||||
},
|
||||
},
|
||||
result: map[string][]stringSet{
|
||||
"*": {newStringSet([]string{"global-21", "global-22"})},
|
||||
"os": {newStringSet([]string{"os-1", "os-2"}), newStringSet([]string{"os-1", "os-2"})},
|
||||
},
|
||||
},
|
||||
"global+os+payload": { // global repos with os-specific repos and (user-defined) payload repositories
|
||||
repos: []rpmmd.RepoConfig{
|
||||
{
|
||||
Name: "global-21",
|
||||
BaseURLs: []string{"http://global-11.example.com"},
|
||||
},
|
||||
{
|
||||
Name: "global-22",
|
||||
BaseURLs: []string{"http://global-12.example.com"},
|
||||
},
|
||||
{
|
||||
Name: "os-1",
|
||||
BaseURLs: []string{"http://os-1.example.com"},
|
||||
PackageSets: []string{"os"},
|
||||
},
|
||||
{
|
||||
Name: "os-2",
|
||||
BaseURLs: []string{"http://os-2.example.com"},
|
||||
PackageSets: []string{"os"},
|
||||
},
|
||||
{
|
||||
Name: "payload",
|
||||
BaseURLs: []string{"http://payload.example.com"},
|
||||
// User-defined payload repositories automatically get the "blueprint" key.
|
||||
// This is handled by the APIs.
|
||||
PackageSets: []string{"blueprint"},
|
||||
},
|
||||
},
|
||||
result: map[string][]stringSet{
|
||||
"*": {newStringSet([]string{"global-21", "global-22"})},
|
||||
"os": {
|
||||
// chain with payload repo only in the second set for the blueprint package depsolve
|
||||
newStringSet([]string{"os-1", "os-2"}),
|
||||
newStringSet([]string{"os-1", "os-2", "payload"})},
|
||||
},
|
||||
},
|
||||
"noglobal": { // no global repositories; only pipeline restricted ones (unrealistic but technically valid)
|
||||
repos: []rpmmd.RepoConfig{
|
||||
{
|
||||
Name: "build-1",
|
||||
BaseURLs: []string{"http://build-1.example.com"},
|
||||
PackageSets: []string{"build"},
|
||||
},
|
||||
{
|
||||
Name: "build-2",
|
||||
BaseURLs: []string{"http://build-2.example.com"},
|
||||
PackageSets: []string{"build"},
|
||||
},
|
||||
{
|
||||
Name: "os-1",
|
||||
BaseURLs: []string{"http://os-1.example.com"},
|
||||
PackageSets: []string{"os"},
|
||||
},
|
||||
{
|
||||
Name: "os-2",
|
||||
BaseURLs: []string{"http://os-2.example.com"},
|
||||
PackageSets: []string{"os"},
|
||||
},
|
||||
{
|
||||
Name: "anaconda-1",
|
||||
BaseURLs: []string{"http://anaconda-1.example.com"},
|
||||
PackageSets: []string{"anaconda-tree"},
|
||||
},
|
||||
{
|
||||
Name: "container-1",
|
||||
BaseURLs: []string{"http://container-1.example.com"},
|
||||
PackageSets: []string{"container-tree"},
|
||||
},
|
||||
{
|
||||
Name: "coi-1",
|
||||
BaseURLs: []string{"http://coi-1.example.com"},
|
||||
PackageSets: []string{"coi-tree"},
|
||||
},
|
||||
},
|
||||
result: map[string][]stringSet{
|
||||
"*": nil,
|
||||
"build": {newStringSet([]string{"build-1", "build-2"})},
|
||||
"os": {newStringSet([]string{"os-1", "os-2"}), newStringSet([]string{"os-1", "os-2"})},
|
||||
"anaconda-tree": {newStringSet([]string{"anaconda-1"})},
|
||||
"container-tree": {newStringSet([]string{"container-1"})},
|
||||
"coi-tree": {newStringSet([]string{"coi-1"})},
|
||||
},
|
||||
},
|
||||
"global+unknown": { // package set names that don't match a pipeline are ignored
|
||||
repos: []rpmmd.RepoConfig{
|
||||
{
|
||||
Name: "global-1",
|
||||
BaseURLs: []string{"http://global-1.example.com"},
|
||||
},
|
||||
{
|
||||
Name: "global-2",
|
||||
BaseURLs: []string{"http://global-2.example.com"},
|
||||
},
|
||||
{
|
||||
Name: "custom-1",
|
||||
BaseURLs: []string{"http://custom.example.com"},
|
||||
PackageSets: []string{"notapipeline"},
|
||||
},
|
||||
},
|
||||
result: map[string][]stringSet{
|
||||
"*": {newStringSet([]string{"global-1", "global-2"})},
|
||||
},
|
||||
},
|
||||
"none": { // empty
|
||||
repos: []rpmmd.RepoConfig{},
|
||||
result: map[string][]stringSet{},
|
||||
},
|
||||
}
|
||||
|
||||
distros := distroregistry.NewDefault()
|
||||
for tName, tCase := range testCases {
|
||||
t.Run(tName, func(t *testing.T) {
|
||||
for _, distroName := range distros.List() {
|
||||
d := distros.GetDistro(distroName)
|
||||
for _, archName := range d.ListArches() {
|
||||
arch, err := d.GetArch(archName)
|
||||
require.Nil(err)
|
||||
for _, imageTypeName := range arch.ListImageTypes() {
|
||||
if imageTypeName == "azure-eap7-rhui" {
|
||||
// NOTE (akoutsou): Ideally, at some point we will
|
||||
// have a good way of reading what's supported by
|
||||
// each image type and we can skip or adapt tests
|
||||
// based on this information. For image types with
|
||||
// a preset workload, payload packages are ignored
|
||||
// and dropped.
|
||||
continue
|
||||
}
|
||||
t.Run(fmt.Sprintf("%s/%s/%s", distroName, archName, imageTypeName), func(t *testing.T) {
|
||||
imageType, err := arch.GetImageType(imageTypeName)
|
||||
require.Nil(err)
|
||||
|
||||
// set up bare minimum args for image type
|
||||
var customizations *blueprint.Customizations
|
||||
if imageType.Name() == "edge-simplified-installer" {
|
||||
customizations = &blueprint.Customizations{
|
||||
InstallationDevice: "/dev/null",
|
||||
}
|
||||
}
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: customizations,
|
||||
Packages: []blueprint.Package{
|
||||
{Name: "filesystem"},
|
||||
},
|
||||
}
|
||||
options := distro.ImageOptions{}
|
||||
|
||||
// Add ostree options for image types that require them
|
||||
options.OSTree = &ostree.ImageOptions{
|
||||
URL: "https://example.com",
|
||||
}
|
||||
|
||||
repos := tCase.repos
|
||||
manifest, _, err := imageType.Manifest(&bp, options, repos, 0)
|
||||
require.NoError(err)
|
||||
packageSets := manifest.GetPackageSetChains()
|
||||
|
||||
var globals stringSet
|
||||
if len(tCase.result["*"]) > 0 {
|
||||
globals = tCase.result["*"][0]
|
||||
}
|
||||
for psName, psChain := range packageSets {
|
||||
|
||||
expChain := tCase.result[psName]
|
||||
if len(expChain) > 0 {
|
||||
// if we specified an expected chain it should match the returned.
|
||||
if len(expChain) != len(psChain) {
|
||||
t.Fatalf("expected %d package sets in the %q chain; got %d", len(expChain), psName, len(psChain))
|
||||
}
|
||||
} else {
|
||||
// if we didn't, initialise to empty before merging globals
|
||||
expChain = make([]stringSet, len(psChain))
|
||||
}
|
||||
|
||||
for idx := range expChain {
|
||||
// merge the globals into each expected set
|
||||
expChain[idx] = expChain[idx].Merge(globals)
|
||||
}
|
||||
|
||||
for setIdx, set := range psChain {
|
||||
// collect repositories in the package set
|
||||
repoNamesSet := newStringSet(nil)
|
||||
for _, repo := range set.Repositories {
|
||||
repoNamesSet.Add(repo.Name)
|
||||
}
|
||||
|
||||
// expected set for current package set should be merged with globals
|
||||
expected := expChain[setIdx]
|
||||
if !repoNamesSet.Equals(expected) {
|
||||
t.Errorf("repos for package set %q [idx: %d] %s (distro %q image type %q) do not match expected %s", psName, setIdx, repoNamesSet, d.Name(), imageType.Name(), expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// a very basic implementation of a Set of strings
|
||||
type stringSet struct {
|
||||
elems map[string]bool
|
||||
}
|
||||
|
||||
func newStringSet(init []string) stringSet {
|
||||
s := stringSet{elems: make(map[string]bool)}
|
||||
for _, elem := range init {
|
||||
s.Add(elem)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s stringSet) String() string {
|
||||
elemSlice := make([]string, 0, len(s.elems))
|
||||
for elem := range s.elems {
|
||||
elemSlice = append(elemSlice, elem)
|
||||
}
|
||||
return "{" + strings.Join(elemSlice, ", ") + "}"
|
||||
}
|
||||
|
||||
func (s stringSet) Add(elem string) {
|
||||
s.elems[elem] = true
|
||||
}
|
||||
|
||||
func (s stringSet) Contains(elem string) bool {
|
||||
return s.elems[elem]
|
||||
}
|
||||
|
||||
func (s stringSet) Equals(other stringSet) bool {
|
||||
if len(s.elems) != len(other.elems) {
|
||||
return false
|
||||
}
|
||||
|
||||
for elem := range s.elems {
|
||||
if !other.Contains(elem) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (s stringSet) Merge(other stringSet) stringSet {
|
||||
merged := newStringSet(nil)
|
||||
for elem := range s.elems {
|
||||
merged.Add(elem)
|
||||
}
|
||||
for elem := range other.elems {
|
||||
merged.Add(elem)
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
|
@ -1,623 +0,0 @@
|
|||
package distro_test_common
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/container"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/distroregistry"
|
||||
"github.com/osbuild/osbuild-composer/internal/dnfjson"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/ostree"
|
||||
"github.com/osbuild/osbuild-composer/internal/rhsm/facts"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
const RandomTestSeed = 0
|
||||
|
||||
func TestDistro_Manifest(t *testing.T, pipelinePath string, prefix string, registry *distroregistry.Registry, depsolvePkgSets bool, dnfCacheDir, dnfJsonPath string) {
|
||||
assert := assert.New(t)
|
||||
fileNames, err := filepath.Glob(filepath.Join(pipelinePath, prefix))
|
||||
assert.NoErrorf(err, "Could not read pipelines directory '%s': %v", pipelinePath, err)
|
||||
require.Greaterf(t, len(fileNames), 0, "No pipelines found in %s for %s", pipelinePath, prefix)
|
||||
for _, fileName := range fileNames {
|
||||
type repository struct {
|
||||
BaseURL string `json:"baseurl,omitempty"`
|
||||
Metalink string `json:"metalink,omitempty"`
|
||||
MirrorList string `json:"mirrorlist,omitempty"`
|
||||
GPGKey string `json:"gpgkey,omitempty"`
|
||||
CheckGPG bool `json:"check_gpg,omitempty"`
|
||||
PackageSets []string `json:"package-sets,omitempty"`
|
||||
}
|
||||
type ostreeOptions struct {
|
||||
Ref string `json:"ref"`
|
||||
URL string `json:"url"`
|
||||
Parent string `json:"parent"`
|
||||
RHSM bool `json:"rhsm"`
|
||||
}
|
||||
type composeRequest struct {
|
||||
Distro string `json:"distro"`
|
||||
Arch string `json:"arch"`
|
||||
ImageType string `json:"image-type"`
|
||||
Repositories []repository `json:"repositories"`
|
||||
Blueprint *blueprint.Blueprint `json:"blueprint"`
|
||||
OSTree ostreeOptions `json:"ostree"`
|
||||
}
|
||||
var tt struct {
|
||||
ComposeRequest *composeRequest `json:"compose-request"`
|
||||
PackageSpecSets map[string][]rpmmd.PackageSpec `json:"rpmmd"`
|
||||
Manifest manifest.OSBuildManifest `json:"manifest,omitempty"`
|
||||
Containers map[string][]container.Spec `json:"containers,omitempty"`
|
||||
OSTreeCommits map[string][]ostree.CommitSpec `json:"ostree-commits,omitempty"`
|
||||
}
|
||||
file, err := os.ReadFile(fileName)
|
||||
assert.NoErrorf(err, "Could not read test-case '%s': %v", fileName, err)
|
||||
err = json.Unmarshal([]byte(file), &tt)
|
||||
assert.NoErrorf(err, "Could not parse test-case '%s': %v", fileName, err)
|
||||
if tt.ComposeRequest == nil || tt.ComposeRequest.Blueprint == nil {
|
||||
t.Logf("Skipping '%s'.", fileName)
|
||||
continue
|
||||
}
|
||||
|
||||
repos := make([]rpmmd.RepoConfig, len(tt.ComposeRequest.Repositories))
|
||||
for i, repo := range tt.ComposeRequest.Repositories {
|
||||
var urls []string
|
||||
if repo.BaseURL != "" {
|
||||
urls = []string{repo.BaseURL}
|
||||
}
|
||||
var keys []string
|
||||
if repo.GPGKey != "" {
|
||||
keys = []string{repo.GPGKey}
|
||||
}
|
||||
repos[i] = rpmmd.RepoConfig{
|
||||
Name: fmt.Sprintf("repo-%d", i),
|
||||
BaseURLs: urls,
|
||||
Metalink: repo.Metalink,
|
||||
MirrorList: repo.MirrorList,
|
||||
GPGKeys: keys,
|
||||
CheckGPG: common.ToPtr(repo.CheckGPG),
|
||||
PackageSets: repo.PackageSets,
|
||||
}
|
||||
}
|
||||
t.Run(path.Base(fileName), func(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
d := registry.GetDistro(tt.ComposeRequest.Distro)
|
||||
if d == nil {
|
||||
t.Errorf("unknown distro: %v", tt.ComposeRequest.Distro)
|
||||
return
|
||||
}
|
||||
arch, err := d.GetArch(tt.ComposeRequest.Arch)
|
||||
if err != nil {
|
||||
t.Errorf("unknown arch: %v", tt.ComposeRequest.Arch)
|
||||
return
|
||||
}
|
||||
imageType, err := arch.GetImageType(tt.ComposeRequest.ImageType)
|
||||
if err != nil {
|
||||
t.Errorf("unknown image type: %v", tt.ComposeRequest.ImageType)
|
||||
return
|
||||
}
|
||||
|
||||
ostreeOptions := &ostree.ImageOptions{
|
||||
ImageRef: tt.ComposeRequest.OSTree.Ref,
|
||||
ParentRef: tt.ComposeRequest.OSTree.Parent,
|
||||
URL: tt.ComposeRequest.OSTree.URL,
|
||||
RHSM: tt.ComposeRequest.OSTree.RHSM,
|
||||
}
|
||||
|
||||
options := distro.ImageOptions{
|
||||
Size: imageType.Size(0),
|
||||
OSTree: ostreeOptions,
|
||||
Facts: &facts.ImageOptions{
|
||||
APIType: facts.TEST_APITYPE,
|
||||
},
|
||||
}
|
||||
|
||||
var imgPackageSpecSets map[string][]rpmmd.PackageSpec
|
||||
// depsolve the image's package set to catch changes in the image's default package set.
|
||||
// downside is that this takes long time
|
||||
if depsolvePkgSets {
|
||||
require.NotEmptyf(t, dnfCacheDir, "DNF cache directory path must be provided when chosen to depsolve image package sets")
|
||||
require.NotEmptyf(t, dnfJsonPath, "path to 'dnf-json' must be provided when chosen to depsolve image package sets")
|
||||
imgPackageSpecSets = getImageTypePkgSpecSets(
|
||||
imageType,
|
||||
*tt.ComposeRequest.Blueprint,
|
||||
options,
|
||||
repos,
|
||||
dnfCacheDir,
|
||||
dnfJsonPath,
|
||||
)
|
||||
} else {
|
||||
imgPackageSpecSets = tt.PackageSpecSets
|
||||
}
|
||||
|
||||
manifest, _, err := imageType.Manifest(tt.ComposeRequest.Blueprint, options, repos, RandomTestSeed)
|
||||
if err != nil {
|
||||
t.Errorf("distro.Manifest() error = %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
got, err := manifest.Serialize(imgPackageSpecSets, tt.Containers, tt.OSTreeCommits)
|
||||
|
||||
if (err == nil && tt.Manifest == nil) || (err != nil && tt.Manifest != nil) {
|
||||
t.Errorf("distro.Manifest() error = %v", err)
|
||||
return
|
||||
}
|
||||
if tt.Manifest != nil {
|
||||
var expected, actual interface{}
|
||||
err = json.Unmarshal(tt.Manifest, &expected)
|
||||
require.NoError(t, err)
|
||||
err = json.Unmarshal(got, &actual)
|
||||
require.NoError(t, err)
|
||||
|
||||
diff := cmp.Diff(expected, actual)
|
||||
require.Emptyf(t, diff, "Distro: %s\nArch: %s\nImage type: %s\nTest case file: %s\n", d.Name(), arch.Name(), imageType.Name(), fileName)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func getImageTypePkgSpecSets(imageType distro.ImageType, bp blueprint.Blueprint, options distro.ImageOptions, repos []rpmmd.RepoConfig, cacheDir, dnfJsonPath string) map[string][]rpmmd.PackageSpec {
|
||||
manifest, _, err := imageType.Manifest(&bp, options, repos, 0)
|
||||
if err != nil {
|
||||
panic("Could not generate manifest for package sets: " + err.Error())
|
||||
}
|
||||
imgPackageSets := manifest.GetPackageSetChains()
|
||||
|
||||
solver := dnfjson.NewSolver(imageType.Arch().Distro().ModulePlatformID(),
|
||||
imageType.Arch().Distro().Releasever(),
|
||||
imageType.Arch().Name(),
|
||||
imageType.Arch().Distro().Name(),
|
||||
cacheDir)
|
||||
solver.SetDNFJSONPath(dnfJsonPath)
|
||||
depsolvedSets := make(map[string][]rpmmd.PackageSpec)
|
||||
for name, packages := range imgPackageSets {
|
||||
res, err := solver.Depsolve(packages)
|
||||
if err != nil {
|
||||
panic("Could not depsolve: " + err.Error())
|
||||
}
|
||||
depsolvedSets[name] = res
|
||||
}
|
||||
|
||||
return depsolvedSets
|
||||
}
|
||||
|
||||
func isOSTree(imgType distro.ImageType) bool {
|
||||
return imgType.OSTreeRef() != ""
|
||||
}
|
||||
|
||||
var knownKernels = []string{"kernel", "kernel-debug", "kernel-rt"}
|
||||
|
||||
// Returns the number of known kernels in the package list
|
||||
func kernelCount(imgType distro.ImageType, bp blueprint.Blueprint) int {
|
||||
ostreeOptions := &ostree.ImageOptions{
|
||||
URL: "https://example.com", // required by some image types
|
||||
}
|
||||
manifest, _, err := imgType.Manifest(&bp, distro.ImageOptions{OSTree: ostreeOptions}, nil, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sets := manifest.GetPackageSetChains()
|
||||
|
||||
// Use a map to count unique kernels in a package set. If the same kernel
|
||||
// name appears twice, it will only be installed once, so we only count it
|
||||
// once.
|
||||
kernels := make(map[string]bool)
|
||||
for _, name := range []string{
|
||||
// payload package set names
|
||||
"os", "ostree-tree", "anaconda-tree",
|
||||
"packages", "installer",
|
||||
} {
|
||||
for _, pset := range sets[name] {
|
||||
for _, pkg := range pset.Include {
|
||||
for _, kernel := range knownKernels {
|
||||
if kernel == pkg {
|
||||
kernels[kernel] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(kernels) > 0 {
|
||||
// BUG: some RHEL image types contain both 'packages'
|
||||
// and 'installer' even though only 'installer' is used
|
||||
// this counts the kernel package twice. None of these
|
||||
// sets should appear more than once, so return the count
|
||||
// for the first package set that has at least one kernel.
|
||||
return len(kernels)
|
||||
}
|
||||
}
|
||||
}
|
||||
return len(kernels)
|
||||
}
|
||||
|
||||
func TestDistro_KernelOption(t *testing.T, d distro.Distro) {
|
||||
skipList := map[string]bool{
|
||||
// Ostree installers and raw images download a payload to embed or
|
||||
// deploy. The kernel is part of the payload so it doesn't appear in
|
||||
// the image type's package lists.
|
||||
"iot-installer": true,
|
||||
"edge-installer": true,
|
||||
"edge-simplified-installer": true,
|
||||
"iot-raw-image": true,
|
||||
"edge-raw-image": true,
|
||||
"edge-ami": true,
|
||||
|
||||
// the tar image type is a minimal image type which is not expected to
|
||||
// be usable without a blueprint (see commit 83a63aaf172f556f6176e6099ffaa2b5357b58f5).
|
||||
"tar": true,
|
||||
|
||||
// containers don't have kernels
|
||||
"container": true,
|
||||
|
||||
// image installer on Fedora doesn't support kernel customizations
|
||||
// on RHEL we support kernel name
|
||||
// TODO: Remove when we unify the allowed options
|
||||
"image-installer": true,
|
||||
"live-installer": true,
|
||||
}
|
||||
|
||||
{ // empty blueprint: all image types should just have the default kernel
|
||||
for _, archName := range d.ListArches() {
|
||||
arch, err := d.GetArch(archName)
|
||||
assert.NoError(t, err)
|
||||
for _, typeName := range arch.ListImageTypes() {
|
||||
if skipList[typeName] {
|
||||
continue
|
||||
}
|
||||
imgType, err := arch.GetImageType(typeName)
|
||||
assert.NoError(t, err)
|
||||
nk := kernelCount(imgType, blueprint.Blueprint{})
|
||||
|
||||
if nk != 1 {
|
||||
assert.Fail(t, fmt.Sprintf("%s Kernel count", d.Name()),
|
||||
"Image type %s (arch %s) specifies %d Kernel packages", typeName, archName, nk)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{ // kernel in blueprint: the specified kernel replaces the default
|
||||
for _, kernelName := range []string{"kernel", "kernel-debug"} {
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Kernel: &blueprint.KernelCustomization{
|
||||
Name: kernelName,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range d.ListArches() {
|
||||
arch, err := d.GetArch(archName)
|
||||
assert.NoError(t, err)
|
||||
for _, typeName := range arch.ListImageTypes() {
|
||||
if typeName != "image-installer" {
|
||||
continue
|
||||
}
|
||||
if typeName != "live-installer" {
|
||||
continue
|
||||
}
|
||||
if skipList[typeName] {
|
||||
continue
|
||||
}
|
||||
imgType, err := arch.GetImageType(typeName)
|
||||
assert.NoError(t, err)
|
||||
nk := kernelCount(imgType, bp)
|
||||
|
||||
// ostree image types should have only one kernel
|
||||
// other image types should have at least 1
|
||||
if nk < 1 || (nk != 1 && isOSTree(imgType)) {
|
||||
assert.Fail(t, fmt.Sprintf("%s Kernel count", d.Name()),
|
||||
"Image type %s (arch %s) specifies %d Kernel packages", typeName, archName, nk)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_OSTreeOptions(t *testing.T, d distro.Distro) {
|
||||
// test that ostree parameters are properly resolved by image functions that should support them
|
||||
typesWithParent := map[string]bool{ // image types that support specifying a parent commit
|
||||
"edge-commit": true,
|
||||
"edge-container": true,
|
||||
"iot-commit": true,
|
||||
"iot-container": true,
|
||||
}
|
||||
|
||||
typesWithPayload := map[string]bool{
|
||||
"edge-ami": true,
|
||||
"edge-installer": true,
|
||||
"edge-raw-image": true,
|
||||
"edge-simplified-installer": true,
|
||||
"iot-ami": true,
|
||||
"iot-installer": true,
|
||||
"iot-raw-image": true,
|
||||
"iot-simplified-installer": true,
|
||||
}
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
{ // empty options: payload ref should equal default
|
||||
for _, archName := range d.ListArches() {
|
||||
arch, err := d.GetArch(archName)
|
||||
assert.NoError(err)
|
||||
for _, typeName := range arch.ListImageTypes() {
|
||||
bp := &blueprint.Blueprint{}
|
||||
if strings.HasSuffix(typeName, "simplified-installer") {
|
||||
// simplified installers require installation device
|
||||
bp = &blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
InstallationDevice: "/dev/sda42",
|
||||
},
|
||||
}
|
||||
}
|
||||
imgType, err := arch.GetImageType(typeName)
|
||||
assert.NoError(err)
|
||||
|
||||
ostreeOptions := ostree.ImageOptions{}
|
||||
if typesWithPayload[typeName] {
|
||||
// payload types require URL
|
||||
ostreeOptions.URL = "https://example.com/repo"
|
||||
}
|
||||
options := distro.ImageOptions{OSTree: &ostreeOptions}
|
||||
|
||||
m, _, err := imgType.Manifest(bp, options, nil, 0)
|
||||
assert.NoError(err)
|
||||
|
||||
nrefs := 0
|
||||
// If a manifest returns an ostree source spec, the ref should
|
||||
// match the default.
|
||||
for _, commits := range m.GetOSTreeSourceSpecs() {
|
||||
for _, commit := range commits {
|
||||
assert.Equal(options.OSTree.URL, commit.URL, "url does not match expected for image type %q\n", typeName)
|
||||
assert.Equal(imgType.OSTreeRef(), commit.Ref, "ref does not match expected for image type %q\n", typeName)
|
||||
nrefs++
|
||||
}
|
||||
}
|
||||
nexpected := 0
|
||||
if typesWithPayload[typeName] {
|
||||
// image types with payload should return a ref
|
||||
nexpected = 1
|
||||
}
|
||||
assert.Equal(nexpected, nrefs, "incorrect ref count for image type %q\n", typeName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{ // ImageRef set: should be returned as payload ref - no parent for commits and containers
|
||||
for _, archName := range d.ListArches() {
|
||||
arch, err := d.GetArch(archName)
|
||||
assert.NoError(err)
|
||||
for _, typeName := range arch.ListImageTypes() {
|
||||
bp := &blueprint.Blueprint{}
|
||||
if strings.HasSuffix(typeName, "simplified-installer") {
|
||||
// simplified installers require installation device
|
||||
bp = &blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
InstallationDevice: "/dev/sda42",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
imgType, err := arch.GetImageType(typeName)
|
||||
assert.NoError(err)
|
||||
|
||||
ostreeOptions := ostree.ImageOptions{
|
||||
ImageRef: "test/x86_64/01",
|
||||
}
|
||||
if typesWithPayload[typeName] {
|
||||
// payload types require URL
|
||||
ostreeOptions.URL = "https://example.com/repo"
|
||||
}
|
||||
options := distro.ImageOptions{OSTree: &ostreeOptions}
|
||||
m, _, err := imgType.Manifest(bp, options, nil, 0)
|
||||
assert.NoError(err)
|
||||
|
||||
nrefs := 0
|
||||
// if a manifest returns an ostree source spec, the ref should
|
||||
// match the default
|
||||
for _, commits := range m.GetOSTreeSourceSpecs() {
|
||||
for _, commit := range commits {
|
||||
assert.Equal(options.OSTree.URL, commit.URL, "url does not match expected for image type %q\n", typeName)
|
||||
assert.Equal(options.OSTree.ImageRef, commit.Ref, "ref does not match expected for image type %q\n", typeName)
|
||||
nrefs++
|
||||
}
|
||||
}
|
||||
nexpected := 0
|
||||
if typesWithPayload[typeName] {
|
||||
// image types with payload should return a ref
|
||||
nexpected = 1
|
||||
}
|
||||
assert.Equal(nexpected, nrefs, "incorrect ref count for image type %q\n", typeName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{ // URL always specified: should add a parent to image types that support it and the ref should match the option
|
||||
for _, archName := range d.ListArches() {
|
||||
arch, err := d.GetArch(archName)
|
||||
assert.NoError(err)
|
||||
for _, typeName := range arch.ListImageTypes() {
|
||||
bp := &blueprint.Blueprint{}
|
||||
if strings.HasSuffix(typeName, "simplified-installer") {
|
||||
// simplified installers require installation device
|
||||
bp = &blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
InstallationDevice: "/dev/sda42",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
imgType, err := arch.GetImageType(typeName)
|
||||
assert.NoError(err)
|
||||
|
||||
ostreeOptions := ostree.ImageOptions{
|
||||
ImageRef: "test/x86_64/01",
|
||||
URL: "https://example.com/repo",
|
||||
}
|
||||
options := distro.ImageOptions{OSTree: &ostreeOptions}
|
||||
m, _, err := imgType.Manifest(bp, options, nil, 0)
|
||||
assert.NoError(err)
|
||||
|
||||
nrefs := 0
|
||||
for _, commits := range m.GetOSTreeSourceSpecs() {
|
||||
for _, commit := range commits {
|
||||
assert.Equal(options.OSTree.URL, commit.URL, "url does not match expected for image type %q\n", typeName)
|
||||
assert.Equal(options.OSTree.ImageRef, commit.Ref, "ref does not match expected for image type %q\n", typeName)
|
||||
nrefs++
|
||||
}
|
||||
}
|
||||
nexpected := 0
|
||||
if typesWithPayload[typeName] || typesWithParent[typeName] {
|
||||
// image types with payload or parent should return a ref
|
||||
nexpected = 1
|
||||
}
|
||||
assert.Equal(nexpected, nrefs, "incorrect ref count for image type %q\n", typeName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{ // URL and parent ref always specified: payload ref should be default - parent ref should match option
|
||||
for _, archName := range d.ListArches() {
|
||||
arch, err := d.GetArch(archName)
|
||||
assert.NoError(err)
|
||||
for _, typeName := range arch.ListImageTypes() {
|
||||
bp := &blueprint.Blueprint{}
|
||||
if strings.HasSuffix(typeName, "simplified-installer") {
|
||||
// simplified installers require installation device
|
||||
bp = &blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
InstallationDevice: "/dev/sda42",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
imgType, err := arch.GetImageType(typeName)
|
||||
assert.NoError(err)
|
||||
|
||||
ostreeOptions := ostree.ImageOptions{
|
||||
ParentRef: "test/x86_64/01",
|
||||
URL: "https://example.com/repo",
|
||||
}
|
||||
options := distro.ImageOptions{OSTree: &ostreeOptions}
|
||||
m, _, err := imgType.Manifest(bp, options, nil, 0)
|
||||
assert.NoError(err)
|
||||
|
||||
nrefs := 0
|
||||
for _, commits := range m.GetOSTreeSourceSpecs() {
|
||||
for _, commit := range commits {
|
||||
assert.Equal(options.OSTree.URL, commit.URL, "url does not match expected for image type %q\n", typeName)
|
||||
if typesWithPayload[typeName] {
|
||||
// payload ref should fall back to default
|
||||
assert.Equal(imgType.OSTreeRef(), commit.Ref, "ref does not match expected for image type %q\n", typeName)
|
||||
} else if typesWithParent[typeName] {
|
||||
// parent ref should match option
|
||||
assert.Equal(options.OSTree.ParentRef, commit.Ref, "ref does not match expected for image type %q\n", typeName)
|
||||
} else {
|
||||
// image type requires ostree commit but isn't specified: this shouldn't happen
|
||||
panic(fmt.Sprintf("image type %q requires ostree commit but is not covered by test", typeName))
|
||||
}
|
||||
nrefs++
|
||||
}
|
||||
}
|
||||
nexpected := 0
|
||||
if typesWithPayload[typeName] || typesWithParent[typeName] {
|
||||
// image types with payload or parent should return a ref
|
||||
nexpected = 1
|
||||
}
|
||||
assert.Equal(nexpected, nrefs, "incorrect ref count for image type %q\n", typeName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{ // All options set: all refs should match the corresponding option
|
||||
for _, archName := range d.ListArches() {
|
||||
arch, err := d.GetArch(archName)
|
||||
assert.NoError(err)
|
||||
for _, typeName := range arch.ListImageTypes() {
|
||||
bp := &blueprint.Blueprint{}
|
||||
if strings.HasSuffix(typeName, "simplified-installer") {
|
||||
// simplified installers require installation device
|
||||
bp = &blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
InstallationDevice: "/dev/sda42",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
imgType, err := arch.GetImageType(typeName)
|
||||
assert.NoError(err)
|
||||
|
||||
ostreeOptions := ostree.ImageOptions{
|
||||
ImageRef: "test/x86_64/01",
|
||||
ParentRef: "test/x86_64/02",
|
||||
URL: "https://example.com/repo",
|
||||
}
|
||||
options := distro.ImageOptions{OSTree: &ostreeOptions}
|
||||
m, _, err := imgType.Manifest(bp, options, nil, 0)
|
||||
assert.NoError(err)
|
||||
|
||||
nrefs := 0
|
||||
for _, commits := range m.GetOSTreeSourceSpecs() {
|
||||
for _, commit := range commits {
|
||||
assert.Equal(options.OSTree.URL, commit.URL, "url does not match expected for image type %q\n", typeName)
|
||||
if typesWithPayload[typeName] {
|
||||
// payload ref should match image ref
|
||||
assert.Equal(options.OSTree.ImageRef, commit.Ref, "ref does not match expected for image type %q\n", typeName)
|
||||
} else if typesWithParent[typeName] {
|
||||
// parent ref should match option
|
||||
assert.Equal(options.OSTree.ParentRef, commit.Ref, "ref does not match expected for image type %q\n", typeName)
|
||||
} else {
|
||||
// image type requires ostree commit but isn't specified: this shouldn't happen
|
||||
panic(fmt.Sprintf("image type %q requires ostree commit but is not covered by test", typeName))
|
||||
}
|
||||
nrefs++
|
||||
}
|
||||
}
|
||||
nexpected := 0
|
||||
if typesWithPayload[typeName] || typesWithParent[typeName] {
|
||||
// image types with payload or parent should return a ref
|
||||
nexpected = 1
|
||||
}
|
||||
assert.Equal(nexpected, nrefs, "incorrect ref count for image type %q\n", typeName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{ // Parent set without URL: always causes error
|
||||
for _, archName := range d.ListArches() {
|
||||
arch, err := d.GetArch(archName)
|
||||
assert.NoError(err)
|
||||
for _, typeName := range arch.ListImageTypes() {
|
||||
bp := &blueprint.Blueprint{}
|
||||
if strings.HasSuffix(typeName, "simplified-installer") {
|
||||
// simplified installers require installation device
|
||||
bp = &blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
InstallationDevice: "/dev/sda42",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
imgType, err := arch.GetImageType(typeName)
|
||||
assert.NoError(err)
|
||||
|
||||
ostreeOptions := ostree.ImageOptions{
|
||||
ParentRef: "test/x86_64/02",
|
||||
}
|
||||
options := distro.ImageOptions{OSTree: &ostreeOptions}
|
||||
_, _, err = imgType.Manifest(bp, options, nil, 0)
|
||||
assert.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,722 +0,0 @@
|
|||
package fedora
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/environment"
|
||||
"github.com/osbuild/osbuild-composer/internal/oscap"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/runner"
|
||||
)
|
||||
|
||||
const (
|
||||
// package set names
|
||||
|
||||
// main/common os image package set name
|
||||
osPkgsKey = "os"
|
||||
|
||||
// container package set name
|
||||
containerPkgsKey = "container"
|
||||
|
||||
// installer package set name
|
||||
installerPkgsKey = "installer"
|
||||
|
||||
// blueprint package set name
|
||||
blueprintPkgsKey = "blueprint"
|
||||
|
||||
//Kernel options for ami, qcow2, openstack, vhd and vmdk types
|
||||
defaultKernelOptions = "ro no_timer_check console=ttyS0,115200n8 biosdevname=0 net.ifnames=0"
|
||||
)
|
||||
|
||||
var (
|
||||
oscapProfileAllowList = []oscap.Profile{
|
||||
oscap.Ospp,
|
||||
oscap.PciDss,
|
||||
oscap.Standard,
|
||||
}
|
||||
|
||||
// Services
|
||||
iotServices = []string{
|
||||
"NetworkManager.service",
|
||||
"firewalld.service",
|
||||
"rngd.service",
|
||||
"sshd.service",
|
||||
"zezere_ignition.timer",
|
||||
"zezere_ignition_banner.service",
|
||||
"greenboot-grub2-set-counter",
|
||||
"greenboot-grub2-set-success",
|
||||
"greenboot-healthcheck",
|
||||
"greenboot-rpm-ostree-grub2-check-fallback",
|
||||
"greenboot-status",
|
||||
"greenboot-task-runner",
|
||||
"redboot-auto-reboot",
|
||||
"redboot-task-runner",
|
||||
"parsec",
|
||||
"dbus-parsec",
|
||||
}
|
||||
|
||||
// Image Definitions
|
||||
imageInstallerImgType = imageType{
|
||||
name: "image-installer",
|
||||
nameAliases: []string{"fedora-image-installer"},
|
||||
filename: "installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: minimalrpmPackageSet,
|
||||
installerPkgsKey: imageInstallerPackageSet,
|
||||
},
|
||||
bootable: true,
|
||||
bootISO: true,
|
||||
rpmOstree: false,
|
||||
image: imageInstallerImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"anaconda-tree", "rootfs-image", "efiboot-tree", "os", "bootiso-tree", "bootiso"},
|
||||
exports: []string{"bootiso"},
|
||||
}
|
||||
|
||||
liveInstallerImgType = imageType{
|
||||
name: "live-installer",
|
||||
nameAliases: []string{},
|
||||
filename: "live-installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
installerPkgsKey: liveInstallerPackageSet,
|
||||
},
|
||||
bootable: true,
|
||||
bootISO: true,
|
||||
rpmOstree: false,
|
||||
image: liveInstallerImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"anaconda-tree", "rootfs-image", "efiboot-tree", "bootiso-tree", "bootiso"},
|
||||
exports: []string{"bootiso"},
|
||||
}
|
||||
|
||||
iotCommitImgType = imageType{
|
||||
name: "iot-commit",
|
||||
nameAliases: []string{"fedora-iot-commit"},
|
||||
filename: "commit.tar",
|
||||
mimeType: "application/x-tar",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: iotCommitPackageSet,
|
||||
},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
EnabledServices: iotServices,
|
||||
},
|
||||
rpmOstree: true,
|
||||
image: iotCommitImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "ostree-commit", "commit-archive"},
|
||||
exports: []string{"commit-archive"},
|
||||
}
|
||||
|
||||
iotOCIImgType = imageType{
|
||||
name: "iot-container",
|
||||
nameAliases: []string{"fedora-iot-container"},
|
||||
filename: "container.tar",
|
||||
mimeType: "application/x-tar",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: iotCommitPackageSet,
|
||||
containerPkgsKey: func(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{}
|
||||
},
|
||||
},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
EnabledServices: iotServices,
|
||||
},
|
||||
rpmOstree: true,
|
||||
bootISO: false,
|
||||
image: iotContainerImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "ostree-commit", "container-tree", "container"},
|
||||
exports: []string{"container"},
|
||||
}
|
||||
|
||||
iotInstallerImgType = imageType{
|
||||
name: "iot-installer",
|
||||
nameAliases: []string{"fedora-iot-installer"},
|
||||
filename: "installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
installerPkgsKey: iotInstallerPackageSet,
|
||||
},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
EnabledServices: iotServices,
|
||||
},
|
||||
rpmOstree: true,
|
||||
bootISO: true,
|
||||
image: iotInstallerImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"anaconda-tree", "rootfs-image", "efiboot-tree", "bootiso-tree", "bootiso"},
|
||||
exports: []string{"bootiso"},
|
||||
}
|
||||
|
||||
iotRawImgType = imageType{
|
||||
name: "iot-raw-image",
|
||||
nameAliases: []string{"fedora-iot-raw-image"},
|
||||
filename: "image.raw.xz",
|
||||
compression: "xz",
|
||||
mimeType: "application/xz",
|
||||
packageSets: map[string]packageSetFunc{},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
},
|
||||
defaultSize: 4 * common.GibiByte,
|
||||
rpmOstree: true,
|
||||
bootable: true,
|
||||
image: iotRawImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"ostree-deployment", "image", "xz"},
|
||||
exports: []string{"xz"},
|
||||
basePartitionTables: iotBasePartitionTables,
|
||||
|
||||
// Passing an empty map into the required partition sizes disables the
|
||||
// default partition sizes normally set so our `basePartitionTables` can
|
||||
// override them (and make them smaller, in this case).
|
||||
requiredPartitionSizes: map[string]uint64{},
|
||||
}
|
||||
|
||||
qcow2ImgType = imageType{
|
||||
name: "qcow2",
|
||||
filename: "disk.qcow2",
|
||||
mimeType: "application/x-qemu-disk",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: qcow2CommonPackageSet,
|
||||
},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
EnabledServices: []string{
|
||||
"cloud-init.service",
|
||||
"cloud-config.service",
|
||||
"cloud-final.service",
|
||||
"cloud-init-local.service",
|
||||
},
|
||||
},
|
||||
kernelOptions: defaultKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 5 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "qcow2"},
|
||||
exports: []string{"qcow2"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
|
||||
vhdImgType = imageType{
|
||||
name: "vhd",
|
||||
filename: "disk.vhd",
|
||||
mimeType: "application/x-vhd",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: vhdCommonPackageSet,
|
||||
},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
EnabledServices: []string{
|
||||
"sshd",
|
||||
},
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
DisabledServices: []string{
|
||||
"proc-sys-fs-binfmt_misc.mount",
|
||||
"loadmodules.service",
|
||||
},
|
||||
},
|
||||
kernelOptions: defaultKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 2 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "vpc"},
|
||||
exports: []string{"vpc"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
environment: &environment.Azure{},
|
||||
}
|
||||
|
||||
vmdkDefaultImageConfig = &distro.ImageConfig{
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
EnabledServices: []string{
|
||||
"cloud-init.service",
|
||||
"cloud-config.service",
|
||||
"cloud-final.service",
|
||||
"cloud-init-local.service",
|
||||
},
|
||||
}
|
||||
|
||||
vmdkImgType = imageType{
|
||||
name: "vmdk",
|
||||
filename: "disk.vmdk",
|
||||
mimeType: "application/x-vmdk",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: vmdkCommonPackageSet,
|
||||
},
|
||||
defaultImageConfig: vmdkDefaultImageConfig,
|
||||
kernelOptions: defaultKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 2 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "vmdk"},
|
||||
exports: []string{"vmdk"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
|
||||
ovaImgType = imageType{
|
||||
name: "ova",
|
||||
filename: "image.ova",
|
||||
mimeType: "application/ovf",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: vmdkCommonPackageSet,
|
||||
},
|
||||
defaultImageConfig: vmdkDefaultImageConfig,
|
||||
kernelOptions: defaultKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 2 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "vmdk", "ovf", "archive"},
|
||||
exports: []string{"archive"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
|
||||
containerImgType = imageType{
|
||||
name: "container",
|
||||
filename: "container.tar",
|
||||
mimeType: "application/x-tar",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: containerPackageSet,
|
||||
},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
NoSElinux: common.ToPtr(true),
|
||||
ExcludeDocs: common.ToPtr(true),
|
||||
Locale: common.ToPtr("C.UTF-8"),
|
||||
Timezone: common.ToPtr("Etc/UTC"),
|
||||
},
|
||||
image: containerImage,
|
||||
bootable: false,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "container"},
|
||||
exports: []string{"container"},
|
||||
}
|
||||
|
||||
minimalrawImgType = imageType{
|
||||
name: "minimal-raw",
|
||||
filename: "raw.img",
|
||||
mimeType: "application/disk",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: minimalrpmPackageSet,
|
||||
},
|
||||
rpmOstree: false,
|
||||
kernelOptions: defaultKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 2 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image"},
|
||||
exports: []string{"image"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
)
|
||||
|
||||
type distribution struct {
|
||||
name string
|
||||
product string
|
||||
osVersion string
|
||||
releaseVersion string
|
||||
modulePlatformID string
|
||||
ostreeRefTmpl string
|
||||
isolabelTmpl string
|
||||
runner runner.Runner
|
||||
arches map[string]distro.Arch
|
||||
defaultImageConfig *distro.ImageConfig
|
||||
}
|
||||
|
||||
// Fedora based OS image configuration defaults
|
||||
var defaultDistroImageConfig = &distro.ImageConfig{
|
||||
Timezone: common.ToPtr("UTC"),
|
||||
Locale: common.ToPtr("en_US"),
|
||||
}
|
||||
|
||||
func getDistro(version int) distribution {
|
||||
return distribution{
|
||||
name: fmt.Sprintf("fedora-%d", version),
|
||||
product: "Fedora",
|
||||
osVersion: strconv.Itoa(version),
|
||||
releaseVersion: strconv.Itoa(version),
|
||||
modulePlatformID: fmt.Sprintf("platform:f%d", version),
|
||||
ostreeRefTmpl: fmt.Sprintf("fedora/%d/%%s/iot", version),
|
||||
isolabelTmpl: fmt.Sprintf("Fedora-%d-BaseOS-%%s", version),
|
||||
runner: &runner.Fedora{Version: uint64(version)},
|
||||
defaultImageConfig: defaultDistroImageConfig,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *distribution) Name() string {
|
||||
return d.name
|
||||
}
|
||||
|
||||
func (d *distribution) Releasever() string {
|
||||
return d.releaseVersion
|
||||
}
|
||||
|
||||
func (d *distribution) ModulePlatformID() string {
|
||||
return d.modulePlatformID
|
||||
}
|
||||
|
||||
func (d *distribution) OSTreeRef() string {
|
||||
return d.ostreeRefTmpl
|
||||
}
|
||||
|
||||
func (d *distribution) ListArches() []string {
|
||||
archNames := make([]string, 0, len(d.arches))
|
||||
for name := range d.arches {
|
||||
archNames = append(archNames, name)
|
||||
}
|
||||
sort.Strings(archNames)
|
||||
return archNames
|
||||
}
|
||||
|
||||
func (d *distribution) GetArch(name string) (distro.Arch, error) {
|
||||
arch, exists := d.arches[name]
|
||||
if !exists {
|
||||
return nil, errors.New("invalid architecture: " + name)
|
||||
}
|
||||
return arch, nil
|
||||
}
|
||||
|
||||
func (d *distribution) addArches(arches ...architecture) {
|
||||
if d.arches == nil {
|
||||
d.arches = map[string]distro.Arch{}
|
||||
}
|
||||
|
||||
// Do not make copies of architectures, as opposed to image types,
|
||||
// because architecture definitions are not used by more than a single
|
||||
// distro definition.
|
||||
for idx := range arches {
|
||||
d.arches[arches[idx].name] = &arches[idx]
|
||||
}
|
||||
}
|
||||
|
||||
func (d *distribution) getDefaultImageConfig() *distro.ImageConfig {
|
||||
return d.defaultImageConfig
|
||||
}
|
||||
|
||||
type architecture struct {
|
||||
distro *distribution
|
||||
name string
|
||||
imageTypes map[string]distro.ImageType
|
||||
imageTypeAliases map[string]string
|
||||
}
|
||||
|
||||
func (a *architecture) Name() string {
|
||||
return a.name
|
||||
}
|
||||
|
||||
func (a *architecture) ListImageTypes() []string {
|
||||
itNames := make([]string, 0, len(a.imageTypes))
|
||||
for name := range a.imageTypes {
|
||||
itNames = append(itNames, name)
|
||||
}
|
||||
sort.Strings(itNames)
|
||||
return itNames
|
||||
}
|
||||
|
||||
func (a *architecture) GetImageType(name string) (distro.ImageType, error) {
|
||||
t, exists := a.imageTypes[name]
|
||||
if !exists {
|
||||
aliasForName, exists := a.imageTypeAliases[name]
|
||||
if !exists {
|
||||
return nil, errors.New("invalid image type: " + name)
|
||||
}
|
||||
t, exists = a.imageTypes[aliasForName]
|
||||
if !exists {
|
||||
panic(fmt.Sprintf("image type '%s' is an alias to a non-existing image type '%s'", name, aliasForName))
|
||||
}
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (a *architecture) addImageTypes(platform platform.Platform, imageTypes ...imageType) {
|
||||
if a.imageTypes == nil {
|
||||
a.imageTypes = map[string]distro.ImageType{}
|
||||
}
|
||||
for idx := range imageTypes {
|
||||
it := imageTypes[idx]
|
||||
it.arch = a
|
||||
it.platform = platform
|
||||
a.imageTypes[it.name] = &it
|
||||
for _, alias := range it.nameAliases {
|
||||
if a.imageTypeAliases == nil {
|
||||
a.imageTypeAliases = map[string]string{}
|
||||
}
|
||||
if existingAliasFor, exists := a.imageTypeAliases[alias]; exists {
|
||||
panic(fmt.Sprintf("image type alias '%s' for '%s' is already defined for another image type '%s'", alias, it.name, existingAliasFor))
|
||||
}
|
||||
a.imageTypeAliases[alias] = it.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *architecture) Distro() distro.Distro {
|
||||
return a.distro
|
||||
}
|
||||
|
||||
// New creates a new distro object, defining the supported architectures and image types
|
||||
func NewF37() distro.Distro {
|
||||
return newDistro(37)
|
||||
}
|
||||
func NewF38() distro.Distro {
|
||||
return newDistro(38)
|
||||
}
|
||||
func NewF39() distro.Distro {
|
||||
return newDistro(39)
|
||||
}
|
||||
|
||||
func newDistro(version int) distro.Distro {
|
||||
rd := getDistro(version)
|
||||
|
||||
// Architecture definitions
|
||||
x86_64 := architecture{
|
||||
name: platform.ARCH_X86_64.String(),
|
||||
distro: &rd,
|
||||
}
|
||||
|
||||
aarch64 := architecture{
|
||||
name: platform.ARCH_AARCH64.String(),
|
||||
distro: &rd,
|
||||
}
|
||||
|
||||
ociImgType := qcow2ImgType
|
||||
ociImgType.name = "oci"
|
||||
|
||||
amiImgType := qcow2ImgType
|
||||
amiImgType.name = "ami"
|
||||
amiImgType.filename = "image.raw"
|
||||
amiImgType.mimeType = "application/octet-stream"
|
||||
amiImgType.payloadPipelines = []string{"os", "image"}
|
||||
amiImgType.exports = []string{"image"}
|
||||
amiImgType.environment = &environment.EC2{}
|
||||
|
||||
openstackImgType := qcow2ImgType
|
||||
openstackImgType.name = "openstack"
|
||||
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: "fedora",
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_QCOW2,
|
||||
QCOW2Compat: "1.1",
|
||||
},
|
||||
},
|
||||
qcow2ImgType,
|
||||
ociImgType,
|
||||
)
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: "fedora",
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_QCOW2,
|
||||
},
|
||||
},
|
||||
openstackImgType,
|
||||
)
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: "fedora",
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_VHD,
|
||||
},
|
||||
},
|
||||
vhdImgType,
|
||||
)
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: "fedora",
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_VMDK,
|
||||
},
|
||||
},
|
||||
vmdkImgType,
|
||||
)
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: "fedora",
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_OVA,
|
||||
},
|
||||
},
|
||||
ovaImgType,
|
||||
)
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: "fedora",
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_RAW,
|
||||
},
|
||||
},
|
||||
amiImgType,
|
||||
)
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{},
|
||||
containerImgType,
|
||||
)
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BasePlatform: platform.BasePlatform{
|
||||
FirmwarePackages: []string{
|
||||
"microcode_ctl", // ??
|
||||
"iwl1000-firmware",
|
||||
"iwl100-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6000-firmware",
|
||||
"iwl6050-firmware",
|
||||
},
|
||||
},
|
||||
BIOS: true,
|
||||
UEFIVendor: "fedora",
|
||||
},
|
||||
iotOCIImgType,
|
||||
iotCommitImgType,
|
||||
iotInstallerImgType,
|
||||
imageInstallerImgType,
|
||||
liveInstallerImgType,
|
||||
)
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_RAW,
|
||||
},
|
||||
BIOS: false,
|
||||
UEFIVendor: "fedora",
|
||||
},
|
||||
iotRawImgType,
|
||||
)
|
||||
aarch64.addImageTypes(
|
||||
&platform.Aarch64{
|
||||
UEFIVendor: "fedora",
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_RAW,
|
||||
},
|
||||
},
|
||||
amiImgType,
|
||||
)
|
||||
aarch64.addImageTypes(
|
||||
&platform.Aarch64{
|
||||
UEFIVendor: "fedora",
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_QCOW2,
|
||||
QCOW2Compat: "1.1",
|
||||
},
|
||||
},
|
||||
qcow2ImgType,
|
||||
ociImgType,
|
||||
)
|
||||
aarch64.addImageTypes(
|
||||
&platform.Aarch64{
|
||||
UEFIVendor: "fedora",
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_QCOW2,
|
||||
},
|
||||
},
|
||||
openstackImgType,
|
||||
)
|
||||
aarch64.addImageTypes(
|
||||
&platform.Aarch64{},
|
||||
containerImgType,
|
||||
)
|
||||
aarch64.addImageTypes(
|
||||
&platform.Aarch64{
|
||||
BasePlatform: platform.BasePlatform{
|
||||
FirmwarePackages: []string{
|
||||
"uboot-images-armv8", // ??
|
||||
"bcm283x-firmware",
|
||||
"arm-image-installer", // ??
|
||||
},
|
||||
},
|
||||
UEFIVendor: "fedora",
|
||||
},
|
||||
iotCommitImgType,
|
||||
iotOCIImgType,
|
||||
iotInstallerImgType,
|
||||
imageInstallerImgType,
|
||||
liveInstallerImgType,
|
||||
)
|
||||
aarch64.addImageTypes(
|
||||
&platform.Aarch64_IoT{
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_RAW,
|
||||
},
|
||||
UEFIVendor: "fedora",
|
||||
BootFiles: [][2]string{
|
||||
{"/usr/lib/ostree-boot/efi/bcm2710-rpi-2-b.dtb", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/bcm2710-rpi-3-b-plus.dtb", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/bcm2710-rpi-3-b.dtb", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/bcm2710-rpi-cm3.dtb", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/bcm2710-rpi-zero-2-w.dtb", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/bcm2710-rpi-zero-2.dtb", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/bcm2711-rpi-4-b.dtb", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/bcm2711-rpi-400.dtb", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/bcm2711-rpi-cm4.dtb", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/bcm2711-rpi-cm4s.dtb", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/bootcode.bin", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/config.txt", "/boot/efi/config.txt"},
|
||||
{"/usr/lib/ostree-boot/efi/fixup.dat", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/fixup4.dat", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/fixup4cd.dat", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/fixup4db.dat", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/fixup4x.dat", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/fixup_cd.dat", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/fixup_db.dat", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/fixup_x.dat", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/overlays", "/boot/efi/"},
|
||||
{"/usr/share/uboot/rpi_arm64/u-boot.bin", "/boot/efi/rpi-u-boot.bin"},
|
||||
{"/usr/lib/ostree-boot/efi/start.elf", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/start4.elf", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/start4cd.elf", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/start4db.elf", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/start4x.elf", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/start_cd.elf", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/start_db.elf", "/boot/efi/"},
|
||||
{"/usr/lib/ostree-boot/efi/start_x.elf", "/boot/efi/"},
|
||||
},
|
||||
},
|
||||
iotRawImgType,
|
||||
)
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
UEFIVendor: "fedora",
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_RAW,
|
||||
},
|
||||
},
|
||||
minimalrawImgType,
|
||||
)
|
||||
aarch64.addImageTypes(
|
||||
&platform.Aarch64{
|
||||
UEFIVendor: "fedora",
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_RAW,
|
||||
},
|
||||
},
|
||||
minimalrawImgType,
|
||||
)
|
||||
|
||||
rd.addArches(x86_64, aarch64)
|
||||
return &rd
|
||||
}
|
||||
|
|
@ -1,815 +0,0 @@
|
|||
package fedora_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro/distro_test_common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro/fedora"
|
||||
)
|
||||
|
||||
type fedoraFamilyDistro struct {
|
||||
name string
|
||||
distro distro.Distro
|
||||
}
|
||||
|
||||
var fedoraFamilyDistros = []fedoraFamilyDistro{
|
||||
{
|
||||
name: "fedora",
|
||||
distro: fedora.NewF37(),
|
||||
},
|
||||
{
|
||||
name: "fedora",
|
||||
distro: fedora.NewF38(),
|
||||
},
|
||||
{
|
||||
name: "fedora",
|
||||
distro: fedora.NewF39(),
|
||||
},
|
||||
}
|
||||
|
||||
func TestFilenameFromType(t *testing.T) {
|
||||
type args struct {
|
||||
outputFormat string
|
||||
}
|
||||
type wantResult struct {
|
||||
filename string
|
||||
mimeType string
|
||||
wantErr bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want wantResult
|
||||
}{
|
||||
{
|
||||
name: "ami",
|
||||
args: args{"ami"},
|
||||
want: wantResult{
|
||||
filename: "image.raw",
|
||||
mimeType: "application/octet-stream",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "qcow2",
|
||||
args: args{"qcow2"},
|
||||
want: wantResult{
|
||||
filename: "disk.qcow2",
|
||||
mimeType: "application/x-qemu-disk",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "openstack",
|
||||
args: args{"openstack"},
|
||||
want: wantResult{
|
||||
filename: "disk.qcow2",
|
||||
mimeType: "application/x-qemu-disk",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "vhd",
|
||||
args: args{"vhd"},
|
||||
want: wantResult{
|
||||
filename: "disk.vhd",
|
||||
mimeType: "application/x-vhd",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "vmdk",
|
||||
args: args{"vmdk"},
|
||||
want: wantResult{
|
||||
filename: "disk.vmdk",
|
||||
mimeType: "application/x-vmdk",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ova",
|
||||
args: args{"ova"},
|
||||
want: wantResult{
|
||||
filename: "image.ova",
|
||||
mimeType: "application/ovf",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "container",
|
||||
args: args{"container"},
|
||||
want: wantResult{
|
||||
filename: "container.tar",
|
||||
mimeType: "application/x-tar",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "iot-commit",
|
||||
args: args{"iot-commit"},
|
||||
want: wantResult{
|
||||
filename: "commit.tar",
|
||||
mimeType: "application/x-tar",
|
||||
},
|
||||
},
|
||||
{ // Alias
|
||||
name: "fedora-iot-commit",
|
||||
args: args{"fedora-iot-commit"},
|
||||
want: wantResult{
|
||||
filename: "commit.tar",
|
||||
mimeType: "application/x-tar",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "iot-container",
|
||||
args: args{"iot-container"},
|
||||
want: wantResult{
|
||||
filename: "container.tar",
|
||||
mimeType: "application/x-tar",
|
||||
},
|
||||
},
|
||||
{ // Alias
|
||||
name: "fedora-iot-container",
|
||||
args: args{"fedora-iot-container"},
|
||||
want: wantResult{
|
||||
filename: "container.tar",
|
||||
mimeType: "application/x-tar",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "iot-installer",
|
||||
args: args{"iot-installer"},
|
||||
want: wantResult{
|
||||
filename: "installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
},
|
||||
},
|
||||
{ // Alias
|
||||
name: "fedora-iot-installer",
|
||||
args: args{"fedora-iot-installer"},
|
||||
want: wantResult{
|
||||
filename: "installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "live-installer",
|
||||
args: args{"live-installer"},
|
||||
want: wantResult{
|
||||
filename: "live-installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "image-installer",
|
||||
args: args{"image-installer"},
|
||||
want: wantResult{
|
||||
filename: "installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
},
|
||||
},
|
||||
{ // Alias
|
||||
name: "fedora-image-installer",
|
||||
args: args{"fedora-image-installer"},
|
||||
want: wantResult{
|
||||
filename: "installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid-output-type",
|
||||
args: args{"foobar"},
|
||||
want: wantResult{wantErr: true},
|
||||
},
|
||||
{
|
||||
name: "minimal-raw",
|
||||
args: args{"minimal-raw"},
|
||||
want: wantResult{
|
||||
filename: "raw.img",
|
||||
mimeType: "application/disk",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, dist := range fedoraFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dist := dist.distro
|
||||
arch, _ := dist.GetArch("x86_64")
|
||||
imgType, err := arch.GetImageType(tt.args.outputFormat)
|
||||
if (err != nil) != tt.want.wantErr {
|
||||
t.Errorf("Arch.GetImageType() error = %v, wantErr %v", err, tt.want.wantErr)
|
||||
return
|
||||
}
|
||||
if !tt.want.wantErr {
|
||||
gotFilename := imgType.Filename()
|
||||
gotMIMEType := imgType.MIMEType()
|
||||
if gotFilename != tt.want.filename {
|
||||
t.Errorf("ImageType.Filename() got = %v, want %v", gotFilename, tt.want.filename)
|
||||
}
|
||||
if gotMIMEType != tt.want.mimeType {
|
||||
t.Errorf("ImageType.MIMEType() got1 = %v, want %v", gotMIMEType, tt.want.mimeType)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageType_BuildPackages(t *testing.T) {
|
||||
x8664BuildPackages := []string{
|
||||
"dnf",
|
||||
"dosfstools",
|
||||
"e2fsprogs",
|
||||
"policycoreutils",
|
||||
"qemu-img",
|
||||
"selinux-policy-targeted",
|
||||
"systemd",
|
||||
"tar",
|
||||
"xz",
|
||||
"grub2-pc",
|
||||
}
|
||||
aarch64BuildPackages := []string{
|
||||
"dnf",
|
||||
"dosfstools",
|
||||
"e2fsprogs",
|
||||
"policycoreutils",
|
||||
"qemu-img",
|
||||
"selinux-policy-targeted",
|
||||
"systemd",
|
||||
"tar",
|
||||
"xz",
|
||||
}
|
||||
buildPackages := map[string][]string{
|
||||
"x86_64": x8664BuildPackages,
|
||||
"aarch64": aarch64BuildPackages,
|
||||
}
|
||||
for _, dist := range fedoraFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
d := dist.distro
|
||||
for _, archLabel := range d.ListArches() {
|
||||
archStruct, err := d.GetArch(archLabel)
|
||||
if assert.NoErrorf(t, err, "d.GetArch(%v) returned err = %v; expected nil", archLabel, err) {
|
||||
continue
|
||||
}
|
||||
for _, itLabel := range archStruct.ListImageTypes() {
|
||||
itStruct, err := archStruct.GetImageType(itLabel)
|
||||
if assert.NoErrorf(t, err, "d.GetArch(%v) returned err = %v; expected nil", archLabel, err) {
|
||||
continue
|
||||
}
|
||||
manifest, _, err := itStruct.Manifest(&blueprint.Blueprint{}, distro.ImageOptions{}, nil, 0)
|
||||
assert.NoError(t, err)
|
||||
buildPkgs := manifest.GetPackageSetChains()["build"]
|
||||
assert.NotNil(t, buildPkgs)
|
||||
assert.Len(t, buildPkgs, 1)
|
||||
assert.ElementsMatch(t, buildPackages[archLabel], buildPkgs[0].Include)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageType_Name(t *testing.T) {
|
||||
imgMap := []struct {
|
||||
arch string
|
||||
imgNames []string
|
||||
}{
|
||||
{
|
||||
arch: "x86_64",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"openstack",
|
||||
"vhd",
|
||||
"vmdk",
|
||||
"ova",
|
||||
"ami",
|
||||
"iot-commit",
|
||||
"iot-container",
|
||||
"iot-installer",
|
||||
"iot-raw-image",
|
||||
"oci",
|
||||
"image-installer",
|
||||
"live-installer",
|
||||
"minimal-raw",
|
||||
},
|
||||
},
|
||||
{
|
||||
arch: "aarch64",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"openstack",
|
||||
"ami",
|
||||
"oci",
|
||||
"iot-commit",
|
||||
"iot-container",
|
||||
"iot-installer",
|
||||
"iot-raw-image",
|
||||
"image-installer",
|
||||
"minimal-raw",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, dist := range fedoraFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, mapping := range imgMap {
|
||||
if mapping.arch == "s390x" {
|
||||
continue
|
||||
}
|
||||
arch, err := dist.distro.GetArch(mapping.arch)
|
||||
if assert.NoError(t, err) {
|
||||
for _, imgName := range mapping.imgNames {
|
||||
if imgName == "iot-commit" {
|
||||
continue
|
||||
}
|
||||
imgType, err := arch.GetImageType(imgName)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equalf(t, imgName, imgType.Name(), "arch: %s", mapping.arch)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageTypeAliases(t *testing.T) {
|
||||
type args struct {
|
||||
imageTypeAliases []string
|
||||
}
|
||||
type wantResult struct {
|
||||
imageTypeName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want wantResult
|
||||
}{
|
||||
{
|
||||
name: "iot-commit aliases",
|
||||
args: args{
|
||||
imageTypeAliases: []string{"fedora-iot-commit"},
|
||||
},
|
||||
want: wantResult{
|
||||
imageTypeName: "iot-commit",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "iot-container aliases",
|
||||
args: args{
|
||||
imageTypeAliases: []string{"fedora-iot-container"},
|
||||
},
|
||||
want: wantResult{
|
||||
imageTypeName: "iot-container",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "iot-installer aliases",
|
||||
args: args{
|
||||
imageTypeAliases: []string{"fedora-iot-installer"},
|
||||
},
|
||||
want: wantResult{
|
||||
imageTypeName: "iot-installer",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, dist := range fedoraFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dist := dist.distro
|
||||
for _, archName := range dist.ListArches() {
|
||||
t.Run(archName, func(t *testing.T) {
|
||||
arch, err := dist.GetArch(archName)
|
||||
require.Nilf(t, err,
|
||||
"failed to get architecture '%s', previously listed as supported for the distro '%s'",
|
||||
archName, dist.Name())
|
||||
// Test image type aliases only if the aliased image type is supported for the arch
|
||||
if _, err = arch.GetImageType(tt.want.imageTypeName); err != nil {
|
||||
t.Skipf("aliased image type '%s' is not supported for architecture '%s'",
|
||||
tt.want.imageTypeName, archName)
|
||||
}
|
||||
for _, alias := range tt.args.imageTypeAliases {
|
||||
t.Run(fmt.Sprintf("'%s' alias for image type '%s'", alias, tt.want.imageTypeName),
|
||||
func(t *testing.T) {
|
||||
gotImage, err := arch.GetImageType(alias)
|
||||
require.Nilf(t, err, "arch.GetImageType() for image type alias '%s' failed: %v",
|
||||
alias, err)
|
||||
assert.Equalf(t, tt.want.imageTypeName, gotImage.Name(),
|
||||
"got unexpected image type name for alias '%s'. got = %s, want = %s",
|
||||
alias, tt.want.imageTypeName, gotImage.Name())
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Check that Manifest() function returns an error for unsupported
|
||||
// configurations.
|
||||
func TestDistro_ManifestError(t *testing.T) {
|
||||
// Currently, the only unsupported configuration is OSTree commit types
|
||||
// with Kernel boot options
|
||||
fedoraDistro := fedora.NewF37()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Kernel: &blueprint.KernelCustomization{
|
||||
Append: "debug",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, archName := range fedoraDistro.ListArches() {
|
||||
arch, _ := fedoraDistro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
imgOpts := distro.ImageOptions{
|
||||
Size: imgType.Size(0),
|
||||
}
|
||||
_, _, err := imgType.Manifest(&bp, imgOpts, nil, 0)
|
||||
if imgTypeName == "iot-commit" || imgTypeName == "iot-container" {
|
||||
assert.EqualError(t, err, "kernel boot parameter customizations are not supported for ostree types")
|
||||
} else if imgTypeName == "iot-installer" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("boot ISO image type \"%s\" requires specifying a URL from which to retrieve the OSTree commit", imgTypeName))
|
||||
} else if imgTypeName == "image-installer" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("unsupported blueprint customizations found for boot ISO image type \"%s\": (allowed: User, Group)", imgTypeName))
|
||||
} else if imgTypeName == "live-installer" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("unsupported blueprint customizations found for boot ISO image type \"%s\": (allowed: None)", imgTypeName))
|
||||
} else if imgTypeName == "iot-raw-image" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("unsupported blueprint customizations found for image type %q: (allowed: User, Group, Directories, Files, Services)", imgTypeName))
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestArchitecture_ListImageTypes(t *testing.T) {
|
||||
imgMap := []struct {
|
||||
arch string
|
||||
imgNames []string
|
||||
fedoraAdditionalImageTypes []string
|
||||
}{
|
||||
{
|
||||
arch: "x86_64",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"openstack",
|
||||
"vhd",
|
||||
"vmdk",
|
||||
"ova",
|
||||
"ami",
|
||||
"iot-commit",
|
||||
"iot-container",
|
||||
"iot-installer",
|
||||
"iot-raw-image",
|
||||
"oci",
|
||||
"container",
|
||||
"image-installer",
|
||||
"live-installer",
|
||||
"minimal-raw",
|
||||
},
|
||||
},
|
||||
{
|
||||
arch: "aarch64",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"openstack",
|
||||
"ami",
|
||||
"iot-commit",
|
||||
"iot-container",
|
||||
"iot-installer",
|
||||
"iot-raw-image",
|
||||
"oci",
|
||||
"container",
|
||||
"image-installer",
|
||||
"live-installer",
|
||||
"minimal-raw",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, dist := range fedoraFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, mapping := range imgMap {
|
||||
arch, err := dist.distro.GetArch(mapping.arch)
|
||||
require.NoError(t, err)
|
||||
imageTypes := arch.ListImageTypes()
|
||||
|
||||
var expectedImageTypes []string
|
||||
expectedImageTypes = append(expectedImageTypes, mapping.imgNames...)
|
||||
if dist.name == "fedora" {
|
||||
expectedImageTypes = append(expectedImageTypes, mapping.fedoraAdditionalImageTypes...)
|
||||
}
|
||||
|
||||
require.ElementsMatch(t, expectedImageTypes, imageTypes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFedora_ListArches(t *testing.T) {
|
||||
arches := fedora.NewF37().ListArches()
|
||||
assert.Equal(t, []string{"aarch64", "x86_64"}, arches)
|
||||
}
|
||||
|
||||
func TestFedora37_GetArch(t *testing.T) {
|
||||
arches := []struct {
|
||||
name string
|
||||
errorExpected bool
|
||||
errorExpectedInCentos bool
|
||||
}{
|
||||
{
|
||||
name: "x86_64",
|
||||
},
|
||||
{
|
||||
name: "aarch64",
|
||||
},
|
||||
{
|
||||
name: "s390x",
|
||||
errorExpected: true,
|
||||
},
|
||||
{
|
||||
name: "ppc64le",
|
||||
errorExpected: true,
|
||||
},
|
||||
{
|
||||
name: "foo-arch",
|
||||
errorExpected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, dist := range fedoraFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, a := range arches {
|
||||
actualArch, err := dist.distro.GetArch(a.name)
|
||||
if a.errorExpected {
|
||||
assert.Nil(t, actualArch)
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.Equal(t, a.name, actualArch.Name())
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFedora37_Name(t *testing.T) {
|
||||
distro := fedora.NewF37()
|
||||
assert.Equal(t, "fedora-37", distro.Name())
|
||||
}
|
||||
|
||||
func TestFedora37_KernelOption(t *testing.T) {
|
||||
distro_test_common.TestDistro_KernelOption(t, fedora.NewF37())
|
||||
}
|
||||
|
||||
func TestFedora_OSTreeOptions(t *testing.T) {
|
||||
distro_test_common.TestDistro_OSTreeOptions(t, fedora.NewF37())
|
||||
}
|
||||
|
||||
func TestDistro_CustomFileSystemManifestError(t *testing.T) {
|
||||
fedoraDistro := fedora.NewF37()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/etc",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range fedoraDistro.ListArches() {
|
||||
arch, _ := fedoraDistro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if imgTypeName == "iot-commit" || imgTypeName == "iot-container" {
|
||||
assert.EqualError(t, err, "Custom mountpoints are not supported for ostree types")
|
||||
} else if imgTypeName == "iot-raw-image" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("unsupported blueprint customizations found for image type %q: (allowed: User, Group, Directories, Files, Services)", imgTypeName))
|
||||
} else if imgTypeName == "iot-installer" || imgTypeName == "image-installer" {
|
||||
continue
|
||||
} else if imgTypeName == "live-installer" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("unsupported blueprint customizations found for boot ISO image type \"%s\": (allowed: None)", imgTypeName))
|
||||
} else {
|
||||
assert.EqualError(t, err, "The following custom mountpoints are not supported [\"/etc\"]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_TestRootMountPoint(t *testing.T) {
|
||||
fedoraDistro := fedora.NewF37()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range fedoraDistro.ListArches() {
|
||||
arch, _ := fedoraDistro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if imgTypeName == "iot-commit" || imgTypeName == "iot-container" {
|
||||
assert.EqualError(t, err, "Custom mountpoints are not supported for ostree types")
|
||||
} else if imgTypeName == "iot-raw-image" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("unsupported blueprint customizations found for image type %q: (allowed: User, Group, Directories, Files, Services)", imgTypeName))
|
||||
} else if imgTypeName == "iot-installer" || imgTypeName == "image-installer" {
|
||||
continue
|
||||
} else if imgTypeName == "live-installer" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("unsupported blueprint customizations found for boot ISO image type \"%s\": (allowed: None)", imgTypeName))
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_CustomFileSystemSubDirectories(t *testing.T) {
|
||||
fedoraDistro := fedora.NewF37()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/log",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/log/audit",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range fedoraDistro.ListArches() {
|
||||
arch, _ := fedoraDistro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if strings.HasPrefix(imgTypeName, "iot-") || strings.HasPrefix(imgTypeName, "image-") {
|
||||
continue
|
||||
} else if imgTypeName == "live-installer" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("unsupported blueprint customizations found for boot ISO image type \"%s\": (allowed: None)", imgTypeName))
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_MountpointsWithArbitraryDepthAllowed(t *testing.T) {
|
||||
fedoraDistro := fedora.NewF37()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/a",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/a/b",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/a/b/c",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/a/b/c/d",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range fedoraDistro.ListArches() {
|
||||
arch, _ := fedoraDistro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if strings.HasPrefix(imgTypeName, "iot-") || strings.HasPrefix(imgTypeName, "image-") {
|
||||
continue
|
||||
} else if imgTypeName == "live-installer" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("unsupported blueprint customizations found for boot ISO image type \"%s\": (allowed: None)", imgTypeName))
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_DirtyMountpointsNotAllowed(t *testing.T) {
|
||||
fedoraDistro := fedora.NewF37()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "//",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var//",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var//log/audit/",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range fedoraDistro.ListArches() {
|
||||
arch, _ := fedoraDistro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if strings.HasPrefix(imgTypeName, "iot-") || strings.HasPrefix(imgTypeName, "image-") {
|
||||
continue
|
||||
} else if imgTypeName == "live-installer" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("unsupported blueprint customizations found for boot ISO image type \"%s\": (allowed: None)", imgTypeName))
|
||||
} else {
|
||||
assert.EqualError(t, err, "The following custom mountpoints are not supported [\"//\" \"/var//\" \"/var//log/audit/\"]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_CustomFileSystemPatternMatching(t *testing.T) {
|
||||
fedoraDistro := fedora.NewF37()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/variable",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/variable/log/audit",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range fedoraDistro.ListArches() {
|
||||
arch, _ := fedoraDistro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if imgTypeName == "iot-commit" || imgTypeName == "iot-container" {
|
||||
assert.EqualError(t, err, "Custom mountpoints are not supported for ostree types")
|
||||
} else if imgTypeName == "iot-raw-image" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("unsupported blueprint customizations found for image type %q: (allowed: User, Group, Directories, Files, Services)", imgTypeName))
|
||||
} else if imgTypeName == "iot-installer" || imgTypeName == "image-installer" {
|
||||
continue
|
||||
} else if imgTypeName == "live-installer" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("unsupported blueprint customizations found for boot ISO image type \"%s\": (allowed: None)", imgTypeName))
|
||||
} else {
|
||||
assert.EqualError(t, err, "The following custom mountpoints are not supported [\"/variable\" \"/variable/log/audit\"]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_CustomUsrPartitionNotLargeEnough(t *testing.T) {
|
||||
fedoraDistro := fedora.NewF37()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/usr",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range fedoraDistro.ListArches() {
|
||||
arch, _ := fedoraDistro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if imgTypeName == "iot-commit" || imgTypeName == "iot-container" {
|
||||
assert.EqualError(t, err, "Custom mountpoints are not supported for ostree types")
|
||||
} else if imgTypeName == "iot-raw-image" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("unsupported blueprint customizations found for image type %q: (allowed: User, Group, Directories, Files, Services)", imgTypeName))
|
||||
} else if imgTypeName == "iot-installer" || imgTypeName == "image-installer" {
|
||||
continue
|
||||
} else if imgTypeName == "live-installer" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("unsupported blueprint customizations found for boot ISO image type \"%s\": (allowed: None)", imgTypeName))
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,532 +0,0 @@
|
|||
package fedora
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/container"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/image"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/oscap"
|
||||
"github.com/osbuild/osbuild-composer/internal/ostree"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/users"
|
||||
"github.com/osbuild/osbuild-composer/internal/workload"
|
||||
)
|
||||
|
||||
// HELPERS
|
||||
|
||||
func osCustomizations(
|
||||
t *imageType,
|
||||
osPackageSet rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
c *blueprint.Customizations) manifest.OSCustomizations {
|
||||
|
||||
imageConfig := t.getDefaultImageConfig()
|
||||
|
||||
osc := manifest.OSCustomizations{}
|
||||
|
||||
if t.bootable || t.rpmOstree {
|
||||
osc.KernelName = c.GetKernel().Name
|
||||
|
||||
var kernelOptions []string
|
||||
if t.kernelOptions != "" {
|
||||
kernelOptions = append(kernelOptions, t.kernelOptions)
|
||||
}
|
||||
if bpKernel := c.GetKernel(); bpKernel.Append != "" {
|
||||
kernelOptions = append(kernelOptions, bpKernel.Append)
|
||||
}
|
||||
osc.KernelOptionsAppend = kernelOptions
|
||||
}
|
||||
|
||||
osc.ExtraBasePackages = osPackageSet.Include
|
||||
osc.ExcludeBasePackages = osPackageSet.Exclude
|
||||
osc.ExtraBaseRepos = osPackageSet.Repositories
|
||||
|
||||
osc.Containers = containers
|
||||
|
||||
osc.GPGKeyFiles = imageConfig.GPGKeyFiles
|
||||
if imageConfig.ExcludeDocs != nil {
|
||||
osc.ExcludeDocs = *imageConfig.ExcludeDocs
|
||||
}
|
||||
|
||||
if !t.bootISO {
|
||||
// don't put users and groups in the payload of an installer
|
||||
// add them via kickstart instead
|
||||
osc.Groups = users.GroupsFromBP(c.GetGroups())
|
||||
osc.Users = users.UsersFromBP(c.GetUsers())
|
||||
}
|
||||
|
||||
osc.EnabledServices = imageConfig.EnabledServices
|
||||
osc.DisabledServices = imageConfig.DisabledServices
|
||||
if imageConfig.DefaultTarget != nil {
|
||||
osc.DefaultTarget = *imageConfig.DefaultTarget
|
||||
}
|
||||
|
||||
if fw := c.GetFirewall(); fw != nil {
|
||||
options := osbuild.FirewallStageOptions{
|
||||
Ports: fw.Ports,
|
||||
}
|
||||
|
||||
if fw.Services != nil {
|
||||
options.EnabledServices = fw.Services.Enabled
|
||||
options.DisabledServices = fw.Services.Disabled
|
||||
}
|
||||
osc.Firewall = &options
|
||||
}
|
||||
|
||||
language, keyboard := c.GetPrimaryLocale()
|
||||
if language != nil {
|
||||
osc.Language = *language
|
||||
} else if imageConfig.Locale != nil {
|
||||
osc.Language = *imageConfig.Locale
|
||||
}
|
||||
if keyboard != nil {
|
||||
osc.Keyboard = keyboard
|
||||
} else if imageConfig.Keyboard != nil {
|
||||
osc.Keyboard = &imageConfig.Keyboard.Keymap
|
||||
}
|
||||
|
||||
if hostname := c.GetHostname(); hostname != nil {
|
||||
osc.Hostname = *hostname
|
||||
} else {
|
||||
osc.Hostname = "localhost.localdomain"
|
||||
}
|
||||
|
||||
timezone, ntpServers := c.GetTimezoneSettings()
|
||||
if timezone != nil {
|
||||
osc.Timezone = *timezone
|
||||
} else if imageConfig.Timezone != nil {
|
||||
osc.Timezone = *imageConfig.Timezone
|
||||
}
|
||||
|
||||
if len(ntpServers) > 0 {
|
||||
for _, server := range ntpServers {
|
||||
osc.NTPServers = append(osc.NTPServers, osbuild.ChronyConfigServer{Hostname: server})
|
||||
}
|
||||
} else if imageConfig.TimeSynchronization != nil {
|
||||
osc.NTPServers = imageConfig.TimeSynchronization.Servers
|
||||
}
|
||||
|
||||
// Relabel the tree, unless the `NoSElinux` flag is explicitly set to `true`
|
||||
if imageConfig.NoSElinux == nil || imageConfig.NoSElinux != nil && !*imageConfig.NoSElinux {
|
||||
osc.SElinux = "targeted"
|
||||
}
|
||||
|
||||
if oscapConfig := c.GetOpenSCAP(); oscapConfig != nil {
|
||||
if t.rpmOstree {
|
||||
panic("unexpected oscap options for ostree image type")
|
||||
}
|
||||
var datastream = oscapConfig.DataStream
|
||||
if datastream == "" {
|
||||
datastream = oscap.DefaultFedoraDatastream()
|
||||
}
|
||||
osc.OpenSCAPConfig = osbuild.NewOscapRemediationStageOptions(
|
||||
osbuild.OscapConfig{
|
||||
Datastream: datastream,
|
||||
ProfileID: oscapConfig.ProfileID,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
var err error
|
||||
osc.Directories, err = blueprint.DirectoryCustomizationsToFsNodeDirectories(c.GetDirectories())
|
||||
if err != nil {
|
||||
// In theory this should never happen, because the blueprint directory customizations
|
||||
// should have been validated before this point.
|
||||
panic(fmt.Sprintf("failed to convert directory customizations to fs node directories: %v", err))
|
||||
}
|
||||
|
||||
osc.Files, err = blueprint.FileCustomizationsToFsNodeFiles(c.GetFiles())
|
||||
if err != nil {
|
||||
// In theory this should never happen, because the blueprint file customizations
|
||||
// should have been validated before this point.
|
||||
panic(fmt.Sprintf("failed to convert file customizations to fs node files: %v", err))
|
||||
}
|
||||
|
||||
customRepos, err := c.GetRepositories()
|
||||
if err != nil {
|
||||
// This shouldn't happen and since the repos
|
||||
// should have already been validated
|
||||
panic(fmt.Sprintf("failed to get custom repos: %v", err))
|
||||
}
|
||||
|
||||
// This function returns a map of filename and corresponding yum repos
|
||||
// and a list of fs node files for the inline gpg keys so we can save
|
||||
// them to disk. This step also swaps the inline gpg key with the path
|
||||
// to the file in the os file tree
|
||||
yumRepos, gpgKeyFiles, err := blueprint.RepoCustomizationsToRepoConfigAndGPGKeyFiles(customRepos)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to convert inline gpgkeys to fs node files: %v", err))
|
||||
}
|
||||
|
||||
// add the gpg key files to the list of files to be added to the tree
|
||||
if len(gpgKeyFiles) > 0 {
|
||||
osc.Files = append(osc.Files, gpgKeyFiles...)
|
||||
}
|
||||
|
||||
for filename, repos := range yumRepos {
|
||||
osc.YUMRepos = append(osc.YUMRepos, osbuild.NewYumReposStageOptions(filename, repos))
|
||||
}
|
||||
|
||||
osc.ShellInit = imageConfig.ShellInit
|
||||
|
||||
osc.Grub2Config = imageConfig.Grub2Config
|
||||
osc.Sysconfig = imageConfig.Sysconfig
|
||||
osc.SystemdLogind = imageConfig.SystemdLogind
|
||||
osc.CloudInit = imageConfig.CloudInit
|
||||
osc.Modprobe = imageConfig.Modprobe
|
||||
osc.DracutConf = imageConfig.DracutConf
|
||||
osc.SystemdUnit = imageConfig.SystemdUnit
|
||||
osc.Authselect = imageConfig.Authselect
|
||||
osc.SELinuxConfig = imageConfig.SELinuxConfig
|
||||
osc.Tuned = imageConfig.Tuned
|
||||
osc.Tmpfilesd = imageConfig.Tmpfilesd
|
||||
osc.PamLimitsConf = imageConfig.PamLimitsConf
|
||||
osc.Sysctld = imageConfig.Sysctld
|
||||
osc.DNFConfig = imageConfig.DNFConfig
|
||||
osc.SshdConfig = imageConfig.SshdConfig
|
||||
osc.AuthConfig = imageConfig.Authconfig
|
||||
osc.PwQuality = imageConfig.PwQuality
|
||||
|
||||
return osc
|
||||
}
|
||||
|
||||
// IMAGES
|
||||
|
||||
func liveImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
img := image.NewLiveImage()
|
||||
img.Platform = t.platform
|
||||
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], containers, customizations)
|
||||
img.Environment = t.environment
|
||||
img.Workload = workload
|
||||
// TODO: move generation into LiveImage
|
||||
pt, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img.PartitionTable = pt
|
||||
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func containerImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
c *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
img := image.NewBaseContainer()
|
||||
|
||||
img.Platform = t.platform
|
||||
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], containers, c)
|
||||
img.Environment = t.environment
|
||||
img.Workload = workload
|
||||
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func liveInstallerImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
img := image.NewAnacondaLiveInstaller()
|
||||
|
||||
distro := t.Arch().Distro()
|
||||
|
||||
// If the live installer is generated for Fedora 39 or higher then we enable the web ui
|
||||
// kernel options. This is a temporary thing as the check for this should really lie with
|
||||
// anaconda and their `liveinst` script to determine which frontend to start.
|
||||
if common.VersionLessThan(distro.Releasever(), "39") {
|
||||
img.AdditionalKernelOpts = []string{}
|
||||
} else {
|
||||
img.AdditionalKernelOpts = []string{"inst.webui"}
|
||||
}
|
||||
|
||||
img.Platform = t.platform
|
||||
img.Workload = workload
|
||||
img.ExtraBasePackages = packageSets[installerPkgsKey]
|
||||
|
||||
d := t.arch.distro
|
||||
|
||||
img.ISOLabelTempl = d.isolabelTmpl
|
||||
img.Product = d.product
|
||||
img.OSName = "fedora"
|
||||
img.OSVersion = d.osVersion
|
||||
img.Release = fmt.Sprintf("%s %s", d.product, d.osVersion)
|
||||
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func imageInstallerImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
img := image.NewAnacondaTarInstaller()
|
||||
|
||||
// Enable anaconda-webui for Fedora > 38
|
||||
distro := t.Arch().Distro()
|
||||
if strings.HasPrefix(distro.Name(), "fedora") && !common.VersionLessThan(distro.Releasever(), "38") {
|
||||
img.AdditionalAnacondaModules = []string{
|
||||
"org.fedoraproject.Anaconda.Modules.Security",
|
||||
"org.fedoraproject.Anaconda.Modules.Timezone",
|
||||
"org.fedoraproject.Anaconda.Modules.Localization",
|
||||
}
|
||||
img.AdditionalKernelOpts = []string{"inst.webui", "inst.webui.remote"}
|
||||
}
|
||||
img.AdditionalAnacondaModules = append(img.AdditionalAnacondaModules, "org.fedoraproject.Anaconda.Modules.Users")
|
||||
|
||||
img.Platform = t.platform
|
||||
img.Workload = workload
|
||||
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], containers, customizations)
|
||||
img.ExtraBasePackages = packageSets[installerPkgsKey]
|
||||
img.Users = users.UsersFromBP(customizations.GetUsers())
|
||||
img.Groups = users.GroupsFromBP(customizations.GetGroups())
|
||||
|
||||
img.SquashfsCompression = "lz4"
|
||||
|
||||
d := t.arch.distro
|
||||
|
||||
img.ISOLabelTempl = d.isolabelTmpl
|
||||
img.Product = d.product
|
||||
img.OSName = "fedora"
|
||||
img.OSVersion = d.osVersion
|
||||
img.Release = fmt.Sprintf("%s %s", d.product, d.osVersion)
|
||||
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func iotCommitImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
parentCommit, commitRef := makeOSTreeParentCommit(options.OSTree, t.OSTreeRef())
|
||||
img := image.NewOSTreeArchive(commitRef)
|
||||
|
||||
img.Platform = t.platform
|
||||
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], containers, customizations)
|
||||
img.Environment = t.environment
|
||||
img.Workload = workload
|
||||
img.OSTreeParent = parentCommit
|
||||
img.OSVersion = t.arch.distro.osVersion
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func iotContainerImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
parentCommit, commitRef := makeOSTreeParentCommit(options.OSTree, t.OSTreeRef())
|
||||
img := image.NewOSTreeContainer(commitRef)
|
||||
|
||||
img.Platform = t.platform
|
||||
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], containers, customizations)
|
||||
img.ContainerLanguage = img.OSCustomizations.Language
|
||||
img.Environment = t.environment
|
||||
img.Workload = workload
|
||||
img.OSTreeParent = parentCommit
|
||||
img.OSVersion = t.arch.distro.osVersion
|
||||
img.ExtraContainerPackages = packageSets[containerPkgsKey]
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func iotInstallerImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
d := t.arch.distro
|
||||
|
||||
commit, err := makeOSTreePayloadCommit(options.OSTree, t.OSTreeRef())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", t.Name(), err.Error())
|
||||
}
|
||||
|
||||
img := image.NewAnacondaOSTreeInstaller(commit)
|
||||
|
||||
img.Platform = t.platform
|
||||
img.ExtraBasePackages = packageSets[installerPkgsKey]
|
||||
img.Users = users.UsersFromBP(customizations.GetUsers())
|
||||
img.Groups = users.GroupsFromBP(customizations.GetGroups())
|
||||
img.AdditionalAnacondaModules = []string{
|
||||
"org.fedoraproject.Anaconda.Modules.Timezone",
|
||||
"org.fedoraproject.Anaconda.Modules.Localization",
|
||||
"org.fedoraproject.Anaconda.Modules.Users",
|
||||
}
|
||||
|
||||
img.SquashfsCompression = "lz4"
|
||||
|
||||
img.ISOLabelTempl = d.isolabelTmpl
|
||||
img.Product = d.product
|
||||
img.Variant = "IoT"
|
||||
img.OSName = "fedora"
|
||||
img.OSVersion = d.osVersion
|
||||
img.Release = fmt.Sprintf("%s %s", d.product, d.osVersion)
|
||||
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func iotRawImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
commit, err := makeOSTreePayloadCommit(options.OSTree, t.OSTreeRef())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", t.Name(), err.Error())
|
||||
}
|
||||
|
||||
img := image.NewOSTreeRawImage(commit)
|
||||
|
||||
// Set sysroot read-only only for Fedora 37+
|
||||
distro := t.Arch().Distro()
|
||||
if strings.HasPrefix(distro.Name(), "fedora") && !common.VersionLessThan(distro.Releasever(), "37") {
|
||||
img.SysrootReadOnly = true
|
||||
}
|
||||
|
||||
img.Users = users.UsersFromBP(customizations.GetUsers())
|
||||
img.Groups = users.GroupsFromBP(customizations.GetGroups())
|
||||
|
||||
img.Directories, err = blueprint.DirectoryCustomizationsToFsNodeDirectories(customizations.GetDirectories())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img.Files, err = blueprint.FileCustomizationsToFsNodeFiles(customizations.GetFiles())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// "rw" kernel option is required when /sysroot is mounted read-only to
|
||||
// keep stateful parts of the filesystem writeable (/var/ and /etc)
|
||||
img.KernelOptionsAppend = []string{"modprobe.blacklist=vc4", "rw"}
|
||||
img.Keyboard = "us"
|
||||
img.Locale = "C.UTF-8"
|
||||
|
||||
img.Platform = t.platform
|
||||
img.Workload = workload
|
||||
|
||||
img.Remote = ostree.Remote{
|
||||
Name: "fedora-iot",
|
||||
URL: "https://ostree.fedoraproject.org/iot",
|
||||
ContentURL: "mirrorlist=https://ostree.fedoraproject.org/iot/mirrorlist",
|
||||
GPGKeyPaths: []string{"/etc/pki/rpm-gpg/"},
|
||||
}
|
||||
img.OSName = "fedora-iot"
|
||||
|
||||
// TODO: move generation into LiveImage
|
||||
pt, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img.PartitionTable = pt
|
||||
|
||||
img.Filename = t.Filename()
|
||||
img.Compression = t.compression
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// Create an ostree SourceSpec to define an ostree parent commit using the user
|
||||
// options and the default ref for the image type. Additionally returns the
|
||||
// ref to be used for the new commit to be created.
|
||||
func makeOSTreeParentCommit(options *ostree.ImageOptions, defaultRef string) (*ostree.SourceSpec, string) {
|
||||
commitRef := defaultRef
|
||||
if options == nil {
|
||||
// nothing to do
|
||||
return nil, commitRef
|
||||
}
|
||||
if options.ImageRef != "" {
|
||||
// user option overrides default commit ref
|
||||
commitRef = options.ImageRef
|
||||
}
|
||||
|
||||
var parentCommit *ostree.SourceSpec
|
||||
if options.URL == "" {
|
||||
// no parent
|
||||
return nil, commitRef
|
||||
}
|
||||
|
||||
// ostree URL specified: set source spec for parent commit
|
||||
parentRef := options.ParentRef
|
||||
if parentRef == "" {
|
||||
// parent ref not set: use image ref
|
||||
parentRef = commitRef
|
||||
|
||||
}
|
||||
parentCommit = &ostree.SourceSpec{
|
||||
URL: options.URL,
|
||||
Ref: parentRef,
|
||||
RHSM: options.RHSM,
|
||||
}
|
||||
return parentCommit, commitRef
|
||||
}
|
||||
|
||||
// Create an ostree SourceSpec to define an ostree payload using the user options and the default ref for the image type.
|
||||
func makeOSTreePayloadCommit(options *ostree.ImageOptions, defaultRef string) (ostree.SourceSpec, error) {
|
||||
if options == nil || options.URL == "" {
|
||||
// this should be caught by checkOptions() in distro, but it's good
|
||||
// to guard against it here as well
|
||||
return ostree.SourceSpec{}, fmt.Errorf("ostree commit URL required")
|
||||
}
|
||||
|
||||
commitRef := defaultRef
|
||||
if options.ImageRef != "" {
|
||||
// user option overrides default commit ref
|
||||
commitRef = options.ImageRef
|
||||
}
|
||||
|
||||
return ostree.SourceSpec{
|
||||
URL: options.URL,
|
||||
Ref: commitRef,
|
||||
RHSM: options.RHSM,
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -1,342 +0,0 @@
|
|||
package fedora
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/container"
|
||||
"github.com/osbuild/osbuild-composer/internal/disk"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/environment"
|
||||
"github.com/osbuild/osbuild-composer/internal/image"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/oscap"
|
||||
"github.com/osbuild/osbuild-composer/internal/ostree"
|
||||
"github.com/osbuild/osbuild-composer/internal/pathpolicy"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/workload"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type imageFunc func(workload workload.Workload, t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, packageSets map[string]rpmmd.PackageSet, containers []container.SourceSpec, rng *rand.Rand) (image.ImageKind, error)
|
||||
|
||||
type packageSetFunc func(t *imageType) rpmmd.PackageSet
|
||||
|
||||
type imageType struct {
|
||||
arch *architecture
|
||||
platform platform.Platform
|
||||
environment environment.Environment
|
||||
workload workload.Workload
|
||||
name string
|
||||
nameAliases []string
|
||||
filename string
|
||||
compression string
|
||||
mimeType string
|
||||
packageSets map[string]packageSetFunc
|
||||
defaultImageConfig *distro.ImageConfig
|
||||
kernelOptions string
|
||||
defaultSize uint64
|
||||
buildPipelines []string
|
||||
payloadPipelines []string
|
||||
exports []string
|
||||
image imageFunc
|
||||
|
||||
// bootISO: installable ISO
|
||||
bootISO bool
|
||||
// rpmOstree: iot/ostree
|
||||
rpmOstree bool
|
||||
// bootable image
|
||||
bootable bool
|
||||
// List of valid arches for the image type
|
||||
basePartitionTables distro.BasePartitionTableMap
|
||||
requiredPartitionSizes map[string]uint64
|
||||
}
|
||||
|
||||
func (t *imageType) Name() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *imageType) Arch() distro.Arch {
|
||||
return t.arch
|
||||
}
|
||||
|
||||
func (t *imageType) Filename() string {
|
||||
return t.filename
|
||||
}
|
||||
|
||||
func (t *imageType) MIMEType() string {
|
||||
return t.mimeType
|
||||
}
|
||||
|
||||
func (t *imageType) OSTreeRef() string {
|
||||
d := t.arch.distro
|
||||
if t.rpmOstree {
|
||||
return fmt.Sprintf(d.ostreeRefTmpl, t.arch.Name())
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t *imageType) Size(size uint64) uint64 {
|
||||
// Microsoft Azure requires vhd images to be rounded up to the nearest MB
|
||||
if t.name == "vhd" && size%common.MebiByte != 0 {
|
||||
size = (size/common.MebiByte + 1) * common.MebiByte
|
||||
}
|
||||
if size == 0 {
|
||||
size = t.defaultSize
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func (t *imageType) BuildPipelines() []string {
|
||||
return t.buildPipelines
|
||||
}
|
||||
|
||||
func (t *imageType) PayloadPipelines() []string {
|
||||
return t.payloadPipelines
|
||||
}
|
||||
|
||||
func (t *imageType) PayloadPackageSets() []string {
|
||||
return []string{blueprintPkgsKey}
|
||||
}
|
||||
|
||||
func (t *imageType) PackageSetsChains() map[string][]string {
|
||||
return make(map[string][]string)
|
||||
}
|
||||
|
||||
func (t *imageType) Exports() []string {
|
||||
if len(t.exports) > 0 {
|
||||
return t.exports
|
||||
}
|
||||
return []string{"assembler"}
|
||||
}
|
||||
|
||||
func (t *imageType) BootMode() distro.BootMode {
|
||||
if t.platform.GetUEFIVendor() != "" && t.platform.GetBIOSPlatform() != "" {
|
||||
return distro.BOOT_HYBRID
|
||||
} else if t.platform.GetUEFIVendor() != "" {
|
||||
return distro.BOOT_UEFI
|
||||
} else if t.platform.GetBIOSPlatform() != "" || t.platform.GetZiplSupport() {
|
||||
return distro.BOOT_LEGACY
|
||||
}
|
||||
return distro.BOOT_NONE
|
||||
}
|
||||
|
||||
func (t *imageType) getPartitionTable(
|
||||
mountpoints []blueprint.FilesystemCustomization,
|
||||
options distro.ImageOptions,
|
||||
rng *rand.Rand,
|
||||
) (*disk.PartitionTable, error) {
|
||||
basePartitionTable, exists := t.basePartitionTables[t.arch.Name()]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("unknown arch: " + t.arch.Name())
|
||||
}
|
||||
|
||||
imageSize := t.Size(options.Size)
|
||||
|
||||
lvmify := !t.rpmOstree
|
||||
|
||||
return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, lvmify, t.requiredPartitionSizes, rng)
|
||||
}
|
||||
|
||||
func (t *imageType) getDefaultImageConfig() *distro.ImageConfig {
|
||||
// ensure that image always returns non-nil default config
|
||||
imageConfig := t.defaultImageConfig
|
||||
if imageConfig == nil {
|
||||
imageConfig = &distro.ImageConfig{}
|
||||
}
|
||||
return imageConfig.InheritFrom(t.arch.distro.getDefaultImageConfig())
|
||||
|
||||
}
|
||||
|
||||
func (t *imageType) PartitionType() string {
|
||||
basePartitionTable, exists := t.basePartitionTables[t.arch.Name()]
|
||||
if !exists {
|
||||
return ""
|
||||
}
|
||||
|
||||
return basePartitionTable.Type
|
||||
}
|
||||
|
||||
func (t *imageType) Manifest(bp *blueprint.Blueprint,
|
||||
options distro.ImageOptions,
|
||||
repos []rpmmd.RepoConfig,
|
||||
seed int64) (*manifest.Manifest, []string, error) {
|
||||
|
||||
warnings, err := t.checkOptions(bp, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// merge package sets that appear in the image type with the package sets
|
||||
// of the same name from the distro and arch
|
||||
staticPackageSets := make(map[string]rpmmd.PackageSet)
|
||||
|
||||
for name, getter := range t.packageSets {
|
||||
staticPackageSets[name] = getter(t)
|
||||
}
|
||||
|
||||
// amend with repository information and collect payload repos
|
||||
payloadRepos := make([]rpmmd.RepoConfig, 0)
|
||||
for _, repo := range repos {
|
||||
if len(repo.PackageSets) > 0 {
|
||||
// only apply the repo to the listed package sets
|
||||
for _, psName := range repo.PackageSets {
|
||||
if slices.Contains(t.PayloadPackageSets(), psName) {
|
||||
payloadRepos = append(payloadRepos, repo)
|
||||
}
|
||||
ps := staticPackageSets[psName]
|
||||
ps.Repositories = append(ps.Repositories, repo)
|
||||
staticPackageSets[psName] = ps
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w := t.workload
|
||||
if w == nil {
|
||||
cw := &workload.Custom{
|
||||
BaseWorkload: workload.BaseWorkload{
|
||||
Repos: payloadRepos,
|
||||
},
|
||||
Packages: bp.GetPackagesEx(false),
|
||||
}
|
||||
if services := bp.Customizations.GetServices(); services != nil {
|
||||
cw.Services = services.Enabled
|
||||
cw.DisabledServices = services.Disabled
|
||||
}
|
||||
w = cw
|
||||
}
|
||||
|
||||
containerSources := make([]container.SourceSpec, len(bp.Containers))
|
||||
for idx := range bp.Containers {
|
||||
containerSources[idx] = container.SourceSpec(bp.Containers[idx])
|
||||
}
|
||||
|
||||
source := rand.NewSource(seed)
|
||||
// math/rand is good enough in this case
|
||||
/* #nosec G404 */
|
||||
rng := rand.New(source)
|
||||
|
||||
img, err := t.image(w, t, bp.Customizations, options, staticPackageSets, containerSources, rng)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
mf := manifest.New()
|
||||
mf.Distro = manifest.DISTRO_FEDORA
|
||||
_, err = img.InstantiateManifest(&mf, repos, t.arch.distro.runner, rng)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &mf, warnings, err
|
||||
}
|
||||
|
||||
// checkOptions checks the validity and compatibility of options and customizations for the image type.
|
||||
// Returns ([]string, error) where []string, if non-nil, will hold any generated warnings (e.g. deprecation notices).
|
||||
func (t *imageType) checkOptions(bp *blueprint.Blueprint, options distro.ImageOptions) ([]string, error) {
|
||||
|
||||
customizations := bp.Customizations
|
||||
|
||||
// we do not support embedding containers on ostree-derived images, only on commits themselves
|
||||
if len(bp.Containers) > 0 && t.rpmOstree && (t.name != "iot-commit" && t.name != "iot-container") {
|
||||
return nil, fmt.Errorf("embedding containers is not supported for %s on %s", t.name, t.arch.distro.name)
|
||||
}
|
||||
|
||||
ostreeURL := ""
|
||||
if options.OSTree != nil {
|
||||
if options.OSTree.ParentRef != "" && options.OSTree.URL == "" {
|
||||
// specifying parent ref also requires URL
|
||||
return nil, ostree.NewParameterComboError("ostree parent ref specified, but no URL to retrieve it")
|
||||
}
|
||||
ostreeURL = options.OSTree.URL
|
||||
}
|
||||
|
||||
if t.bootISO && t.rpmOstree {
|
||||
// ostree-based ISOs require a URL from which to pull a payload commit
|
||||
if ostreeURL == "" {
|
||||
return nil, fmt.Errorf("boot ISO image type %q requires specifying a URL from which to retrieve the OSTree commit", t.name)
|
||||
}
|
||||
}
|
||||
|
||||
if t.name == "iot-raw-image" {
|
||||
allowed := []string{"User", "Group", "Directories", "Files", "Services"}
|
||||
if err := customizations.CheckAllowed(allowed...); err != nil {
|
||||
return nil, fmt.Errorf("unsupported blueprint customizations found for image type %q: (allowed: %s)", t.name, strings.Join(allowed, ", "))
|
||||
}
|
||||
// TODO: consider additional checks, such as those in "edge-simplified-installer" in RHEL distros
|
||||
}
|
||||
|
||||
// BootISO's have limited support for customizations.
|
||||
// TODO: Support kernel name selection for image-installer
|
||||
if t.bootISO {
|
||||
if t.name == "iot-installer" || t.name == "image-installer" {
|
||||
allowed := []string{"User", "Group"}
|
||||
if err := customizations.CheckAllowed(allowed...); err != nil {
|
||||
return nil, fmt.Errorf("unsupported blueprint customizations found for boot ISO image type %q: (allowed: %s)", t.name, strings.Join(allowed, ", "))
|
||||
}
|
||||
} else if t.name == "live-installer" {
|
||||
allowed := []string{}
|
||||
if err := customizations.CheckAllowed(allowed...); err != nil {
|
||||
return nil, fmt.Errorf("unsupported blueprint customizations found for boot ISO image type %q: (allowed: None)", t.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if kernelOpts := customizations.GetKernel(); kernelOpts.Append != "" && t.rpmOstree {
|
||||
return nil, fmt.Errorf("kernel boot parameter customizations are not supported for ostree types")
|
||||
}
|
||||
|
||||
mountpoints := customizations.GetFilesystems()
|
||||
|
||||
if mountpoints != nil && t.rpmOstree {
|
||||
return nil, fmt.Errorf("Custom mountpoints are not supported for ostree types")
|
||||
}
|
||||
|
||||
err := blueprint.CheckMountpointsPolicy(mountpoints, pathpolicy.MountpointPolicies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if osc := customizations.GetOpenSCAP(); osc != nil {
|
||||
supported := oscap.IsProfileAllowed(osc.ProfileID, oscapProfileAllowList)
|
||||
if !supported {
|
||||
return nil, fmt.Errorf(fmt.Sprintf("OpenSCAP unsupported profile: %s", osc.ProfileID))
|
||||
}
|
||||
if t.rpmOstree {
|
||||
return nil, fmt.Errorf("OpenSCAP customizations are not supported for ostree types")
|
||||
}
|
||||
if osc.ProfileID == "" {
|
||||
return nil, fmt.Errorf("OpenSCAP profile cannot be empty")
|
||||
}
|
||||
}
|
||||
|
||||
// Check Directory/File Customizations are valid
|
||||
dc := customizations.GetDirectories()
|
||||
fc := customizations.GetFiles()
|
||||
|
||||
err = blueprint.ValidateDirFileCustomizations(dc, fc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = blueprint.CheckDirectoryCustomizationsPolicy(dc, pathpolicy.CustomDirectoriesPolicies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = blueprint.CheckFileCustomizationsPolicy(fc, pathpolicy.CustomFilesPolicies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// check if repository customizations are valid
|
||||
_, err = customizations.GetRepositories()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -1,541 +0,0 @@
|
|||
package fedora
|
||||
|
||||
// This file defines package sets that are used by more than one image type.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
func qcow2CommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"@Fedora Cloud Server",
|
||||
"chrony", // not mentioned in the kickstart, anaconda pulls it when setting the timezone
|
||||
"langpacks-en",
|
||||
"qemu-guest-agent",
|
||||
},
|
||||
Exclude: []string{
|
||||
"dracut-config-rescue",
|
||||
"firewalld",
|
||||
"geolite2-city",
|
||||
"geolite2-country",
|
||||
"plymouth",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func vhdCommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"@core",
|
||||
"chrony",
|
||||
"langpacks-en",
|
||||
"net-tools",
|
||||
"ntfsprogs",
|
||||
"libxcrypt-compat",
|
||||
"initscripts",
|
||||
"glibc-all-langpacks",
|
||||
},
|
||||
Exclude: []string{
|
||||
"dracut-config-rescue",
|
||||
"geolite2-city",
|
||||
"geolite2-country",
|
||||
"zram-generator-defaults",
|
||||
},
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
func vmdkCommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"@Fedora Cloud Server",
|
||||
"chrony",
|
||||
"systemd-udev",
|
||||
"langpacks-en",
|
||||
"open-vm-tools",
|
||||
},
|
||||
Exclude: []string{
|
||||
"dracut-config-rescue",
|
||||
"etables",
|
||||
"firewalld",
|
||||
"geolite2-city",
|
||||
"geolite2-country",
|
||||
"gobject-introspection",
|
||||
"plymouth",
|
||||
"zram-generator-defaults",
|
||||
"grubby-deprecated",
|
||||
"extlinux-bootloader",
|
||||
},
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
// fedora iot commit OS package set
|
||||
func iotCommitPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"fedora-release-iot",
|
||||
"glibc",
|
||||
"glibc-minimal-langpack",
|
||||
"nss-altfiles",
|
||||
"sssd-client",
|
||||
"libsss_sudo",
|
||||
"shadow-utils",
|
||||
"dracut-network",
|
||||
"polkit",
|
||||
"lvm2",
|
||||
"cryptsetup",
|
||||
"pinentry",
|
||||
"keyutils",
|
||||
"cracklib-dicts",
|
||||
"e2fsprogs",
|
||||
"xfsprogs",
|
||||
"dosfstools",
|
||||
"gnupg2",
|
||||
"basesystem",
|
||||
"python3",
|
||||
"bash",
|
||||
"xz",
|
||||
"gzip",
|
||||
"coreutils",
|
||||
"which",
|
||||
"curl",
|
||||
"firewalld",
|
||||
"iptables",
|
||||
"NetworkManager",
|
||||
"NetworkManager-wifi",
|
||||
"NetworkManager-wwan",
|
||||
"wpa_supplicant",
|
||||
"iwd",
|
||||
"tpm2-pkcs11",
|
||||
"dnsmasq",
|
||||
"traceroute",
|
||||
"hostname",
|
||||
"iproute",
|
||||
"iputils",
|
||||
"openssh-clients",
|
||||
"openssh-server",
|
||||
"passwd",
|
||||
"policycoreutils",
|
||||
"procps-ng",
|
||||
"rootfiles",
|
||||
"rpm",
|
||||
"smartmontools-selinux",
|
||||
"setup",
|
||||
"shadow-utils",
|
||||
"sudo",
|
||||
"systemd",
|
||||
"util-linux",
|
||||
"vim-minimal",
|
||||
"less",
|
||||
"tar",
|
||||
"fwupd",
|
||||
"usbguard",
|
||||
"greenboot",
|
||||
"ignition",
|
||||
"zezere-ignition",
|
||||
"rsync",
|
||||
"attr",
|
||||
"ima-evm-utils",
|
||||
"bash-completion",
|
||||
"tmux",
|
||||
"screen",
|
||||
"policycoreutils-python-utils",
|
||||
"setools-console",
|
||||
"audit",
|
||||
"rng-tools",
|
||||
"chrony",
|
||||
"bluez",
|
||||
"bluez-libs",
|
||||
"bluez-mesh",
|
||||
"kernel-tools",
|
||||
"libgpiod-utils",
|
||||
"podman",
|
||||
"container-selinux",
|
||||
"skopeo",
|
||||
"criu",
|
||||
"slirp4netns",
|
||||
"fuse-overlayfs",
|
||||
"clevis",
|
||||
"clevis-dracut",
|
||||
"clevis-luks",
|
||||
"clevis-pin-tpm2",
|
||||
"parsec",
|
||||
"dbus-parsec",
|
||||
"iwl7260-firmware",
|
||||
"iwlax2xx-firmware",
|
||||
"greenboot-default-health-checks",
|
||||
},
|
||||
}
|
||||
|
||||
return ps
|
||||
|
||||
}
|
||||
|
||||
// INSTALLER PACKAGE SET
|
||||
|
||||
func installerPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"anaconda-dracut",
|
||||
"curl",
|
||||
"dracut-config-generic",
|
||||
"dracut-network",
|
||||
"hostname",
|
||||
"iwl100-firmware",
|
||||
"iwl1000-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6050-firmware",
|
||||
"iwl7260-firmware",
|
||||
"kernel",
|
||||
"less",
|
||||
"nfs-utils",
|
||||
"openssh-clients",
|
||||
"ostree",
|
||||
"plymouth",
|
||||
"rng-tools",
|
||||
"rpcbind",
|
||||
"selinux-policy-targeted",
|
||||
"systemd",
|
||||
"tar",
|
||||
"xfsprogs",
|
||||
"xz",
|
||||
},
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
func anacondaPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
|
||||
// common installer packages
|
||||
ps := installerPackageSet(t)
|
||||
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"aajohan-comfortaa-fonts",
|
||||
"abattis-cantarell-fonts",
|
||||
"alsa-firmware",
|
||||
"alsa-tools-firmware",
|
||||
"anaconda",
|
||||
"anaconda-dracut",
|
||||
"anaconda-install-env-deps",
|
||||
"anaconda-widgets",
|
||||
"audit",
|
||||
"bind-utils",
|
||||
"bitmap-fangsongti-fonts",
|
||||
"bzip2",
|
||||
"cryptsetup",
|
||||
"curl",
|
||||
"dbus-x11",
|
||||
"dejavu-sans-fonts",
|
||||
"dejavu-sans-mono-fonts",
|
||||
"device-mapper-persistent-data",
|
||||
"dmidecode",
|
||||
"dnf",
|
||||
"dracut-config-generic",
|
||||
"dracut-network",
|
||||
"efibootmgr",
|
||||
"ethtool",
|
||||
"fcoe-utils",
|
||||
"ftp",
|
||||
"gdb-gdbserver",
|
||||
"gdisk",
|
||||
"glibc-all-langpacks",
|
||||
"gnome-kiosk",
|
||||
"google-noto-sans-cjk-ttc-fonts",
|
||||
"grub2-tools",
|
||||
"grub2-tools-extra",
|
||||
"grub2-tools-minimal",
|
||||
"grubby",
|
||||
"gsettings-desktop-schemas",
|
||||
"hdparm",
|
||||
"hexedit",
|
||||
"hostname",
|
||||
"initscripts",
|
||||
"ipmitool",
|
||||
"iwl1000-firmware",
|
||||
"iwl100-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6000g2a-firmware",
|
||||
"iwl6000g2b-firmware",
|
||||
"iwl6050-firmware",
|
||||
"iwl7260-firmware",
|
||||
"jomolhari-fonts",
|
||||
"kacst-farsi-fonts",
|
||||
"kacst-qurn-fonts",
|
||||
"kbd",
|
||||
"kbd-misc",
|
||||
"kdump-anaconda-addon",
|
||||
"kernel",
|
||||
"khmeros-base-fonts",
|
||||
"less",
|
||||
"libblockdev-lvm-dbus",
|
||||
"libibverbs",
|
||||
"libreport-plugin-bugzilla",
|
||||
"libreport-plugin-reportuploader",
|
||||
"librsvg2",
|
||||
"linux-firmware",
|
||||
"lldpad",
|
||||
"lohit-assamese-fonts",
|
||||
"lohit-bengali-fonts",
|
||||
"lohit-devanagari-fonts",
|
||||
"lohit-gujarati-fonts",
|
||||
"lohit-gurmukhi-fonts",
|
||||
"lohit-kannada-fonts",
|
||||
"lohit-odia-fonts",
|
||||
"lohit-tamil-fonts",
|
||||
"lohit-telugu-fonts",
|
||||
"lsof",
|
||||
"madan-fonts",
|
||||
"mtr",
|
||||
"mt-st",
|
||||
"net-tools",
|
||||
"nfs-utils",
|
||||
"nmap-ncat",
|
||||
"nm-connection-editor",
|
||||
"nss-tools",
|
||||
"openssh-clients",
|
||||
"openssh-server",
|
||||
"oscap-anaconda-addon",
|
||||
"ostree",
|
||||
"pciutils",
|
||||
"perl-interpreter",
|
||||
"pigz",
|
||||
"plymouth",
|
||||
"python3-pyatspi",
|
||||
"rdma-core",
|
||||
"rit-meera-new-fonts",
|
||||
"rng-tools",
|
||||
"rpcbind",
|
||||
"rpm-ostree",
|
||||
"rsync",
|
||||
"rsyslog",
|
||||
"selinux-policy-targeted",
|
||||
"sg3_utils",
|
||||
"sil-abyssinica-fonts",
|
||||
"sil-padauk-fonts",
|
||||
"sil-scheherazade-new-fonts",
|
||||
"smartmontools",
|
||||
"spice-vdagent",
|
||||
"strace",
|
||||
"systemd",
|
||||
"tar",
|
||||
"thai-scalable-waree-fonts",
|
||||
"tigervnc-server-minimal",
|
||||
"tigervnc-server-module",
|
||||
"udisks2",
|
||||
"udisks2-iscsi",
|
||||
"usbutils",
|
||||
"vim-minimal",
|
||||
"volume_key",
|
||||
"wget",
|
||||
"xfsdump",
|
||||
"xfsprogs",
|
||||
"xorg-x11-drivers",
|
||||
"xorg-x11-fonts-misc",
|
||||
"xorg-x11-server-Xorg",
|
||||
"xorg-x11-xauth",
|
||||
"metacity",
|
||||
"xrdb",
|
||||
"xz",
|
||||
},
|
||||
})
|
||||
|
||||
if common.VersionLessThan(t.arch.distro.osVersion, "39") {
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"lklug-fonts", // orphaned, unavailable in F39
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
switch t.Arch().Name() {
|
||||
case platform.ARCH_X86_64.String():
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"biosdevname",
|
||||
"dmidecode",
|
||||
"grub2-tools-efi",
|
||||
"memtest86+",
|
||||
},
|
||||
})
|
||||
|
||||
case platform.ARCH_AARCH64.String():
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"dmidecode",
|
||||
},
|
||||
})
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported arch: %s", t.Arch().Name()))
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
func iotInstallerPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
// include anaconda packages
|
||||
ps := anacondaPackageSet(t)
|
||||
|
||||
releasever := t.Arch().Distro().Releasever()
|
||||
version, err := strconv.Atoi(releasever)
|
||||
if err != nil {
|
||||
panic("cannot convert releasever to int: " + err.Error())
|
||||
}
|
||||
|
||||
if version >= 38 {
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"fedora-release-iot",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
func liveInstallerPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"@workstation-product-environment",
|
||||
"@anaconda-tools",
|
||||
"anaconda-install-env-deps",
|
||||
"anaconda-live",
|
||||
"anaconda-dracut",
|
||||
"dracut-live",
|
||||
"glibc-all-langpacks",
|
||||
"kernel",
|
||||
"kernel-modules",
|
||||
"kernel-modules-extra",
|
||||
"livesys-scripts",
|
||||
"rng-tools",
|
||||
"rdma-core",
|
||||
"gnome-kiosk",
|
||||
},
|
||||
Exclude: []string{
|
||||
"@dial-up",
|
||||
"@input-methods",
|
||||
"@standard",
|
||||
"device-mapper-multipath",
|
||||
"fcoe-utils",
|
||||
"gfs2-utils",
|
||||
"reiserfs-utils",
|
||||
},
|
||||
}
|
||||
|
||||
// We want to generate a preview image when rawhide is built
|
||||
if !common.VersionLessThan(t.arch.distro.osVersion, "39") {
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"anaconda-webui",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
func imageInstallerPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := anacondaPackageSet(t)
|
||||
|
||||
releasever := t.Arch().Distro().Releasever()
|
||||
version, err := strconv.Atoi(releasever)
|
||||
if err != nil {
|
||||
panic("cannot convert releasever to int: " + err.Error())
|
||||
}
|
||||
|
||||
// We want to generate a preview image when rawhide is built
|
||||
if version >= 38 {
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"anaconda-webui",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
func containerPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"bash",
|
||||
"coreutils",
|
||||
"dnf-yum",
|
||||
"dnf",
|
||||
"fedora-release-container",
|
||||
"fedora-repos-modular",
|
||||
"glibc-minimal-langpack",
|
||||
"rootfiles",
|
||||
"rpm",
|
||||
"sudo",
|
||||
"tar",
|
||||
"util-linux-core",
|
||||
"vim-minimal",
|
||||
},
|
||||
Exclude: []string{
|
||||
"crypto-policies-scripts",
|
||||
"dbus-broker",
|
||||
"deltarpm",
|
||||
"dosfstools",
|
||||
"e2fsprogs",
|
||||
"elfutils-debuginfod-client",
|
||||
"fuse-libs",
|
||||
"gawk-all-langpacks",
|
||||
"glibc-gconv-extra",
|
||||
"glibc-langpack-en",
|
||||
"gnupg2-smime",
|
||||
"grubby",
|
||||
"kernel-core",
|
||||
"kernel-debug-core",
|
||||
"kernel",
|
||||
"langpacks-en_GB",
|
||||
"langpacks-en",
|
||||
"libss",
|
||||
"libxcrypt-compat",
|
||||
"nano",
|
||||
"openssl-pkcs11",
|
||||
"pinentry",
|
||||
"python3-unbound",
|
||||
"shared-mime-info",
|
||||
"sssd-client",
|
||||
"sudo-python-plugin",
|
||||
"systemd",
|
||||
"trousers",
|
||||
"whois-nls",
|
||||
"xkeyboard-config",
|
||||
},
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
func minimalrpmPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"@core",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
package fedora
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/disk"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
)
|
||||
|
||||
var defaultBasePartitionTables = distro.BasePartitionTableMap{
|
||||
platform.ARCH_X86_64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 1 * common.MebiByte, // 1MB
|
||||
Bootable: true,
|
||||
Type: disk.BIOSBootPartitionGUID,
|
||||
UUID: disk.BIOSBootPartitionUUID,
|
||||
},
|
||||
{
|
||||
Size: 200 * common.MebiByte, // 200 MB
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
Label: "EFI-SYSTEM",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 500 * common.MebiByte, // 500 MB
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.FilesystemDataUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "ext4",
|
||||
Mountpoint: "/boot",
|
||||
Label: "boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte, // 2GiB
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "ext4",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
platform.ARCH_AARCH64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 200 * common.MebiByte, // 200 MB
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
Label: "EFI-SYSTEM",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 500 * common.MebiByte, // 500 MB
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.FilesystemDataUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "ext4",
|
||||
Mountpoint: "/boot",
|
||||
Label: "boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte, // 2GiB
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "ext4",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var iotBasePartitionTables = distro.BasePartitionTableMap{
|
||||
platform.ARCH_X86_64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 501 * common.MebiByte, // 501 MiB
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
Label: "EFI-SYSTEM",
|
||||
FSTabOptions: "umask=0077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 1 * common.GibiByte, // 1 GiB
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.FilesystemDataUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "ext4",
|
||||
Mountpoint: "/boot",
|
||||
Label: "boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 1,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2569 * common.MebiByte, // 2.5 GiB
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "ext4",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 1,
|
||||
FSTabPassNo: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
platform.ARCH_AARCH64.String(): disk.PartitionTable{
|
||||
UUID: "0xc1748067",
|
||||
Type: "dos",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 501 * common.MebiByte, // 501 MiB
|
||||
Type: "06",
|
||||
Bootable: true,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
Label: "EFI-SYSTEM",
|
||||
FSTabOptions: "umask=0077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 1 * common.GibiByte, // 1 GiB
|
||||
Type: "83",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "ext4",
|
||||
Mountpoint: "/boot",
|
||||
Label: "boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 1,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2569 * common.MebiByte, // 2.5 GiB
|
||||
Type: "83",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "ext4",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 1,
|
||||
FSTabPassNo: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
package distro
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/shell"
|
||||
"github.com/osbuild/osbuild-composer/internal/subscription"
|
||||
)
|
||||
|
||||
// ImageConfig represents a (default) configuration applied to the image
|
||||
type ImageConfig struct {
|
||||
Timezone *string
|
||||
TimeSynchronization *osbuild.ChronyStageOptions
|
||||
Locale *string
|
||||
Keyboard *osbuild.KeymapStageOptions
|
||||
EnabledServices []string
|
||||
DisabledServices []string
|
||||
DefaultTarget *string
|
||||
Sysconfig []*osbuild.SysconfigStageOptions
|
||||
|
||||
// List of files from which to import GPG keys into the RPM database
|
||||
GPGKeyFiles []string
|
||||
|
||||
// Disable SELinux labelling
|
||||
NoSElinux *bool
|
||||
|
||||
// Do not use. Forces auto-relabelling on first boot.
|
||||
// See https://github.com/osbuild/osbuild/commit/52cb27631b587c1df177cd17625c5b473e1e85d2
|
||||
SELinuxForceRelabel *bool
|
||||
|
||||
// Disable documentation
|
||||
ExcludeDocs *bool
|
||||
|
||||
ShellInit []shell.InitFile
|
||||
|
||||
// for RHSM configuration, we need to potentially distinguish the case
|
||||
// when the user want the image to be subscribed on first boot and when not
|
||||
RHSMConfig map[subscription.RHSMStatus]*osbuild.RHSMStageOptions
|
||||
SystemdLogind []*osbuild.SystemdLogindStageOptions
|
||||
CloudInit []*osbuild.CloudInitStageOptions
|
||||
Modprobe []*osbuild.ModprobeStageOptions
|
||||
DracutConf []*osbuild.DracutConfStageOptions
|
||||
SystemdUnit []*osbuild.SystemdUnitStageOptions
|
||||
Authselect *osbuild.AuthselectStageOptions
|
||||
SELinuxConfig *osbuild.SELinuxConfigStageOptions
|
||||
Tuned *osbuild.TunedStageOptions
|
||||
Tmpfilesd []*osbuild.TmpfilesdStageOptions
|
||||
PamLimitsConf []*osbuild.PamLimitsConfStageOptions
|
||||
Sysctld []*osbuild.SysctldStageOptions
|
||||
DNFConfig []*osbuild.DNFConfigStageOptions
|
||||
SshdConfig *osbuild.SshdConfigStageOptions
|
||||
Authconfig *osbuild.AuthconfigStageOptions
|
||||
PwQuality *osbuild.PwqualityConfStageOptions
|
||||
WAAgentConfig *osbuild.WAAgentConfStageOptions
|
||||
Grub2Config *osbuild.GRUB2Config
|
||||
DNFAutomaticConfig *osbuild.DNFAutomaticConfigStageOptions
|
||||
YumConfig *osbuild.YumConfigStageOptions
|
||||
YUMRepos []*osbuild.YumReposStageOptions
|
||||
Firewall *osbuild.FirewallStageOptions
|
||||
UdevRules *osbuild.UdevRulesStageOptions
|
||||
GCPGuestAgentConfig *osbuild.GcpGuestAgentConfigOptions
|
||||
}
|
||||
|
||||
// InheritFrom inherits unset values from the provided parent configuration and
|
||||
// returns a new structure instance, which is a result of the inheritance.
|
||||
func (c *ImageConfig) InheritFrom(parentConfig *ImageConfig) *ImageConfig {
|
||||
finalConfig := ImageConfig(*c)
|
||||
if parentConfig != nil {
|
||||
// iterate over all struct fields and copy unset values from the parent
|
||||
for i := 0; i < reflect.TypeOf(*c).NumField(); i++ {
|
||||
fieldName := reflect.TypeOf(*c).Field(i).Name
|
||||
field := reflect.ValueOf(&finalConfig).Elem().FieldByName(fieldName)
|
||||
|
||||
// Only container types or pointer are supported.
|
||||
// The reason is that with basic types, we can't distinguish between unset value and zero value.
|
||||
if kind := field.Kind(); kind != reflect.Ptr && kind != reflect.Slice && kind != reflect.Map {
|
||||
panic(fmt.Sprintf("unsupported field type: %s (only container types or pointer are supported)",
|
||||
field.Kind()))
|
||||
}
|
||||
|
||||
if field.IsNil() {
|
||||
field.Set(reflect.ValueOf(parentConfig).Elem().FieldByName(fieldName))
|
||||
}
|
||||
}
|
||||
}
|
||||
return &finalConfig
|
||||
}
|
||||
|
|
@ -1,218 +0,0 @@
|
|||
package distro
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
)
|
||||
|
||||
func TestImageConfigInheritFrom(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
distroConfig *ImageConfig
|
||||
imageConfig *ImageConfig
|
||||
expectedConfig *ImageConfig
|
||||
}{
|
||||
{
|
||||
name: "inheritance with overridden values",
|
||||
distroConfig: &ImageConfig{
|
||||
Timezone: common.ToPtr("America/New_York"),
|
||||
TimeSynchronization: &osbuild.ChronyStageOptions{
|
||||
Servers: []osbuild.ChronyConfigServer{{Hostname: "127.0.0.1"}},
|
||||
},
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
Keyboard: &osbuild.KeymapStageOptions{
|
||||
Keymap: "us",
|
||||
},
|
||||
EnabledServices: []string{"sshd"},
|
||||
DisabledServices: []string{"named"},
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
Sysconfig: []*osbuild.SysconfigStageOptions{
|
||||
{
|
||||
Kernel: &osbuild.SysconfigKernelOptions{
|
||||
UpdateDefault: true,
|
||||
DefaultKernel: "kernel",
|
||||
},
|
||||
Network: &osbuild.SysconfigNetworkOptions{
|
||||
Networking: true,
|
||||
NoZeroConf: true,
|
||||
},
|
||||
NetworkScripts: &osbuild.NetworkScriptsOptions{
|
||||
IfcfgFiles: map[string]osbuild.IfcfgFile{
|
||||
"eth0": {
|
||||
Device: "eth0",
|
||||
Bootproto: osbuild.IfcfgBootprotoDHCP,
|
||||
OnBoot: common.ToPtr(true),
|
||||
Type: osbuild.IfcfgTypeEthernet,
|
||||
UserCtl: common.ToPtr(true),
|
||||
PeerDNS: common.ToPtr(true),
|
||||
IPv6Init: common.ToPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
imageConfig: &ImageConfig{
|
||||
Timezone: common.ToPtr("UTC"),
|
||||
TimeSynchronization: &osbuild.ChronyStageOptions{
|
||||
Servers: []osbuild.ChronyConfigServer{
|
||||
{
|
||||
Hostname: "169.254.169.123",
|
||||
Prefer: common.ToPtr(true),
|
||||
Iburst: common.ToPtr(true),
|
||||
Minpoll: common.ToPtr(4),
|
||||
Maxpoll: common.ToPtr(4),
|
||||
},
|
||||
},
|
||||
LeapsecTz: common.ToPtr(""),
|
||||
},
|
||||
},
|
||||
expectedConfig: &ImageConfig{
|
||||
Timezone: common.ToPtr("UTC"),
|
||||
TimeSynchronization: &osbuild.ChronyStageOptions{
|
||||
Servers: []osbuild.ChronyConfigServer{
|
||||
{
|
||||
Hostname: "169.254.169.123",
|
||||
Prefer: common.ToPtr(true),
|
||||
Iburst: common.ToPtr(true),
|
||||
Minpoll: common.ToPtr(4),
|
||||
Maxpoll: common.ToPtr(4),
|
||||
},
|
||||
},
|
||||
LeapsecTz: common.ToPtr(""),
|
||||
},
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
Keyboard: &osbuild.KeymapStageOptions{
|
||||
Keymap: "us",
|
||||
},
|
||||
EnabledServices: []string{"sshd"},
|
||||
DisabledServices: []string{"named"},
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
Sysconfig: []*osbuild.SysconfigStageOptions{
|
||||
{
|
||||
Kernel: &osbuild.SysconfigKernelOptions{
|
||||
UpdateDefault: true,
|
||||
DefaultKernel: "kernel",
|
||||
},
|
||||
Network: &osbuild.SysconfigNetworkOptions{
|
||||
Networking: true,
|
||||
NoZeroConf: true,
|
||||
},
|
||||
NetworkScripts: &osbuild.NetworkScriptsOptions{
|
||||
IfcfgFiles: map[string]osbuild.IfcfgFile{
|
||||
"eth0": {
|
||||
Device: "eth0",
|
||||
Bootproto: osbuild.IfcfgBootprotoDHCP,
|
||||
OnBoot: common.ToPtr(true),
|
||||
Type: osbuild.IfcfgTypeEthernet,
|
||||
UserCtl: common.ToPtr(true),
|
||||
PeerDNS: common.ToPtr(true),
|
||||
IPv6Init: common.ToPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty image type configuration",
|
||||
distroConfig: &ImageConfig{
|
||||
Timezone: common.ToPtr("America/New_York"),
|
||||
TimeSynchronization: &osbuild.ChronyStageOptions{
|
||||
Servers: []osbuild.ChronyConfigServer{{Hostname: "127.0.0.1"}},
|
||||
},
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
Keyboard: &osbuild.KeymapStageOptions{
|
||||
Keymap: "us",
|
||||
},
|
||||
EnabledServices: []string{"sshd"},
|
||||
DisabledServices: []string{"named"},
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
},
|
||||
imageConfig: &ImageConfig{},
|
||||
expectedConfig: &ImageConfig{
|
||||
Timezone: common.ToPtr("America/New_York"),
|
||||
TimeSynchronization: &osbuild.ChronyStageOptions{
|
||||
Servers: []osbuild.ChronyConfigServer{{Hostname: "127.0.0.1"}},
|
||||
},
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
Keyboard: &osbuild.KeymapStageOptions{
|
||||
Keymap: "us",
|
||||
},
|
||||
EnabledServices: []string{"sshd"},
|
||||
DisabledServices: []string{"named"},
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty distro configuration",
|
||||
distroConfig: &ImageConfig{},
|
||||
imageConfig: &ImageConfig{
|
||||
Timezone: common.ToPtr("America/New_York"),
|
||||
TimeSynchronization: &osbuild.ChronyStageOptions{
|
||||
Servers: []osbuild.ChronyConfigServer{{Hostname: "127.0.0.1"}},
|
||||
},
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
Keyboard: &osbuild.KeymapStageOptions{
|
||||
Keymap: "us",
|
||||
},
|
||||
EnabledServices: []string{"sshd"},
|
||||
DisabledServices: []string{"named"},
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
},
|
||||
expectedConfig: &ImageConfig{
|
||||
Timezone: common.ToPtr("America/New_York"),
|
||||
TimeSynchronization: &osbuild.ChronyStageOptions{
|
||||
Servers: []osbuild.ChronyConfigServer{{Hostname: "127.0.0.1"}},
|
||||
},
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
Keyboard: &osbuild.KeymapStageOptions{
|
||||
Keymap: "us",
|
||||
},
|
||||
EnabledServices: []string{"sshd"},
|
||||
DisabledServices: []string{"named"},
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty distro configuration",
|
||||
distroConfig: nil,
|
||||
imageConfig: &ImageConfig{
|
||||
Timezone: common.ToPtr("America/New_York"),
|
||||
TimeSynchronization: &osbuild.ChronyStageOptions{
|
||||
Servers: []osbuild.ChronyConfigServer{{Hostname: "127.0.0.1"}},
|
||||
},
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
Keyboard: &osbuild.KeymapStageOptions{
|
||||
Keymap: "us",
|
||||
},
|
||||
EnabledServices: []string{"sshd"},
|
||||
DisabledServices: []string{"named"},
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
},
|
||||
expectedConfig: &ImageConfig{
|
||||
Timezone: common.ToPtr("America/New_York"),
|
||||
TimeSynchronization: &osbuild.ChronyStageOptions{
|
||||
Servers: []osbuild.ChronyConfigServer{{Hostname: "127.0.0.1"}},
|
||||
},
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
Keyboard: &osbuild.KeymapStageOptions{
|
||||
Keymap: "us",
|
||||
},
|
||||
EnabledServices: []string{"sshd"},
|
||||
DisabledServices: []string{"named"},
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
},
|
||||
},
|
||||
}
|
||||
for idx, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equalf(t, tt.expectedConfig, tt.imageConfig.InheritFrom(tt.distroConfig), "test case %q failed (idx %d)", tt.name, idx)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,384 +0,0 @@
|
|||
package rhel7
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/disk"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/subscription"
|
||||
)
|
||||
|
||||
var azureRhuiImgType = imageType{
|
||||
name: "azure-rhui",
|
||||
filename: "disk.vhd.xz",
|
||||
mimeType: "application/xz",
|
||||
compression: "xz",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: azureRhuiCommonPackageSet,
|
||||
},
|
||||
packageSetChains: map[string][]string{
|
||||
osPkgsKey: {osPkgsKey, blueprintPkgsKey},
|
||||
},
|
||||
defaultImageConfig: azureDefaultImgConfig,
|
||||
kernelOptions: "ro crashkernel=auto console=tty1 console=ttyS0 earlyprintk=ttyS0 rootdelay=300 scsi_mod.use_blk_mq=y",
|
||||
bootable: true,
|
||||
defaultSize: 64 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "vpc", "xz"},
|
||||
exports: []string{"xz"},
|
||||
basePartitionTables: azureRhuiBasePartitionTables,
|
||||
}
|
||||
|
||||
var azureDefaultImgConfig = &distro.ImageConfig{
|
||||
Timezone: common.ToPtr("Etc/UTC"),
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
GPGKeyFiles: []string{
|
||||
"/etc/pki/rpm-gpg/RPM-GPG-KEY-microsoft-azure-release",
|
||||
"/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release",
|
||||
},
|
||||
SELinuxForceRelabel: common.ToPtr(true),
|
||||
Authconfig: &osbuild.AuthconfigStageOptions{},
|
||||
Sysconfig: []*osbuild.SysconfigStageOptions{
|
||||
{
|
||||
Kernel: &osbuild.SysconfigKernelOptions{
|
||||
UpdateDefault: true,
|
||||
DefaultKernel: "kernel-core",
|
||||
},
|
||||
Network: &osbuild.SysconfigNetworkOptions{
|
||||
Networking: true,
|
||||
NoZeroConf: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
EnabledServices: []string{
|
||||
"cloud-config",
|
||||
"cloud-final",
|
||||
"cloud-init-local",
|
||||
"cloud-init",
|
||||
"firewalld",
|
||||
"NetworkManager",
|
||||
"sshd",
|
||||
"waagent",
|
||||
},
|
||||
SshdConfig: &osbuild.SshdConfigStageOptions{
|
||||
Config: osbuild.SshdConfigConfig{
|
||||
ClientAliveInterval: common.ToPtr(180),
|
||||
},
|
||||
},
|
||||
Modprobe: []*osbuild.ModprobeStageOptions{
|
||||
{
|
||||
Filename: "blacklist-amdgpu.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("amdgpu"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Filename: "blacklist-intel-cstate.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("intel_cstate"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Filename: "blacklist-floppy.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("floppy"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Filename: "blacklist-nouveau.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("nouveau"),
|
||||
osbuild.NewModprobeConfigCmdBlacklist("lbm-nouveau"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Filename: "blacklist-skylake-edac.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("skx_edac"),
|
||||
},
|
||||
},
|
||||
},
|
||||
CloudInit: []*osbuild.CloudInitStageOptions{
|
||||
{
|
||||
Filename: "06_logging_override.cfg",
|
||||
Config: osbuild.CloudInitConfigFile{
|
||||
Output: &osbuild.CloudInitConfigOutput{
|
||||
All: common.ToPtr("| tee -a /var/log/cloud-init-output.log"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Filename: "10-azure-kvp.cfg",
|
||||
Config: osbuild.CloudInitConfigFile{
|
||||
Reporting: &osbuild.CloudInitConfigReporting{
|
||||
Logging: &osbuild.CloudInitConfigReportingHandlers{
|
||||
Type: "log",
|
||||
},
|
||||
Telemetry: &osbuild.CloudInitConfigReportingHandlers{
|
||||
Type: "hyperv",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Filename: "91-azure_datasource.cfg",
|
||||
Config: osbuild.CloudInitConfigFile{
|
||||
Datasource: &osbuild.CloudInitConfigDatasource{
|
||||
Azure: &osbuild.CloudInitConfigDatasourceAzure{
|
||||
ApplyNetworkConfig: false,
|
||||
},
|
||||
},
|
||||
DatasourceList: []string{
|
||||
"Azure",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
PwQuality: &osbuild.PwqualityConfStageOptions{
|
||||
Config: osbuild.PwqualityConfConfig{
|
||||
Minlen: common.ToPtr(6),
|
||||
Minclass: common.ToPtr(3),
|
||||
Dcredit: common.ToPtr(0),
|
||||
Ucredit: common.ToPtr(0),
|
||||
Lcredit: common.ToPtr(0),
|
||||
Ocredit: common.ToPtr(0),
|
||||
},
|
||||
},
|
||||
WAAgentConfig: &osbuild.WAAgentConfStageOptions{
|
||||
Config: osbuild.WAAgentConfig{
|
||||
RDFormat: common.ToPtr(false),
|
||||
RDEnableSwap: common.ToPtr(false),
|
||||
},
|
||||
},
|
||||
RHSMConfig: map[subscription.RHSMStatus]*osbuild.RHSMStageOptions{
|
||||
subscription.RHSMConfigNoSubscription: {
|
||||
YumPlugins: &osbuild.RHSMStageOptionsDnfPlugins{
|
||||
SubscriptionManager: &osbuild.RHSMStageOptionsDnfPlugin{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
Rhsm: &osbuild.SubManConfigRHSMSection{
|
||||
ManageRepos: common.ToPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
subscription.RHSMConfigWithSubscription: {
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
// do not disable the redhat.repo management if the user
|
||||
// explicitly request the system to be subscribed
|
||||
},
|
||||
},
|
||||
},
|
||||
Grub2Config: &osbuild.GRUB2Config{
|
||||
TerminalInput: []string{"serial", "console"},
|
||||
TerminalOutput: []string{"serial", "console"},
|
||||
Serial: "serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1",
|
||||
Timeout: 10,
|
||||
},
|
||||
UdevRules: &osbuild.UdevRulesStageOptions{
|
||||
Filename: "/etc/udev/rules.d/68-azure-sriov-nm-unmanaged.rules",
|
||||
Rules: osbuild.UdevRules{
|
||||
osbuild.UdevRuleComment{
|
||||
Comment: []string{
|
||||
"Accelerated Networking on Azure exposes a new SRIOV interface to the VM.",
|
||||
"This interface is transparently bonded to the synthetic interface,",
|
||||
"so NetworkManager should just ignore any SRIOV interfaces.",
|
||||
},
|
||||
},
|
||||
osbuild.NewUdevRule(
|
||||
[]osbuild.UdevKV{
|
||||
{K: "SUBSYSTEM", O: "==", V: "net"},
|
||||
{K: "DRIVERS", O: "==", V: "hv_pci"},
|
||||
{K: "ACTION", O: "==", V: "add"},
|
||||
{K: "ENV", A: "NM_UNMANAGED", O: "=", V: "1"},
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
YumConfig: &osbuild.YumConfigStageOptions{
|
||||
Config: &osbuild.YumConfigConfig{
|
||||
HttpCaching: common.ToPtr("packages"),
|
||||
},
|
||||
Plugins: &osbuild.YumConfigPlugins{
|
||||
Langpacks: &osbuild.YumConfigPluginsLangpacks{
|
||||
Locales: []string{"en_US.UTF-8"},
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
}
|
||||
|
||||
func azureRhuiCommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"@base",
|
||||
"@core",
|
||||
"authconfig",
|
||||
"bpftool",
|
||||
"bzip2",
|
||||
"chrony",
|
||||
"cloud-init",
|
||||
"cloud-utils-growpart",
|
||||
"dracut-config-generic",
|
||||
"dracut-norescue",
|
||||
"efibootmgr",
|
||||
"firewalld",
|
||||
"gdisk",
|
||||
"grub2-efi-x64",
|
||||
"grub2-pc",
|
||||
"grub2",
|
||||
"hyperv-daemons",
|
||||
"kernel",
|
||||
"lvm2",
|
||||
"redhat-release-eula",
|
||||
"redhat-support-tool",
|
||||
"rh-dotnetcore11",
|
||||
"rhn-setup",
|
||||
"rhui-azure-rhel7",
|
||||
"rsync",
|
||||
"shim-x64",
|
||||
"tar",
|
||||
"tcpdump",
|
||||
"WALinuxAgent",
|
||||
"yum-rhn-plugin",
|
||||
"yum-utils",
|
||||
},
|
||||
Exclude: []string{
|
||||
"dracut-config-rescue",
|
||||
"mariadb-libs",
|
||||
"NetworkManager-config-server",
|
||||
"postfix",
|
||||
},
|
||||
}
|
||||
|
||||
if t.arch.distro.isRHEL() {
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"insights-client",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
var azureRhuiBasePartitionTables = distro.BasePartitionTableMap{
|
||||
platform.ARCH_X86_64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Size: 64 * common.GibiByte,
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 500 * common.MebiByte,
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 500 * common.MebiByte,
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.FilesystemDataUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.MebiByte,
|
||||
Bootable: true,
|
||||
Type: disk.BIOSBootPartitionGUID,
|
||||
UUID: disk.BIOSBootPartitionUUID,
|
||||
},
|
||||
{
|
||||
Type: disk.LVMPartitionGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.LVMVolumeGroup{
|
||||
Name: "rootvg",
|
||||
Description: "built with lvm2 and osbuild",
|
||||
LogicalVolumes: []disk.LVMLogicalVolume{
|
||||
{
|
||||
Size: 1 * common.GibiByte,
|
||||
Name: "homelv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "home",
|
||||
Mountpoint: "/home",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte,
|
||||
Name: "rootlv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte,
|
||||
Name: "tmplv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "tmp",
|
||||
Mountpoint: "/tmp",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 10 * common.GibiByte,
|
||||
Name: "usrlv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "usr",
|
||||
Mountpoint: "/usr",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 10 * common.GibiByte, // firedrill: 8 GB
|
||||
Name: "varlv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "var",
|
||||
Mountpoint: "/var",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -1,235 +0,0 @@
|
|||
package rhel7
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/runner"
|
||||
)
|
||||
|
||||
const (
|
||||
// package set names
|
||||
|
||||
// main/common os image package set name
|
||||
osPkgsKey = "os"
|
||||
|
||||
// blueprint package set name
|
||||
blueprintPkgsKey = "blueprint"
|
||||
)
|
||||
|
||||
// RHEL-based OS image configuration defaults
|
||||
var defaultDistroImageConfig = &distro.ImageConfig{
|
||||
Timezone: common.ToPtr("America/New_York"),
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
GPGKeyFiles: []string{
|
||||
"/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release",
|
||||
},
|
||||
Sysconfig: []*osbuild.SysconfigStageOptions{
|
||||
{
|
||||
Kernel: &osbuild.SysconfigKernelOptions{
|
||||
UpdateDefault: true,
|
||||
DefaultKernel: "kernel",
|
||||
},
|
||||
Network: &osbuild.SysconfigNetworkOptions{
|
||||
Networking: true,
|
||||
NoZeroConf: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// distribution objects without the arches > image types
|
||||
var distroMap = map[string]distribution{
|
||||
"rhel-7": {
|
||||
name: "rhel-7",
|
||||
product: "Red Hat Enterprise Linux",
|
||||
osVersion: "7.9",
|
||||
nick: "Maipo",
|
||||
releaseVersion: "7",
|
||||
modulePlatformID: "platform:el7",
|
||||
vendor: "redhat",
|
||||
runner: &runner.RHEL{Major: uint64(7), Minor: uint64(9)},
|
||||
defaultImageConfig: defaultDistroImageConfig,
|
||||
},
|
||||
}
|
||||
|
||||
// --- Distribution ---
|
||||
type distribution struct {
|
||||
name string
|
||||
product string
|
||||
nick string
|
||||
osVersion string
|
||||
releaseVersion string
|
||||
modulePlatformID string
|
||||
vendor string
|
||||
runner runner.Runner
|
||||
arches map[string]distro.Arch
|
||||
defaultImageConfig *distro.ImageConfig
|
||||
}
|
||||
|
||||
func (d *distribution) Name() string {
|
||||
return d.name
|
||||
}
|
||||
|
||||
func (d *distribution) Releasever() string {
|
||||
return d.releaseVersion
|
||||
}
|
||||
|
||||
func (d *distribution) ModulePlatformID() string {
|
||||
return d.modulePlatformID
|
||||
}
|
||||
|
||||
func (d *distribution) OSTreeRef() string {
|
||||
return "" // not supported
|
||||
}
|
||||
|
||||
func (d *distribution) ListArches() []string {
|
||||
archNames := make([]string, 0, len(d.arches))
|
||||
for name := range d.arches {
|
||||
archNames = append(archNames, name)
|
||||
}
|
||||
sort.Strings(archNames)
|
||||
return archNames
|
||||
}
|
||||
|
||||
func (d *distribution) GetArch(name string) (distro.Arch, error) {
|
||||
arch, exists := d.arches[name]
|
||||
if !exists {
|
||||
return nil, errors.New("invalid architecture: " + name)
|
||||
}
|
||||
return arch, nil
|
||||
}
|
||||
|
||||
func (d *distribution) addArches(arches ...architecture) {
|
||||
if d.arches == nil {
|
||||
d.arches = map[string]distro.Arch{}
|
||||
}
|
||||
|
||||
// Do not make copies of architectures, as opposed to image types,
|
||||
// because architecture definitions are not used by more than a single
|
||||
// distro definition.
|
||||
for idx := range arches {
|
||||
d.arches[arches[idx].name] = &arches[idx]
|
||||
}
|
||||
}
|
||||
|
||||
func (d *distribution) isRHEL() bool {
|
||||
return strings.HasPrefix(d.name, "rhel")
|
||||
}
|
||||
|
||||
func (d *distribution) getDefaultImageConfig() *distro.ImageConfig {
|
||||
return d.defaultImageConfig
|
||||
}
|
||||
|
||||
// --- Architecture ---
|
||||
|
||||
type architecture struct {
|
||||
distro *distribution
|
||||
name string
|
||||
imageTypes map[string]distro.ImageType
|
||||
imageTypeAliases map[string]string
|
||||
}
|
||||
|
||||
func (a *architecture) Name() string {
|
||||
return a.name
|
||||
}
|
||||
|
||||
func (a *architecture) ListImageTypes() []string {
|
||||
itNames := make([]string, 0, len(a.imageTypes))
|
||||
for name := range a.imageTypes {
|
||||
itNames = append(itNames, name)
|
||||
}
|
||||
sort.Strings(itNames)
|
||||
return itNames
|
||||
}
|
||||
|
||||
func (a *architecture) GetImageType(name string) (distro.ImageType, error) {
|
||||
t, exists := a.imageTypes[name]
|
||||
if !exists {
|
||||
aliasForName, exists := a.imageTypeAliases[name]
|
||||
if !exists {
|
||||
return nil, errors.New("invalid image type: " + name)
|
||||
}
|
||||
t, exists = a.imageTypes[aliasForName]
|
||||
if !exists {
|
||||
panic(fmt.Sprintf("image type '%s' is an alias to a non-existing image type '%s'", name, aliasForName))
|
||||
}
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (a *architecture) addImageTypes(platform platform.Platform, imageTypes ...imageType) {
|
||||
if a.imageTypes == nil {
|
||||
a.imageTypes = map[string]distro.ImageType{}
|
||||
}
|
||||
for idx := range imageTypes {
|
||||
it := imageTypes[idx]
|
||||
it.arch = a
|
||||
it.platform = platform
|
||||
a.imageTypes[it.name] = &it
|
||||
for _, alias := range it.nameAliases {
|
||||
if a.imageTypeAliases == nil {
|
||||
a.imageTypeAliases = map[string]string{}
|
||||
}
|
||||
if existingAliasFor, exists := a.imageTypeAliases[alias]; exists {
|
||||
panic(fmt.Sprintf("image type alias '%s' for '%s' is already defined for another image type '%s'", alias, it.name, existingAliasFor))
|
||||
}
|
||||
a.imageTypeAliases[alias] = it.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *architecture) Distro() distro.Distro {
|
||||
return a.distro
|
||||
}
|
||||
|
||||
// New creates a new distro object, defining the supported architectures and image types
|
||||
func New() distro.Distro {
|
||||
return newDistro("rhel-7")
|
||||
}
|
||||
|
||||
func newDistro(distroName string) distro.Distro {
|
||||
|
||||
rd := distroMap[distroName]
|
||||
|
||||
// Architecture definitions
|
||||
x86_64 := architecture{
|
||||
name: platform.ARCH_X86_64.String(),
|
||||
distro: &rd,
|
||||
}
|
||||
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_QCOW2,
|
||||
QCOW2Compat: "0.10",
|
||||
},
|
||||
},
|
||||
qcow2ImgType,
|
||||
)
|
||||
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_VHD,
|
||||
},
|
||||
},
|
||||
azureRhuiImgType,
|
||||
)
|
||||
|
||||
rd.addArches(
|
||||
x86_64,
|
||||
)
|
||||
|
||||
return &rd
|
||||
}
|
||||
|
|
@ -1,452 +0,0 @@
|
|||
package rhel7_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro/distro_test_common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro/rhel7"
|
||||
)
|
||||
|
||||
type rhelFamilyDistro struct {
|
||||
name string
|
||||
distro distro.Distro
|
||||
}
|
||||
|
||||
var rhelFamilyDistros = []rhelFamilyDistro{
|
||||
{
|
||||
name: "rhel",
|
||||
distro: rhel7.New(),
|
||||
},
|
||||
}
|
||||
|
||||
func TestFilenameFromType(t *testing.T) {
|
||||
type args struct {
|
||||
outputFormat string
|
||||
}
|
||||
type wantResult struct {
|
||||
filename string
|
||||
mimeType string
|
||||
wantErr bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want wantResult
|
||||
}{
|
||||
{
|
||||
name: "qcow2",
|
||||
args: args{"qcow2"},
|
||||
want: wantResult{
|
||||
filename: "disk.qcow2",
|
||||
mimeType: "application/x-qemu-disk",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "azure-rhui",
|
||||
args: args{"azure-rhui"},
|
||||
want: wantResult{
|
||||
filename: "disk.vhd.xz",
|
||||
mimeType: "application/xz",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid-output-type",
|
||||
args: args{"foobar"},
|
||||
want: wantResult{wantErr: true},
|
||||
},
|
||||
}
|
||||
for _, dist := range rhelFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dist := dist.distro
|
||||
arch, _ := dist.GetArch("x86_64")
|
||||
imgType, err := arch.GetImageType(tt.args.outputFormat)
|
||||
if (err != nil) != tt.want.wantErr {
|
||||
t.Errorf("Arch.GetImageType() error = %v, wantErr %v", err, tt.want.wantErr)
|
||||
return
|
||||
}
|
||||
if !tt.want.wantErr {
|
||||
gotFilename := imgType.Filename()
|
||||
gotMIMEType := imgType.MIMEType()
|
||||
if gotFilename != tt.want.filename {
|
||||
t.Errorf("ImageType.Filename() got = %v, want %v", gotFilename, tt.want.filename)
|
||||
}
|
||||
if gotMIMEType != tt.want.mimeType {
|
||||
t.Errorf("ImageType.MIMEType() got1 = %v, want %v", gotMIMEType, tt.want.mimeType)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageType_BuildPackages(t *testing.T) {
|
||||
x8664BuildPackages := []string{
|
||||
"dnf",
|
||||
"dosfstools",
|
||||
"e2fsprogs",
|
||||
"grub2-efi-x64",
|
||||
"grub2-pc",
|
||||
"policycoreutils",
|
||||
"shim-x64",
|
||||
"systemd",
|
||||
"tar",
|
||||
"qemu-img",
|
||||
"xz",
|
||||
}
|
||||
buildPackages := map[string][]string{
|
||||
"x86_64": x8664BuildPackages,
|
||||
}
|
||||
for _, dist := range rhelFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
d := dist.distro
|
||||
for _, archLabel := range d.ListArches() {
|
||||
archStruct, err := d.GetArch(archLabel)
|
||||
if assert.NoErrorf(t, err, "d.GetArch(%v) returned err = %v; expected nil", archLabel, err) {
|
||||
continue
|
||||
}
|
||||
for _, itLabel := range archStruct.ListImageTypes() {
|
||||
itStruct, err := archStruct.GetImageType(itLabel)
|
||||
if assert.NoErrorf(t, err, "d.GetArch(%v) returned err = %v; expected nil", archLabel, err) {
|
||||
continue
|
||||
}
|
||||
manifest, _, err := itStruct.Manifest(&blueprint.Blueprint{}, distro.ImageOptions{}, nil, 0)
|
||||
assert.NoError(t, err)
|
||||
buildPkgs := manifest.GetPackageSetChains()["build"]
|
||||
assert.NotNil(t, buildPkgs)
|
||||
assert.Len(t, buildPkgs, 1)
|
||||
assert.ElementsMatch(t, buildPackages[archLabel], buildPkgs[0].Include)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageType_Name(t *testing.T) {
|
||||
imgMap := []struct {
|
||||
arch string
|
||||
imgNames []string
|
||||
}{
|
||||
{
|
||||
arch: "x86_64",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"azure-rhui",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, dist := range rhelFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, mapping := range imgMap {
|
||||
arch, err := dist.distro.GetArch(mapping.arch)
|
||||
if assert.NoError(t, err) {
|
||||
for _, imgName := range mapping.imgNames {
|
||||
imgType, err := arch.GetImageType(imgName)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equalf(t, imgName, imgType.Name(), "arch: %s", mapping.arch)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Check that Manifest() function returns an error for unsupported
|
||||
// configurations.
|
||||
func TestDistro_ManifestError(t *testing.T) {
|
||||
r7distro := rhel7.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Kernel: &blueprint.KernelCustomization{
|
||||
Append: "debug",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, archName := range r7distro.ListArches() {
|
||||
arch, _ := r7distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
imgOpts := distro.ImageOptions{
|
||||
Size: imgType.Size(0),
|
||||
}
|
||||
_, _, err := imgType.Manifest(&bp, imgOpts, nil, 0)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestArchitecture_ListImageTypes(t *testing.T) {
|
||||
imgMap := []struct {
|
||||
arch string
|
||||
imgNames []string
|
||||
rhelAdditionalImageTypes []string
|
||||
}{
|
||||
{
|
||||
arch: "x86_64",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"azure-rhui",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, dist := range rhelFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, mapping := range imgMap {
|
||||
arch, err := dist.distro.GetArch(mapping.arch)
|
||||
require.NoError(t, err)
|
||||
imageTypes := arch.ListImageTypes()
|
||||
|
||||
var expectedImageTypes []string
|
||||
expectedImageTypes = append(expectedImageTypes, mapping.imgNames...)
|
||||
if dist.name == "rhel" {
|
||||
expectedImageTypes = append(expectedImageTypes, mapping.rhelAdditionalImageTypes...)
|
||||
}
|
||||
|
||||
require.ElementsMatch(t, expectedImageTypes, imageTypes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRhel7_ListArches(t *testing.T) {
|
||||
arches := rhel7.New().ListArches()
|
||||
assert.Equal(t, []string{"x86_64"}, arches)
|
||||
}
|
||||
|
||||
func TestRhel7_GetArch(t *testing.T) {
|
||||
arches := []struct {
|
||||
name string
|
||||
errorExpected bool
|
||||
errorExpectedInCentos bool
|
||||
}{
|
||||
{
|
||||
name: "x86_64",
|
||||
},
|
||||
{
|
||||
name: "foo-arch",
|
||||
errorExpected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, dist := range rhelFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, a := range arches {
|
||||
actualArch, err := dist.distro.GetArch(a.name)
|
||||
if a.errorExpected || (a.errorExpectedInCentos && dist.name == "centos") {
|
||||
assert.Nil(t, actualArch)
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.Equal(t, a.name, actualArch.Name())
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRhel7_Name(t *testing.T) {
|
||||
distro := rhel7.New()
|
||||
assert.Equal(t, "rhel-7", distro.Name())
|
||||
}
|
||||
|
||||
func TestRhel7_ModulePlatformID(t *testing.T) {
|
||||
distro := rhel7.New()
|
||||
assert.Equal(t, "platform:el7", distro.ModulePlatformID())
|
||||
}
|
||||
|
||||
func TestRhel7_KernelOption(t *testing.T) {
|
||||
distro_test_common.TestDistro_KernelOption(t, rhel7.New())
|
||||
}
|
||||
|
||||
func TestDistro_CustomFileSystemManifestError(t *testing.T) {
|
||||
r7distro := rhel7.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/etc",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range r7distro.ListArches() {
|
||||
arch, _ := r7distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
assert.EqualError(t, err, "The following custom mountpoints are not supported [\"/etc\"]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_TestRootMountPoint(t *testing.T) {
|
||||
r7distro := rhel7.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range r7distro.ListArches() {
|
||||
arch, _ := r7distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_CustomFileSystemSubDirectories(t *testing.T) {
|
||||
r7distro := rhel7.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/log",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/log/audit",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range r7distro.ListArches() {
|
||||
arch, _ := r7distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_MountpointsWithArbitraryDepthAllowed(t *testing.T) {
|
||||
r7distro := rhel7.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/a",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/a/b",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/a/b/c",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/a/b/c/d",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range r7distro.ListArches() {
|
||||
arch, _ := r7distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_DirtyMountpointsNotAllowed(t *testing.T) {
|
||||
r7distro := rhel7.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "//",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var//",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var//log/audit/",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range r7distro.ListArches() {
|
||||
arch, _ := r7distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
assert.EqualError(t, err, "The following custom mountpoints are not supported [\"//\" \"/var//\" \"/var//log/audit/\"]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_CustomFileSystemPatternMatching(t *testing.T) {
|
||||
r7distro := rhel7.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/variable",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/variable/log/audit",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range r7distro.ListArches() {
|
||||
arch, _ := r7distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
assert.EqualError(t, err, "The following custom mountpoints are not supported [\"/variable\" \"/variable/log/audit\"]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_CustomUsrPartitionNotLargeEnough(t *testing.T) {
|
||||
r7distro := rhel7.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/usr",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range r7distro.ListArches() {
|
||||
arch, _ := r7distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,250 +0,0 @@
|
|||
package rhel7
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/container"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/image"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/users"
|
||||
"github.com/osbuild/osbuild-composer/internal/workload"
|
||||
)
|
||||
|
||||
func osCustomizations(
|
||||
t *imageType,
|
||||
osPackageSet rpmmd.PackageSet,
|
||||
options distro.ImageOptions,
|
||||
containers []container.SourceSpec,
|
||||
c *blueprint.Customizations,
|
||||
) manifest.OSCustomizations {
|
||||
|
||||
imageConfig := t.getDefaultImageConfig()
|
||||
|
||||
osc := manifest.OSCustomizations{}
|
||||
|
||||
if t.bootable {
|
||||
osc.KernelName = c.GetKernel().Name
|
||||
|
||||
var kernelOptions []string
|
||||
if t.kernelOptions != "" {
|
||||
kernelOptions = append(kernelOptions, t.kernelOptions)
|
||||
}
|
||||
if bpKernel := c.GetKernel(); bpKernel.Append != "" {
|
||||
kernelOptions = append(kernelOptions, bpKernel.Append)
|
||||
}
|
||||
osc.KernelOptionsAppend = kernelOptions
|
||||
if t.platform.GetArch() != platform.ARCH_S390X {
|
||||
osc.KernelOptionsBootloader = true
|
||||
}
|
||||
}
|
||||
|
||||
osc.ExtraBasePackages = osPackageSet.Include
|
||||
osc.ExcludeBasePackages = osPackageSet.Exclude
|
||||
osc.ExtraBaseRepos = osPackageSet.Repositories
|
||||
|
||||
osc.Containers = containers
|
||||
|
||||
osc.GPGKeyFiles = imageConfig.GPGKeyFiles
|
||||
if imageConfig.ExcludeDocs != nil {
|
||||
osc.ExcludeDocs = *imageConfig.ExcludeDocs
|
||||
}
|
||||
|
||||
// don't put users and groups in the payload of an installer
|
||||
// add them via kickstart instead
|
||||
osc.Groups = users.GroupsFromBP(c.GetGroups())
|
||||
osc.Users = users.UsersFromBP(c.GetUsers())
|
||||
|
||||
osc.EnabledServices = imageConfig.EnabledServices
|
||||
osc.DisabledServices = imageConfig.DisabledServices
|
||||
if imageConfig.DefaultTarget != nil {
|
||||
osc.DefaultTarget = *imageConfig.DefaultTarget
|
||||
}
|
||||
|
||||
osc.Firewall = imageConfig.Firewall
|
||||
if fw := c.GetFirewall(); fw != nil {
|
||||
options := osbuild.FirewallStageOptions{
|
||||
Ports: fw.Ports,
|
||||
}
|
||||
|
||||
if fw.Services != nil {
|
||||
options.EnabledServices = fw.Services.Enabled
|
||||
options.DisabledServices = fw.Services.Disabled
|
||||
}
|
||||
if fw.Zones != nil {
|
||||
for _, z := range fw.Zones {
|
||||
options.Zones = append(options.Zones, osbuild.FirewallZone{
|
||||
Name: *z.Name,
|
||||
Sources: z.Sources,
|
||||
})
|
||||
}
|
||||
}
|
||||
osc.Firewall = &options
|
||||
}
|
||||
|
||||
language, keyboard := c.GetPrimaryLocale()
|
||||
if language != nil {
|
||||
osc.Language = *language
|
||||
} else if imageConfig.Locale != nil {
|
||||
osc.Language = *imageConfig.Locale
|
||||
}
|
||||
if keyboard != nil {
|
||||
osc.Keyboard = keyboard
|
||||
} else if imageConfig.Keyboard != nil {
|
||||
osc.Keyboard = &imageConfig.Keyboard.Keymap
|
||||
if imageConfig.Keyboard.X11Keymap != nil {
|
||||
osc.X11KeymapLayouts = imageConfig.Keyboard.X11Keymap.Layouts
|
||||
}
|
||||
}
|
||||
|
||||
if hostname := c.GetHostname(); hostname != nil {
|
||||
osc.Hostname = *hostname
|
||||
}
|
||||
|
||||
timezone, ntpServers := c.GetTimezoneSettings()
|
||||
if timezone != nil {
|
||||
osc.Timezone = *timezone
|
||||
} else if imageConfig.Timezone != nil {
|
||||
osc.Timezone = *imageConfig.Timezone
|
||||
}
|
||||
|
||||
if len(ntpServers) > 0 {
|
||||
for _, server := range ntpServers {
|
||||
osc.NTPServers = append(osc.NTPServers, osbuild.ChronyConfigServer{Hostname: server})
|
||||
}
|
||||
} else if imageConfig.TimeSynchronization != nil {
|
||||
osc.NTPServers = imageConfig.TimeSynchronization.Servers
|
||||
osc.LeapSecTZ = imageConfig.TimeSynchronization.LeapsecTz
|
||||
}
|
||||
|
||||
// Relabel the tree, unless the `NoSElinux` flag is explicitly set to `true`
|
||||
if imageConfig.NoSElinux == nil || imageConfig.NoSElinux != nil && !*imageConfig.NoSElinux {
|
||||
osc.SElinux = "targeted"
|
||||
osc.SELinuxForceRelabel = imageConfig.SELinuxForceRelabel
|
||||
}
|
||||
|
||||
if oscapConfig := c.GetOpenSCAP(); oscapConfig != nil {
|
||||
osc.OpenSCAPConfig = osbuild.NewOscapRemediationStageOptions(
|
||||
osbuild.OscapConfig{
|
||||
Datastream: oscapConfig.DataStream,
|
||||
ProfileID: oscapConfig.ProfileID,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if t.arch.distro.isRHEL() && options.Facts != nil {
|
||||
osc.FactAPIType = &options.Facts.APIType
|
||||
}
|
||||
|
||||
var err error
|
||||
osc.Directories, err = blueprint.DirectoryCustomizationsToFsNodeDirectories(c.GetDirectories())
|
||||
if err != nil {
|
||||
// In theory this should never happen, because the blueprint directory customizations
|
||||
// should have been validated before this point.
|
||||
panic(fmt.Sprintf("failed to convert directory customizations to fs node directories: %v", err))
|
||||
}
|
||||
|
||||
osc.Files, err = blueprint.FileCustomizationsToFsNodeFiles(c.GetFiles())
|
||||
if err != nil {
|
||||
// In theory this should never happen, because the blueprint file customizations
|
||||
// should have been validated before this point.
|
||||
panic(fmt.Sprintf("failed to convert file customizations to fs node files: %v", err))
|
||||
}
|
||||
|
||||
// set yum repos first, so it doesn't get overridden by
|
||||
// imageConfig.YUMRepos
|
||||
osc.YUMRepos = imageConfig.YUMRepos
|
||||
|
||||
customRepos, err := c.GetRepositories()
|
||||
if err != nil {
|
||||
// This shouldn't happen and since the repos
|
||||
// should have already been validated
|
||||
panic(fmt.Sprintf("failed to get custom repos: %v", err))
|
||||
}
|
||||
|
||||
// This function returns a map of filename and corresponding yum repos
|
||||
// and a list of fs node files for the inline gpg keys so we can save
|
||||
// them to disk. This step also swaps the inline gpg key with the path
|
||||
// to the file in the os file tree
|
||||
yumRepos, gpgKeyFiles, err := blueprint.RepoCustomizationsToRepoConfigAndGPGKeyFiles(customRepos)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to convert inline gpgkeys to fs node files: %v", err))
|
||||
}
|
||||
|
||||
// add the gpg key files to the list of files to be added to the tree
|
||||
if len(gpgKeyFiles) > 0 {
|
||||
osc.Files = append(osc.Files, gpgKeyFiles...)
|
||||
}
|
||||
|
||||
for filename, repos := range yumRepos {
|
||||
osc.YUMRepos = append(osc.YUMRepos, osbuild.NewYumReposStageOptions(filename, repos))
|
||||
}
|
||||
|
||||
osc.ShellInit = imageConfig.ShellInit
|
||||
|
||||
osc.Grub2Config = imageConfig.Grub2Config
|
||||
osc.Sysconfig = imageConfig.Sysconfig
|
||||
osc.SystemdLogind = imageConfig.SystemdLogind
|
||||
osc.CloudInit = imageConfig.CloudInit
|
||||
osc.Modprobe = imageConfig.Modprobe
|
||||
osc.DracutConf = imageConfig.DracutConf
|
||||
osc.SystemdUnit = imageConfig.SystemdUnit
|
||||
osc.Authselect = imageConfig.Authselect
|
||||
osc.SELinuxConfig = imageConfig.SELinuxConfig
|
||||
osc.Tuned = imageConfig.Tuned
|
||||
osc.Tmpfilesd = imageConfig.Tmpfilesd
|
||||
osc.PamLimitsConf = imageConfig.PamLimitsConf
|
||||
osc.Sysctld = imageConfig.Sysctld
|
||||
osc.DNFConfig = imageConfig.DNFConfig
|
||||
osc.DNFAutomaticConfig = imageConfig.DNFAutomaticConfig
|
||||
osc.YUMConfig = imageConfig.YumConfig
|
||||
osc.SshdConfig = imageConfig.SshdConfig
|
||||
osc.AuthConfig = imageConfig.Authconfig
|
||||
osc.PwQuality = imageConfig.PwQuality
|
||||
osc.RHSMConfig = imageConfig.RHSMConfig
|
||||
osc.Subscription = options.Subscription
|
||||
osc.WAAgentConfig = imageConfig.WAAgentConfig
|
||||
osc.UdevRules = imageConfig.UdevRules
|
||||
osc.GCPGuestAgentConfig = imageConfig.GCPGuestAgentConfig
|
||||
|
||||
return osc
|
||||
}
|
||||
|
||||
func liveImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
img := image.NewLiveImage()
|
||||
img.Platform = t.platform
|
||||
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], options, containers, customizations)
|
||||
img.Environment = t.environment
|
||||
img.Workload = workload
|
||||
img.Compression = t.compression
|
||||
img.PartTool = osbuild.PTSgdisk // all RHEL 7 images should use sgdisk
|
||||
img.ForceSize = common.ToPtr(false) // RHEL 7 qemu vpc subformat does not support force_size
|
||||
img.NoBLS = true // RHEL 7 grub does not support BLS
|
||||
img.OSProduct = t.arch.distro.product
|
||||
img.OSVersion = t.arch.distro.osVersion
|
||||
img.OSNick = t.arch.distro.nick
|
||||
|
||||
// TODO: move generation into LiveImage
|
||||
pt, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img.PartitionTable = pt
|
||||
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
|
@ -1,285 +0,0 @@
|
|||
package rhel7
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/container"
|
||||
"github.com/osbuild/osbuild-composer/internal/disk"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/environment"
|
||||
"github.com/osbuild/osbuild-composer/internal/image"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/pathpolicy"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/workload"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type packageSetFunc func(t *imageType) rpmmd.PackageSet
|
||||
|
||||
type imageFunc func(workload workload.Workload, t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, packageSets map[string]rpmmd.PackageSet, containers []container.SourceSpec, rng *rand.Rand) (image.ImageKind, error)
|
||||
|
||||
type imageType struct {
|
||||
arch *architecture
|
||||
platform platform.Platform
|
||||
environment environment.Environment
|
||||
workload workload.Workload
|
||||
name string
|
||||
nameAliases []string
|
||||
filename string
|
||||
compression string // TODO: remove from image definition and make it a transport option
|
||||
mimeType string
|
||||
packageSets map[string]packageSetFunc
|
||||
packageSetChains map[string][]string
|
||||
defaultImageConfig *distro.ImageConfig
|
||||
kernelOptions string
|
||||
defaultSize uint64
|
||||
buildPipelines []string
|
||||
payloadPipelines []string
|
||||
exports []string
|
||||
image imageFunc
|
||||
|
||||
// bootable image
|
||||
bootable bool
|
||||
// List of valid arches for the image type
|
||||
basePartitionTables distro.BasePartitionTableMap
|
||||
}
|
||||
|
||||
func (t *imageType) Name() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *imageType) Arch() distro.Arch {
|
||||
return t.arch
|
||||
}
|
||||
|
||||
func (t *imageType) Filename() string {
|
||||
return t.filename
|
||||
}
|
||||
|
||||
func (t *imageType) MIMEType() string {
|
||||
return t.mimeType
|
||||
}
|
||||
|
||||
func (t *imageType) OSTreeRef() string {
|
||||
// Not supported
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t *imageType) Size(size uint64) uint64 {
|
||||
if size == 0 {
|
||||
size = t.defaultSize
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func (t *imageType) BuildPipelines() []string {
|
||||
return t.buildPipelines
|
||||
}
|
||||
|
||||
func (t *imageType) PayloadPipelines() []string {
|
||||
return t.payloadPipelines
|
||||
}
|
||||
|
||||
func (t *imageType) PayloadPackageSets() []string {
|
||||
return []string{blueprintPkgsKey}
|
||||
}
|
||||
|
||||
func (t *imageType) PackageSetsChains() map[string][]string {
|
||||
return t.packageSetChains
|
||||
}
|
||||
|
||||
func (t *imageType) Exports() []string {
|
||||
if len(t.exports) == 0 {
|
||||
panic(fmt.Sprintf("programming error: no exports for '%s'", t.name))
|
||||
}
|
||||
return t.exports
|
||||
}
|
||||
|
||||
func (t *imageType) BootMode() distro.BootMode {
|
||||
if t.platform.GetUEFIVendor() != "" && t.platform.GetBIOSPlatform() != "" {
|
||||
return distro.BOOT_HYBRID
|
||||
} else if t.platform.GetUEFIVendor() != "" {
|
||||
return distro.BOOT_UEFI
|
||||
} else if t.platform.GetBIOSPlatform() != "" || t.platform.GetZiplSupport() {
|
||||
return distro.BOOT_LEGACY
|
||||
}
|
||||
return distro.BOOT_NONE
|
||||
}
|
||||
|
||||
func (t *imageType) getPartitionTable(
|
||||
mountpoints []blueprint.FilesystemCustomization,
|
||||
options distro.ImageOptions,
|
||||
rng *rand.Rand,
|
||||
) (*disk.PartitionTable, error) {
|
||||
archName := t.arch.Name()
|
||||
|
||||
basePartitionTable, exists := t.basePartitionTables[archName]
|
||||
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("unknown arch: " + archName)
|
||||
}
|
||||
|
||||
imageSize := t.Size(options.Size)
|
||||
|
||||
return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, true, nil, rng)
|
||||
}
|
||||
|
||||
func (t *imageType) getDefaultImageConfig() *distro.ImageConfig {
|
||||
// ensure that image always returns non-nil default config
|
||||
imageConfig := t.defaultImageConfig
|
||||
if imageConfig == nil {
|
||||
imageConfig = &distro.ImageConfig{}
|
||||
}
|
||||
return imageConfig.InheritFrom(t.arch.distro.getDefaultImageConfig())
|
||||
|
||||
}
|
||||
|
||||
func (t *imageType) PartitionType() string {
|
||||
archName := t.arch.Name()
|
||||
basePartitionTable, exists := t.basePartitionTables[archName]
|
||||
if !exists {
|
||||
return ""
|
||||
}
|
||||
|
||||
return basePartitionTable.Type
|
||||
}
|
||||
|
||||
func (t *imageType) Manifest(bp *blueprint.Blueprint,
|
||||
options distro.ImageOptions,
|
||||
repos []rpmmd.RepoConfig,
|
||||
seed int64) (*manifest.Manifest, []string, error) {
|
||||
|
||||
warnings, err := t.checkOptions(bp, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// merge package sets that appear in the image type with the package sets
|
||||
// of the same name from the distro and arch
|
||||
staticPackageSets := make(map[string]rpmmd.PackageSet)
|
||||
|
||||
for name, getter := range t.packageSets {
|
||||
staticPackageSets[name] = getter(t)
|
||||
}
|
||||
|
||||
// amend with repository information and collect payload repos
|
||||
payloadRepos := make([]rpmmd.RepoConfig, 0)
|
||||
for _, repo := range repos {
|
||||
if len(repo.PackageSets) > 0 {
|
||||
// only apply the repo to the listed package sets
|
||||
for _, psName := range repo.PackageSets {
|
||||
if slices.Contains(t.PayloadPackageSets(), psName) {
|
||||
payloadRepos = append(payloadRepos, repo)
|
||||
}
|
||||
ps := staticPackageSets[psName]
|
||||
ps.Repositories = append(ps.Repositories, repo)
|
||||
staticPackageSets[psName] = ps
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w := t.workload
|
||||
if w == nil {
|
||||
cw := &workload.Custom{
|
||||
BaseWorkload: workload.BaseWorkload{
|
||||
Repos: payloadRepos,
|
||||
},
|
||||
Packages: bp.GetPackagesEx(false),
|
||||
}
|
||||
if services := bp.Customizations.GetServices(); services != nil {
|
||||
cw.Services = services.Enabled
|
||||
cw.DisabledServices = services.Disabled
|
||||
}
|
||||
w = cw
|
||||
}
|
||||
|
||||
containerSources := make([]container.SourceSpec, len(bp.Containers))
|
||||
for idx := range bp.Containers {
|
||||
containerSources[idx] = container.SourceSpec(bp.Containers[idx])
|
||||
}
|
||||
|
||||
source := rand.NewSource(seed)
|
||||
// math/rand is good enough in this case
|
||||
/* #nosec G404 */
|
||||
rng := rand.New(source)
|
||||
|
||||
if t.image == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
img, err := t.image(w, t, bp.Customizations, options, staticPackageSets, containerSources, rng)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
mf := manifest.New()
|
||||
mf.Distro = manifest.DISTRO_EL7
|
||||
_, err = img.InstantiateManifest(&mf, repos, t.arch.distro.runner, rng)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &mf, warnings, err
|
||||
}
|
||||
|
||||
// checkOptions checks the validity and compatibility of options and customizations for the image type.
|
||||
// Returns ([]string, error) where []string, if non-nil, will hold any generated warnings (e.g. deprecation notices).
|
||||
func (t *imageType) checkOptions(bp *blueprint.Blueprint, options distro.ImageOptions) ([]string, error) {
|
||||
customizations := bp.Customizations
|
||||
// holds warnings (e.g. deprecation notices)
|
||||
var warnings []string
|
||||
if t.workload != nil {
|
||||
// For now, if an image type defines its own workload, don't allow any
|
||||
// user customizations.
|
||||
// Soon we will have more workflows and each will define its allowed
|
||||
// set of customizations. The current set of customizations defined in
|
||||
// the blueprint spec corresponds to the Custom workflow.
|
||||
if customizations != nil {
|
||||
return warnings, fmt.Errorf("image type %q does not support customizations", t.name)
|
||||
}
|
||||
}
|
||||
|
||||
if len(bp.Containers) > 0 {
|
||||
return warnings, fmt.Errorf("embedding containers is not supported for %s on %s", t.name, t.arch.distro.name)
|
||||
}
|
||||
|
||||
mountpoints := customizations.GetFilesystems()
|
||||
|
||||
err := blueprint.CheckMountpointsPolicy(mountpoints, pathpolicy.MountpointPolicies)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
if osc := customizations.GetOpenSCAP(); osc != nil {
|
||||
return warnings, fmt.Errorf(fmt.Sprintf("OpenSCAP unsupported os version: %s", t.arch.distro.osVersion))
|
||||
}
|
||||
|
||||
// Check Directory/File Customizations are valid
|
||||
dc := customizations.GetDirectories()
|
||||
fc := customizations.GetFiles()
|
||||
|
||||
err = blueprint.ValidateDirFileCustomizations(dc, fc)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
err = blueprint.CheckDirectoryCustomizationsPolicy(dc, pathpolicy.CustomDirectoriesPolicies)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
err = blueprint.CheckFileCustomizationsPolicy(fc, pathpolicy.CustomFilesPolicies)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
// check if repository customizations are valid
|
||||
_, err = customizations.GetRepositories()
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
return warnings, nil
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
package rhel7
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
// packages that are only in some (sub)-distributions
|
||||
func distroSpecificPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
if t.arch.distro.isRHEL() {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{"insights-client"},
|
||||
}
|
||||
}
|
||||
return rpmmd.PackageSet{}
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
package rhel7
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/disk"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
)
|
||||
|
||||
// ////////// Partition table //////////
|
||||
|
||||
var defaultBasePartitionTables = distro.BasePartitionTableMap{
|
||||
platform.ARCH_X86_64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 1 * common.MebiByte, // 1MB
|
||||
Bootable: true,
|
||||
Type: disk.BIOSBootPartitionGUID,
|
||||
UUID: disk.BIOSBootPartitionUUID,
|
||||
},
|
||||
{
|
||||
Size: 200 * common.MebiByte, // 200 MB
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
Label: "EFI-SYSTEM",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 500 * common.MebiByte, // 500 MB
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.FilesystemDataUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/boot",
|
||||
Label: "boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte, // 2GiB
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
package rhel7
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/subscription"
|
||||
)
|
||||
|
||||
var qcow2ImgType = imageType{
|
||||
name: "qcow2",
|
||||
filename: "disk.qcow2",
|
||||
mimeType: "application/x-qemu-disk",
|
||||
kernelOptions: "console=tty0 console=ttyS0,115200n8 no_timer_check net.ifnames=0 crashkernel=auto",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: qcow2CommonPackageSet,
|
||||
},
|
||||
defaultImageConfig: qcow2DefaultImgConfig,
|
||||
bootable: true,
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "qcow2"},
|
||||
exports: []string{"qcow2"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
|
||||
var qcow2DefaultImgConfig = &distro.ImageConfig{
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
SELinuxForceRelabel: common.ToPtr(true),
|
||||
Sysconfig: []*osbuild.SysconfigStageOptions{
|
||||
{
|
||||
Kernel: &osbuild.SysconfigKernelOptions{
|
||||
UpdateDefault: true,
|
||||
DefaultKernel: "kernel",
|
||||
},
|
||||
Network: &osbuild.SysconfigNetworkOptions{
|
||||
Networking: true,
|
||||
NoZeroConf: true,
|
||||
},
|
||||
NetworkScripts: &osbuild.NetworkScriptsOptions{
|
||||
IfcfgFiles: map[string]osbuild.IfcfgFile{
|
||||
"eth0": {
|
||||
Device: "eth0",
|
||||
Bootproto: osbuild.IfcfgBootprotoDHCP,
|
||||
OnBoot: common.ToPtr(true),
|
||||
Type: osbuild.IfcfgTypeEthernet,
|
||||
UserCtl: common.ToPtr(true),
|
||||
PeerDNS: common.ToPtr(true),
|
||||
IPv6Init: common.ToPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
RHSMConfig: map[subscription.RHSMStatus]*osbuild.RHSMStageOptions{
|
||||
subscription.RHSMConfigNoSubscription: {
|
||||
YumPlugins: &osbuild.RHSMStageOptionsDnfPlugins{
|
||||
ProductID: &osbuild.RHSMStageOptionsDnfPlugin{
|
||||
Enabled: false,
|
||||
},
|
||||
SubscriptionManager: &osbuild.RHSMStageOptionsDnfPlugin{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func qcow2CommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"@core",
|
||||
"kernel",
|
||||
"nfs-utils",
|
||||
"yum-utils",
|
||||
|
||||
"cloud-init",
|
||||
//"ovirt-guest-agent-common",
|
||||
"rhn-setup",
|
||||
"yum-rhn-plugin",
|
||||
"cloud-utils-growpart",
|
||||
"dracut-config-generic",
|
||||
"tar",
|
||||
"tcpdump",
|
||||
"rsync",
|
||||
},
|
||||
Exclude: []string{
|
||||
"biosdevname",
|
||||
"dracut-config-rescue",
|
||||
"iprutils",
|
||||
"NetworkManager-team",
|
||||
"NetworkManager-tui",
|
||||
"NetworkManager",
|
||||
"plymouth",
|
||||
|
||||
"aic94xx-firmware",
|
||||
"alsa-firmware",
|
||||
"alsa-lib",
|
||||
"alsa-tools-firmware",
|
||||
"ivtv-firmware",
|
||||
"iwl1000-firmware",
|
||||
"iwl100-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl3945-firmware",
|
||||
"iwl4965-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6000-firmware",
|
||||
"iwl6000g2a-firmware",
|
||||
"iwl6000g2b-firmware",
|
||||
"iwl6050-firmware",
|
||||
"iwl7260-firmware",
|
||||
"libertas-sd8686-firmware",
|
||||
"libertas-sd8787-firmware",
|
||||
"libertas-usb8388-firmware",
|
||||
},
|
||||
}.Append(distroSpecificPackageSet(t))
|
||||
|
||||
return ps
|
||||
}
|
||||
|
|
@ -1,511 +0,0 @@
|
|||
package rhel8
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/subscription"
|
||||
)
|
||||
|
||||
func amiImgTypeX86_64(rd distribution) imageType {
|
||||
it := imageType{
|
||||
name: "ami",
|
||||
filename: "image.raw",
|
||||
mimeType: "application/octet-stream",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: ec2CommonPackageSet,
|
||||
},
|
||||
defaultImageConfig: defaultAMIImageConfigX86_64(rd),
|
||||
kernelOptions: "console=ttyS0,115200n8 console=tty0 net.ifnames=0 rd.blacklist=nouveau nvme_core.io_timeout=4294967295 crashkernel=auto",
|
||||
bootable: true,
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image"},
|
||||
exports: []string{"image"},
|
||||
basePartitionTables: ec2BasePartitionTables,
|
||||
}
|
||||
|
||||
return it
|
||||
}
|
||||
|
||||
func ec2ImgTypeX86_64(rd distribution) imageType {
|
||||
basePartitionTables := ec2BasePartitionTables
|
||||
// use legacy partition tables for RHEL 8.8 and older
|
||||
if common.VersionLessThan(rd.osVersion, "8.9") {
|
||||
basePartitionTables = ec2LegacyBasePartitionTables
|
||||
}
|
||||
|
||||
it := imageType{
|
||||
name: "ec2",
|
||||
filename: "image.raw.xz",
|
||||
mimeType: "application/xz",
|
||||
compression: "xz",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: rhelEc2PackageSet,
|
||||
},
|
||||
defaultImageConfig: defaultEc2ImageConfigX86_64(rd),
|
||||
kernelOptions: "console=ttyS0,115200n8 console=tty0 net.ifnames=0 rd.blacklist=nouveau nvme_core.io_timeout=4294967295 crashkernel=auto",
|
||||
bootable: true,
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "xz"},
|
||||
exports: []string{"xz"},
|
||||
basePartitionTables: basePartitionTables,
|
||||
}
|
||||
return it
|
||||
}
|
||||
|
||||
func ec2HaImgTypeX86_64(rd distribution) imageType {
|
||||
basePartitionTables := ec2BasePartitionTables
|
||||
// use legacy partition tables for RHEL 8.8 and older
|
||||
if common.VersionLessThan(rd.osVersion, "8.9") {
|
||||
basePartitionTables = ec2LegacyBasePartitionTables
|
||||
}
|
||||
|
||||
it := imageType{
|
||||
name: "ec2-ha",
|
||||
filename: "image.raw.xz",
|
||||
mimeType: "application/xz",
|
||||
compression: "xz",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: rhelEc2HaPackageSet,
|
||||
},
|
||||
defaultImageConfig: defaultEc2ImageConfigX86_64(rd),
|
||||
kernelOptions: "console=ttyS0,115200n8 console=tty0 net.ifnames=0 rd.blacklist=nouveau nvme_core.io_timeout=4294967295 crashkernel=auto",
|
||||
bootable: true,
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "xz"},
|
||||
exports: []string{"xz"},
|
||||
basePartitionTables: basePartitionTables,
|
||||
}
|
||||
return it
|
||||
}
|
||||
|
||||
func amiImgTypeAarch64(rd distribution) imageType {
|
||||
it := imageType{
|
||||
name: "ami",
|
||||
filename: "image.raw",
|
||||
mimeType: "application/octet-stream",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: ec2CommonPackageSet,
|
||||
},
|
||||
defaultImageConfig: defaultAMIImageConfig(rd),
|
||||
kernelOptions: "console=ttyS0,115200n8 console=tty0 net.ifnames=0 rd.blacklist=nouveau nvme_core.io_timeout=4294967295 iommu.strict=0 crashkernel=auto",
|
||||
bootable: true,
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image"},
|
||||
exports: []string{"image"},
|
||||
basePartitionTables: ec2BasePartitionTables,
|
||||
}
|
||||
return it
|
||||
}
|
||||
|
||||
func ec2ImgTypeAarch64(rd distribution) imageType {
|
||||
basePartitionTables := ec2BasePartitionTables
|
||||
// use legacy partition tables for RHEL 8.8 and older
|
||||
if common.VersionLessThan(rd.osVersion, "8.9") {
|
||||
basePartitionTables = ec2LegacyBasePartitionTables
|
||||
}
|
||||
|
||||
it := imageType{
|
||||
name: "ec2",
|
||||
filename: "image.raw.xz",
|
||||
mimeType: "application/xz",
|
||||
compression: "xz",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: rhelEc2PackageSet,
|
||||
},
|
||||
defaultImageConfig: defaultEc2ImageConfig(rd),
|
||||
kernelOptions: "console=ttyS0,115200n8 console=tty0 net.ifnames=0 rd.blacklist=nouveau nvme_core.io_timeout=4294967295 iommu.strict=0 crashkernel=auto",
|
||||
bootable: true,
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "xz"},
|
||||
exports: []string{"xz"},
|
||||
basePartitionTables: basePartitionTables,
|
||||
}
|
||||
return it
|
||||
}
|
||||
|
||||
func ec2SapImgTypeX86_64(rd distribution) imageType {
|
||||
basePartitionTables := ec2BasePartitionTables
|
||||
// use legacy partition tables for RHEL 8.8 and older
|
||||
if common.VersionLessThan(rd.osVersion, "8.9") {
|
||||
basePartitionTables = ec2LegacyBasePartitionTables
|
||||
}
|
||||
|
||||
it := imageType{
|
||||
name: "ec2-sap",
|
||||
filename: "image.raw.xz",
|
||||
mimeType: "application/xz",
|
||||
compression: "xz",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: rhelEc2SapPackageSet,
|
||||
},
|
||||
defaultImageConfig: defaultEc2SapImageConfigX86_64(rd),
|
||||
kernelOptions: "console=ttyS0,115200n8 console=tty0 net.ifnames=0 rd.blacklist=nouveau nvme_core.io_timeout=4294967295 crashkernel=auto processor.max_cstate=1 intel_idle.max_cstate=1",
|
||||
bootable: true,
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "xz"},
|
||||
exports: []string{"xz"},
|
||||
basePartitionTables: basePartitionTables,
|
||||
}
|
||||
return it
|
||||
}
|
||||
|
||||
// default EC2 images config (common for all architectures)
|
||||
func baseEc2ImageConfig() *distro.ImageConfig {
|
||||
return &distro.ImageConfig{
|
||||
Timezone: common.ToPtr("UTC"),
|
||||
TimeSynchronization: &osbuild.ChronyStageOptions{
|
||||
Servers: []osbuild.ChronyConfigServer{
|
||||
{
|
||||
Hostname: "169.254.169.123",
|
||||
Prefer: common.ToPtr(true),
|
||||
Iburst: common.ToPtr(true),
|
||||
Minpoll: common.ToPtr(4),
|
||||
Maxpoll: common.ToPtr(4),
|
||||
},
|
||||
},
|
||||
// empty string will remove any occurrences of the option from the configuration
|
||||
LeapsecTz: common.ToPtr(""),
|
||||
},
|
||||
Keyboard: &osbuild.KeymapStageOptions{
|
||||
Keymap: "us",
|
||||
X11Keymap: &osbuild.X11KeymapOptions{
|
||||
Layouts: []string{"us"},
|
||||
},
|
||||
},
|
||||
EnabledServices: []string{
|
||||
"sshd",
|
||||
"NetworkManager",
|
||||
"nm-cloud-setup.service",
|
||||
"nm-cloud-setup.timer",
|
||||
"cloud-init",
|
||||
"cloud-init-local",
|
||||
"cloud-config",
|
||||
"cloud-final",
|
||||
"reboot.target",
|
||||
},
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
Sysconfig: []*osbuild.SysconfigStageOptions{
|
||||
{
|
||||
Kernel: &osbuild.SysconfigKernelOptions{
|
||||
UpdateDefault: true,
|
||||
DefaultKernel: "kernel",
|
||||
},
|
||||
Network: &osbuild.SysconfigNetworkOptions{
|
||||
Networking: true,
|
||||
NoZeroConf: true,
|
||||
},
|
||||
NetworkScripts: &osbuild.NetworkScriptsOptions{
|
||||
IfcfgFiles: map[string]osbuild.IfcfgFile{
|
||||
"eth0": {
|
||||
Device: "eth0",
|
||||
Bootproto: osbuild.IfcfgBootprotoDHCP,
|
||||
OnBoot: common.ToPtr(true),
|
||||
Type: osbuild.IfcfgTypeEthernet,
|
||||
UserCtl: common.ToPtr(true),
|
||||
PeerDNS: common.ToPtr(true),
|
||||
IPv6Init: common.ToPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
RHSMConfig: map[subscription.RHSMStatus]*osbuild.RHSMStageOptions{
|
||||
subscription.RHSMConfigNoSubscription: {
|
||||
// RHBZ#1932802
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
Rhsm: &osbuild.SubManConfigRHSMSection{
|
||||
ManageRepos: common.ToPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
subscription.RHSMConfigWithSubscription: {
|
||||
// RHBZ#1932802
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
// do not disable the redhat.repo management if the user
|
||||
// explicitly request the system to be subscribed
|
||||
},
|
||||
},
|
||||
},
|
||||
SystemdLogind: []*osbuild.SystemdLogindStageOptions{
|
||||
{
|
||||
Filename: "00-getty-fixes.conf",
|
||||
Config: osbuild.SystemdLogindConfigDropin{
|
||||
|
||||
Login: osbuild.SystemdLogindConfigLoginSection{
|
||||
NAutoVTs: common.ToPtr(0),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
CloudInit: []*osbuild.CloudInitStageOptions{
|
||||
{
|
||||
Filename: "00-rhel-default-user.cfg",
|
||||
Config: osbuild.CloudInitConfigFile{
|
||||
SystemInfo: &osbuild.CloudInitConfigSystemInfo{
|
||||
DefaultUser: &osbuild.CloudInitConfigDefaultUser{
|
||||
Name: "ec2-user",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Modprobe: []*osbuild.ModprobeStageOptions{
|
||||
{
|
||||
Filename: "blacklist-nouveau.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("nouveau"),
|
||||
},
|
||||
},
|
||||
// COMPOSER-1807
|
||||
{
|
||||
Filename: "blacklist-amdgpu.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("amdgpu"),
|
||||
},
|
||||
},
|
||||
},
|
||||
DracutConf: []*osbuild.DracutConfStageOptions{
|
||||
{
|
||||
Filename: "sgdisk.conf",
|
||||
Config: osbuild.DracutConfigFile{
|
||||
Install: []string{"sgdisk"},
|
||||
},
|
||||
},
|
||||
},
|
||||
SystemdUnit: []*osbuild.SystemdUnitStageOptions{
|
||||
// RHBZ#1822863
|
||||
{
|
||||
Unit: "nm-cloud-setup.service",
|
||||
Dropin: "10-rh-enable-for-ec2.conf",
|
||||
Config: osbuild.SystemdServiceUnitDropin{
|
||||
Service: &osbuild.SystemdUnitServiceSection{
|
||||
Environment: "NM_CLOUD_SETUP_EC2=yes",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Authselect: &osbuild.AuthselectStageOptions{
|
||||
Profile: "sssd",
|
||||
},
|
||||
SshdConfig: &osbuild.SshdConfigStageOptions{
|
||||
Config: osbuild.SshdConfigConfig{
|
||||
PasswordAuthentication: common.ToPtr(false),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func defaultEc2ImageConfig(rd distribution) *distro.ImageConfig {
|
||||
ic := baseEc2ImageConfig()
|
||||
if rd.isRHEL() && common.VersionLessThan(rd.osVersion, "9.1") {
|
||||
ic = appendRHSM(ic)
|
||||
// Disable RHSM redhat.repo management
|
||||
rhsmConf := ic.RHSMConfig[subscription.RHSMConfigNoSubscription]
|
||||
rhsmConf.SubMan.Rhsm = &osbuild.SubManConfigRHSMSection{ManageRepos: common.ToPtr(false)}
|
||||
ic.RHSMConfig[subscription.RHSMConfigNoSubscription] = rhsmConf
|
||||
}
|
||||
// The RHSM configuration should not be applied since 8.7, but it is instead done by installing the redhat-cloud-client-configuration package.
|
||||
// See COMPOSER-1804 for more information.
|
||||
rhel87PlusEc2ImageConfigOverride := &distro.ImageConfig{
|
||||
RHSMConfig: map[subscription.RHSMStatus]*osbuild.RHSMStageOptions{},
|
||||
}
|
||||
if !common.VersionLessThan(rd.osVersion, "8.7") {
|
||||
ic = rhel87PlusEc2ImageConfigOverride.InheritFrom(ic)
|
||||
}
|
||||
|
||||
return ic
|
||||
}
|
||||
|
||||
// default AMI (EC2 BYOS) images config
|
||||
func defaultAMIImageConfig(rd distribution) *distro.ImageConfig {
|
||||
ic := defaultEc2ImageConfig(rd)
|
||||
if rd.isRHEL() {
|
||||
// defaultAMIImageConfig() adds the rhsm options only for RHEL < 9.1
|
||||
// Add it unconditionally for AMI
|
||||
ic = appendRHSM(ic)
|
||||
}
|
||||
return ic
|
||||
}
|
||||
|
||||
func defaultEc2ImageConfigX86_64(rd distribution) *distro.ImageConfig {
|
||||
ic := defaultEc2ImageConfig(rd)
|
||||
return appendEC2DracutX86_64(ic)
|
||||
}
|
||||
|
||||
func defaultAMIImageConfigX86_64(rd distribution) *distro.ImageConfig {
|
||||
ic := defaultAMIImageConfig(rd).InheritFrom(defaultEc2ImageConfigX86_64(rd))
|
||||
return appendEC2DracutX86_64(ic)
|
||||
}
|
||||
|
||||
func defaultEc2SapImageConfigX86_64(rd distribution) *distro.ImageConfig {
|
||||
// default EC2-SAP image config (x86_64)
|
||||
return sapImageConfig(rd).InheritFrom(defaultEc2ImageConfigX86_64(rd))
|
||||
}
|
||||
|
||||
// common package set for RHEL (BYOS/RHUI) and CentOS Stream images
|
||||
func ec2CommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"@core",
|
||||
"authselect-compat",
|
||||
"chrony",
|
||||
"cloud-init",
|
||||
"cloud-utils-growpart",
|
||||
"dhcp-client",
|
||||
"dracut-config-generic",
|
||||
"dracut-norescue",
|
||||
"gdisk",
|
||||
"grub2",
|
||||
"langpacks-en",
|
||||
"NetworkManager",
|
||||
"NetworkManager-cloud-setup",
|
||||
"redhat-release",
|
||||
"redhat-release-eula",
|
||||
"rsync",
|
||||
"tar",
|
||||
"yum-utils",
|
||||
},
|
||||
Exclude: []string{
|
||||
"aic94xx-firmware",
|
||||
"alsa-firmware",
|
||||
"alsa-tools-firmware",
|
||||
"biosdevname",
|
||||
"firewalld",
|
||||
"iprutils",
|
||||
"ivtv-firmware",
|
||||
"iwl1000-firmware",
|
||||
"iwl100-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl3945-firmware",
|
||||
"iwl4965-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6000-firmware",
|
||||
"iwl6000g2a-firmware",
|
||||
"iwl6000g2b-firmware",
|
||||
"iwl6050-firmware",
|
||||
"iwl7260-firmware",
|
||||
"libertas-sd8686-firmware",
|
||||
"libertas-sd8787-firmware",
|
||||
"libertas-usb8388-firmware",
|
||||
"plymouth",
|
||||
// RHBZ#2075815
|
||||
"qemu-guest-agent",
|
||||
},
|
||||
}.Append(distroSpecificPackageSet(t))
|
||||
}
|
||||
|
||||
// common rhel ec2 RHUI image package set
|
||||
func rhelEc2CommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := ec2CommonPackageSet(t)
|
||||
// Include "redhat-cloud-client-configuration" on 8.7+ (COMPOSER-1804)
|
||||
if !common.VersionLessThan(t.arch.distro.osVersion, "8.7") {
|
||||
ps.Include = append(ps.Include, "redhat-cloud-client-configuration")
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
// rhel-ec2 image package set
|
||||
func rhelEc2PackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ec2PackageSet := rhelEc2CommonPackageSet(t)
|
||||
ec2PackageSet.Include = append(ec2PackageSet.Include, "rh-amazon-rhui-client")
|
||||
ec2PackageSet.Exclude = append(ec2PackageSet.Exclude, "alsa-lib")
|
||||
return ec2PackageSet
|
||||
}
|
||||
|
||||
// rhel-ha-ec2 image package set
|
||||
func rhelEc2HaPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ec2HaPackageSet := rhelEc2CommonPackageSet(t)
|
||||
ec2HaPackageSet.Include = append(ec2HaPackageSet.Include,
|
||||
"fence-agents-all",
|
||||
"pacemaker",
|
||||
"pcs",
|
||||
"rh-amazon-rhui-client-ha",
|
||||
)
|
||||
ec2HaPackageSet.Exclude = append(ec2HaPackageSet.Exclude, "alsa-lib")
|
||||
return ec2HaPackageSet
|
||||
}
|
||||
|
||||
// rhel-sap-ec2 image package set
|
||||
// Includes the common ec2 package set, the common SAP packages, and
|
||||
// the amazon rhui sap package
|
||||
func rhelEc2SapPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"rh-amazon-rhui-client-sap-bundle-e4s",
|
||||
},
|
||||
}.Append(rhelEc2CommonPackageSet(t)).Append(SapPackageSet(t))
|
||||
}
|
||||
|
||||
// Add RHSM config options to ImageConfig.
|
||||
// Used for RHEL distros.
|
||||
func appendRHSM(ic *distro.ImageConfig) *distro.ImageConfig {
|
||||
rhsm := &distro.ImageConfig{
|
||||
RHSMConfig: map[subscription.RHSMStatus]*osbuild.RHSMStageOptions{
|
||||
subscription.RHSMConfigNoSubscription: {
|
||||
// RHBZ#1932802
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
// Don't disable RHSM redhat.repo management on the AMI
|
||||
// image, which is BYOS and does not use RHUI for content.
|
||||
// Otherwise subscribing the system manually after booting
|
||||
// it would result in empty redhat.repo. Without RHUI, such
|
||||
// system would have no way to get Red Hat content, but
|
||||
// enable the repo management manually, which would be very
|
||||
// confusing.
|
||||
},
|
||||
},
|
||||
subscription.RHSMConfigWithSubscription: {
|
||||
// RHBZ#1932802
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
// do not disable the redhat.repo management if the user
|
||||
// explicitly request the system to be subscribed
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return rhsm.InheritFrom(ic)
|
||||
}
|
||||
|
||||
func appendEC2DracutX86_64(ic *distro.ImageConfig) *distro.ImageConfig {
|
||||
ic.DracutConf = append(ic.DracutConf,
|
||||
&osbuild.DracutConfStageOptions{
|
||||
Filename: "ec2.conf",
|
||||
Config: osbuild.DracutConfigFile{
|
||||
AddDrivers: []string{
|
||||
"nvme",
|
||||
"xen-blkfront",
|
||||
},
|
||||
},
|
||||
})
|
||||
return ic
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
package rhel8
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
)
|
||||
|
||||
type architecture struct {
|
||||
distro *distribution
|
||||
name string
|
||||
imageTypes map[string]distro.ImageType
|
||||
imageTypeAliases map[string]string
|
||||
}
|
||||
|
||||
func (a *architecture) Name() string {
|
||||
return a.name
|
||||
}
|
||||
|
||||
func (a *architecture) ListImageTypes() []string {
|
||||
itNames := make([]string, 0, len(a.imageTypes))
|
||||
for name := range a.imageTypes {
|
||||
itNames = append(itNames, name)
|
||||
}
|
||||
sort.Strings(itNames)
|
||||
return itNames
|
||||
}
|
||||
|
||||
func (a *architecture) GetImageType(name string) (distro.ImageType, error) {
|
||||
t, exists := a.imageTypes[name]
|
||||
if !exists {
|
||||
aliasForName, exists := a.imageTypeAliases[name]
|
||||
if !exists {
|
||||
return nil, errors.New("invalid image type: " + name)
|
||||
}
|
||||
t, exists = a.imageTypes[aliasForName]
|
||||
if !exists {
|
||||
panic(fmt.Sprintf("image type '%s' is an alias to a non-existing image type '%s'", name, aliasForName))
|
||||
}
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (a *architecture) addImageTypes(platform platform.Platform, imageTypes ...imageType) {
|
||||
if a.imageTypes == nil {
|
||||
a.imageTypes = map[string]distro.ImageType{}
|
||||
}
|
||||
for idx := range imageTypes {
|
||||
it := imageTypes[idx]
|
||||
if _, e := a.imageTypes[it.name]; e {
|
||||
panic("already added: " + it.name)
|
||||
}
|
||||
it.arch = a
|
||||
it.platform = platform
|
||||
a.imageTypes[it.name] = &it
|
||||
for _, alias := range it.nameAliases {
|
||||
if a.imageTypeAliases == nil {
|
||||
a.imageTypeAliases = map[string]string{}
|
||||
}
|
||||
if existingAliasFor, exists := a.imageTypeAliases[alias]; exists {
|
||||
panic(fmt.Sprintf("image type alias '%s' for '%s' is already defined for another image type '%s'", alias, it.name, existingAliasFor))
|
||||
}
|
||||
a.imageTypeAliases[alias] = it.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *architecture) Distro() distro.Distro {
|
||||
return a.distro
|
||||
}
|
||||
|
|
@ -1,722 +0,0 @@
|
|||
package rhel8
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/disk"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/shell"
|
||||
"github.com/osbuild/osbuild-composer/internal/subscription"
|
||||
)
|
||||
|
||||
const defaultAzureKernelOptions = "ro crashkernel=auto console=tty1 console=ttyS0 earlyprintk=ttyS0 rootdelay=300"
|
||||
|
||||
func azureRhuiImgType() imageType {
|
||||
return imageType{
|
||||
name: "azure-rhui",
|
||||
filename: "disk.vhd.xz",
|
||||
mimeType: "application/xz",
|
||||
compression: "xz",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: azureRhuiPackageSet,
|
||||
},
|
||||
defaultImageConfig: defaultAzureRhuiImageConfig.InheritFrom(defaultVhdImageConfig()),
|
||||
kernelOptions: defaultAzureKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 64 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "vpc", "xz"},
|
||||
exports: []string{"xz"},
|
||||
basePartitionTables: azureRhuiBasePartitionTables,
|
||||
}
|
||||
}
|
||||
|
||||
func azureSapRhuiImgType(rd distribution) imageType {
|
||||
return imageType{
|
||||
name: "azure-sap-rhui",
|
||||
filename: "disk.vhd.xz",
|
||||
mimeType: "application/xz",
|
||||
compression: "xz",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: azureSapPackageSet,
|
||||
},
|
||||
defaultImageConfig: defaultAzureRhuiImageConfig.InheritFrom(sapAzureImageConfig(rd)),
|
||||
kernelOptions: defaultAzureKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 64 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "vpc", "xz"},
|
||||
exports: []string{"xz"},
|
||||
basePartitionTables: azureRhuiBasePartitionTables,
|
||||
}
|
||||
}
|
||||
|
||||
func azureByosImgType() imageType {
|
||||
return imageType{
|
||||
name: "vhd",
|
||||
filename: "disk.vhd",
|
||||
mimeType: "application/x-vhd",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: azurePackageSet,
|
||||
},
|
||||
defaultImageConfig: defaultAzureByosImageConfig.InheritFrom(defaultVhdImageConfig()),
|
||||
kernelOptions: defaultAzureKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 4 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "vpc"},
|
||||
exports: []string{"vpc"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
}
|
||||
|
||||
// Azure non-RHEL image type
|
||||
func azureImgType() imageType {
|
||||
return imageType{
|
||||
name: "vhd",
|
||||
filename: "disk.vhd",
|
||||
mimeType: "application/x-vhd",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: azurePackageSet,
|
||||
},
|
||||
defaultImageConfig: defaultVhdImageConfig(),
|
||||
kernelOptions: defaultAzureKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 4 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "vpc"},
|
||||
exports: []string{"vpc"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
}
|
||||
|
||||
func azureEap7RhuiImgType() imageType {
|
||||
return imageType{
|
||||
name: "azure-eap7-rhui",
|
||||
workload: eapWorkload(),
|
||||
filename: "disk.vhd.xz",
|
||||
mimeType: "application/xz",
|
||||
compression: "xz",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: azureEapPackageSet,
|
||||
},
|
||||
defaultImageConfig: defaultAzureEapImageConfig.InheritFrom(defaultAzureRhuiImageConfig.InheritFrom(defaultAzureImageConfig)),
|
||||
kernelOptions: defaultAzureKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 64 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "vpc", "xz"},
|
||||
exports: []string{"xz"},
|
||||
basePartitionTables: azureRhuiBasePartitionTables,
|
||||
}
|
||||
}
|
||||
|
||||
// PACKAGE SETS
|
||||
|
||||
// Common Azure image package set
|
||||
func azureCommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"@Server",
|
||||
"NetworkManager",
|
||||
"NetworkManager-cloud-setup",
|
||||
"WALinuxAgent",
|
||||
"bzip2",
|
||||
"cloud-init",
|
||||
"cloud-utils-growpart",
|
||||
"cryptsetup-reencrypt",
|
||||
"dracut-config-generic",
|
||||
"dracut-norescue",
|
||||
"efibootmgr",
|
||||
"gdisk",
|
||||
"hyperv-daemons",
|
||||
"kernel",
|
||||
"kernel-core",
|
||||
"kernel-modules",
|
||||
"langpacks-en",
|
||||
"lvm2",
|
||||
"nvme-cli",
|
||||
"patch",
|
||||
"rng-tools",
|
||||
"selinux-policy-targeted",
|
||||
"uuid",
|
||||
"yum-utils",
|
||||
},
|
||||
Exclude: []string{
|
||||
"NetworkManager-config-server",
|
||||
"aic94xx-firmware",
|
||||
"alsa-firmware",
|
||||
"alsa-sof-firmware",
|
||||
"alsa-tools-firmware",
|
||||
"biosdevname",
|
||||
"bolt",
|
||||
"buildah",
|
||||
"cockpit-podman",
|
||||
"containernetworking-plugins",
|
||||
"dnf-plugin-spacewalk",
|
||||
"dracut-config-rescue",
|
||||
"glibc-all-langpacks",
|
||||
"iprutils",
|
||||
"ivtv-firmware",
|
||||
"iwl100-firmware",
|
||||
"iwl1000-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl3945-firmware",
|
||||
"iwl4965-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6000-firmware",
|
||||
"iwl6000g2a-firmware",
|
||||
"iwl6000g2b-firmware",
|
||||
"iwl6050-firmware",
|
||||
"iwl7260-firmware",
|
||||
"libertas-sd8686-firmware",
|
||||
"libertas-sd8787-firmware",
|
||||
"libertas-usb8388-firmware",
|
||||
"plymouth",
|
||||
"podman",
|
||||
"python3-dnf-plugin-spacewalk",
|
||||
"python3-hwdata",
|
||||
"python3-rhnlib",
|
||||
"rhn-check",
|
||||
"rhn-client-tools",
|
||||
"rhn-setup",
|
||||
"rhnlib",
|
||||
"rhnsd",
|
||||
"usb_modeswitch",
|
||||
},
|
||||
}.Append(distroSpecificPackageSet(t))
|
||||
|
||||
if t.arch.distro.isRHEL() {
|
||||
ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"insights-client",
|
||||
"rhc",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
// Azure BYOS image package set
|
||||
func azurePackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"firewalld",
|
||||
},
|
||||
Exclude: []string{
|
||||
"alsa-lib",
|
||||
},
|
||||
}.Append(azureCommonPackageSet(t))
|
||||
}
|
||||
|
||||
// Azure RHUI image package set
|
||||
func azureRhuiPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"firewalld",
|
||||
"rhui-azure-rhel8",
|
||||
},
|
||||
Exclude: []string{
|
||||
"alsa-lib",
|
||||
},
|
||||
}.Append(azureCommonPackageSet(t))
|
||||
}
|
||||
|
||||
// Azure SAP image package set
|
||||
// Includes the common azure package set, the common SAP packages, and
|
||||
// the azure rhui sap package.
|
||||
func azureSapPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"firewalld",
|
||||
"rhui-azure-rhel8-sap-ha",
|
||||
},
|
||||
}.Append(azureCommonPackageSet(t)).Append(SapPackageSet(t))
|
||||
}
|
||||
|
||||
func azureEapPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"rhui-azure-rhel8",
|
||||
},
|
||||
Exclude: []string{
|
||||
"firewalld",
|
||||
},
|
||||
}.Append(azureCommonPackageSet(t))
|
||||
}
|
||||
|
||||
// PARTITION TABLES
|
||||
|
||||
var azureRhuiBasePartitionTables = distro.BasePartitionTableMap{
|
||||
platform.ARCH_X86_64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Size: 64 * common.GibiByte,
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 500 * common.MebiByte,
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 500 * common.MebiByte,
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.FilesystemDataUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.MebiByte,
|
||||
Bootable: true,
|
||||
Type: disk.BIOSBootPartitionGUID,
|
||||
UUID: disk.BIOSBootPartitionUUID,
|
||||
},
|
||||
{
|
||||
Type: disk.LVMPartitionGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.LVMVolumeGroup{
|
||||
Name: "rootvg",
|
||||
Description: "built with lvm2 and osbuild",
|
||||
LogicalVolumes: []disk.LVMLogicalVolume{
|
||||
{
|
||||
Size: 1 * common.GibiByte,
|
||||
Name: "homelv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "home",
|
||||
Mountpoint: "/home",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte,
|
||||
Name: "rootlv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte,
|
||||
Name: "tmplv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "tmp",
|
||||
Mountpoint: "/tmp",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 10 * common.GibiByte,
|
||||
Name: "usrlv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "usr",
|
||||
Mountpoint: "/usr",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 10 * common.GibiByte,
|
||||
Name: "varlv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "var",
|
||||
Mountpoint: "/var",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
platform.ARCH_AARCH64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Size: 64 * common.GibiByte,
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 500 * common.MebiByte,
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 500 * common.MebiByte,
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.FilesystemDataUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: disk.LVMPartitionGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.LVMVolumeGroup{
|
||||
Name: "rootvg",
|
||||
Description: "built with lvm2 and osbuild",
|
||||
LogicalVolumes: []disk.LVMLogicalVolume{
|
||||
{
|
||||
Size: 1 * common.GibiByte,
|
||||
Name: "homelv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "home",
|
||||
Mountpoint: "/home",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte,
|
||||
Name: "rootlv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte,
|
||||
Name: "tmplv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "tmp",
|
||||
Mountpoint: "/tmp",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 10 * common.GibiByte,
|
||||
Name: "usrlv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "usr",
|
||||
Mountpoint: "/usr",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 10 * common.GibiByte,
|
||||
Name: "varlv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "var",
|
||||
Mountpoint: "/var",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var defaultAzureImageConfig = &distro.ImageConfig{
|
||||
Timezone: common.ToPtr("Etc/UTC"),
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
Keyboard: &osbuild.KeymapStageOptions{
|
||||
Keymap: "us",
|
||||
X11Keymap: &osbuild.X11KeymapOptions{
|
||||
Layouts: []string{"us"},
|
||||
},
|
||||
},
|
||||
Sysconfig: []*osbuild.SysconfigStageOptions{
|
||||
{
|
||||
Kernel: &osbuild.SysconfigKernelOptions{
|
||||
UpdateDefault: true,
|
||||
DefaultKernel: "kernel-core",
|
||||
},
|
||||
Network: &osbuild.SysconfigNetworkOptions{
|
||||
Networking: true,
|
||||
NoZeroConf: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
EnabledServices: []string{
|
||||
"nm-cloud-setup.service",
|
||||
"nm-cloud-setup.timer",
|
||||
"sshd",
|
||||
"systemd-resolved",
|
||||
"waagent",
|
||||
},
|
||||
SshdConfig: &osbuild.SshdConfigStageOptions{
|
||||
Config: osbuild.SshdConfigConfig{
|
||||
ClientAliveInterval: common.ToPtr(180),
|
||||
},
|
||||
},
|
||||
Modprobe: []*osbuild.ModprobeStageOptions{
|
||||
{
|
||||
Filename: "blacklist-amdgpu.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("amdgpu"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Filename: "blacklist-intel-cstate.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("intel_cstate"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Filename: "blacklist-floppy.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("floppy"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Filename: "blacklist-nouveau.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("nouveau"),
|
||||
osbuild.NewModprobeConfigCmdBlacklist("lbm-nouveau"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Filename: "blacklist-skylake-edac.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("skx_edac"),
|
||||
},
|
||||
},
|
||||
},
|
||||
CloudInit: []*osbuild.CloudInitStageOptions{
|
||||
{
|
||||
Filename: "10-azure-kvp.cfg",
|
||||
Config: osbuild.CloudInitConfigFile{
|
||||
Reporting: &osbuild.CloudInitConfigReporting{
|
||||
Logging: &osbuild.CloudInitConfigReportingHandlers{
|
||||
Type: "log",
|
||||
},
|
||||
Telemetry: &osbuild.CloudInitConfigReportingHandlers{
|
||||
Type: "hyperv",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Filename: "91-azure_datasource.cfg",
|
||||
Config: osbuild.CloudInitConfigFile{
|
||||
Datasource: &osbuild.CloudInitConfigDatasource{
|
||||
Azure: &osbuild.CloudInitConfigDatasourceAzure{
|
||||
ApplyNetworkConfig: false,
|
||||
},
|
||||
},
|
||||
DatasourceList: []string{
|
||||
"Azure",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
PwQuality: &osbuild.PwqualityConfStageOptions{
|
||||
Config: osbuild.PwqualityConfConfig{
|
||||
Minlen: common.ToPtr(6),
|
||||
Minclass: common.ToPtr(3),
|
||||
Dcredit: common.ToPtr(0),
|
||||
Ucredit: common.ToPtr(0),
|
||||
Lcredit: common.ToPtr(0),
|
||||
Ocredit: common.ToPtr(0),
|
||||
},
|
||||
},
|
||||
WAAgentConfig: &osbuild.WAAgentConfStageOptions{
|
||||
Config: osbuild.WAAgentConfig{
|
||||
RDFormat: common.ToPtr(false),
|
||||
RDEnableSwap: common.ToPtr(false),
|
||||
},
|
||||
},
|
||||
Grub2Config: &osbuild.GRUB2Config{
|
||||
TerminalInput: []string{"serial", "console"},
|
||||
TerminalOutput: []string{"serial", "console"},
|
||||
Serial: "serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1",
|
||||
Timeout: 10,
|
||||
},
|
||||
UdevRules: &osbuild.UdevRulesStageOptions{
|
||||
Filename: "/etc/udev/rules.d/68-azure-sriov-nm-unmanaged.rules",
|
||||
Rules: osbuild.UdevRules{
|
||||
osbuild.UdevRuleComment{
|
||||
Comment: []string{
|
||||
"Accelerated Networking on Azure exposes a new SRIOV interface to the VM.",
|
||||
"This interface is transparently bonded to the synthetic interface,",
|
||||
"so NetworkManager should just ignore any SRIOV interfaces.",
|
||||
},
|
||||
},
|
||||
osbuild.NewUdevRule(
|
||||
[]osbuild.UdevKV{
|
||||
{K: "SUBSYSTEM", O: "==", V: "net"},
|
||||
{K: "DRIVERS", O: "==", V: "hv_pci"},
|
||||
{K: "ACTION", O: "==", V: "add"},
|
||||
{K: "ENV", A: "NM_UNMANAGED", O: "=", V: "1"},
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
SystemdUnit: []*osbuild.SystemdUnitStageOptions{
|
||||
{
|
||||
Unit: "nm-cloud-setup.service",
|
||||
Dropin: "10-rh-enable-for-azure.conf",
|
||||
Config: osbuild.SystemdServiceUnitDropin{
|
||||
Service: &osbuild.SystemdUnitServiceSection{
|
||||
Environment: "NM_CLOUD_SETUP_AZURE=yes",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
}
|
||||
|
||||
// Diff of the default Image Config compare to the `defaultAzureImageConfig`
|
||||
var defaultAzureByosImageConfig = &distro.ImageConfig{
|
||||
GPGKeyFiles: []string{
|
||||
"/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release",
|
||||
},
|
||||
RHSMConfig: map[subscription.RHSMStatus]*osbuild.RHSMStageOptions{
|
||||
subscription.RHSMConfigNoSubscription: {
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
// Don't disable RHSM redhat.repo management on the GCE
|
||||
// image, which is BYOS and does not use RHUI for content.
|
||||
// Otherwise subscribing the system manually after booting
|
||||
// it would result in empty redhat.repo. Without RHUI, such
|
||||
// system would have no way to get Red Hat content, but
|
||||
// enable the repo management manually, which would be very
|
||||
// confusing.
|
||||
},
|
||||
},
|
||||
subscription.RHSMConfigWithSubscription: {
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
// do not disable the redhat.repo management if the user
|
||||
// explicitly request the system to be subscribed
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Diff of the default Image Config compare to the `defaultAzureImageConfig`
|
||||
var defaultAzureRhuiImageConfig = &distro.ImageConfig{
|
||||
GPGKeyFiles: []string{
|
||||
"/etc/pki/rpm-gpg/RPM-GPG-KEY-microsoft-azure-release",
|
||||
"/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release",
|
||||
},
|
||||
RHSMConfig: map[subscription.RHSMStatus]*osbuild.RHSMStageOptions{
|
||||
subscription.RHSMConfigNoSubscription: {
|
||||
DnfPlugins: &osbuild.RHSMStageOptionsDnfPlugins{
|
||||
SubscriptionManager: &osbuild.RHSMStageOptionsDnfPlugin{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
Rhsm: &osbuild.SubManConfigRHSMSection{
|
||||
ManageRepos: common.ToPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
subscription.RHSMConfigWithSubscription: {
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
// do not disable the redhat.repo management if the user
|
||||
// explicitly request the system to be subscribed
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const wildflyPath = "/opt/rh/eap7/root/usr/share/wildfly"
|
||||
|
||||
var defaultAzureEapImageConfig = &distro.ImageConfig{
|
||||
// shell env vars for EAP
|
||||
ShellInit: []shell.InitFile{
|
||||
{
|
||||
Filename: "eap_env.sh",
|
||||
Variables: []shell.EnvironmentVariable{
|
||||
{
|
||||
Key: "EAP_HOME",
|
||||
Value: wildflyPath,
|
||||
},
|
||||
{
|
||||
Key: "JBOSS_HOME",
|
||||
Value: wildflyPath,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func defaultVhdImageConfig() *distro.ImageConfig {
|
||||
imageConfig := &distro.ImageConfig{
|
||||
EnabledServices: append(defaultAzureImageConfig.EnabledServices, "firewalld"),
|
||||
}
|
||||
return imageConfig.InheritFrom(defaultAzureImageConfig)
|
||||
}
|
||||
|
||||
func sapAzureImageConfig(rd distribution) *distro.ImageConfig {
|
||||
return sapImageConfig(rd).InheritFrom(defaultVhdImageConfig())
|
||||
}
|
||||
|
|
@ -1,302 +0,0 @@
|
|||
package rhel8
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
func imageInstaller() imageType {
|
||||
return imageType{
|
||||
name: "image-installer",
|
||||
filename: "installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: bareMetalPackageSet,
|
||||
installerPkgsKey: anacondaPackageSet,
|
||||
},
|
||||
rpmOstree: false,
|
||||
bootISO: true,
|
||||
bootable: true,
|
||||
image: imageInstallerImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"anaconda-tree", "rootfs-image", "efiboot-tree", "os", "bootiso-tree", "bootiso"},
|
||||
exports: []string{"bootiso"},
|
||||
}
|
||||
}
|
||||
|
||||
func tarImgType() imageType {
|
||||
return imageType{
|
||||
name: "tar",
|
||||
filename: "root.tar.xz",
|
||||
mimeType: "application/x-tar",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: func(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{"policycoreutils", "selinux-policy-targeted"},
|
||||
Exclude: []string{"rng-tools"},
|
||||
}
|
||||
},
|
||||
},
|
||||
image: tarImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "archive"},
|
||||
exports: []string{"archive"},
|
||||
}
|
||||
}
|
||||
|
||||
func bareMetalPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"@core",
|
||||
"authselect-compat",
|
||||
"chrony",
|
||||
"cockpit-system",
|
||||
"cockpit-ws",
|
||||
"dhcp-client",
|
||||
"dnf",
|
||||
"dnf-utils",
|
||||
"dosfstools",
|
||||
"dracut-norescue",
|
||||
"iwl1000-firmware",
|
||||
"iwl100-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl3945-firmware",
|
||||
"iwl4965-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6000-firmware",
|
||||
"iwl6000g2a-firmware",
|
||||
"iwl6000g2b-firmware",
|
||||
"iwl6050-firmware",
|
||||
"iwl7260-firmware",
|
||||
"lvm2",
|
||||
"net-tools",
|
||||
"NetworkManager",
|
||||
"nfs-utils",
|
||||
"oddjob",
|
||||
"oddjob-mkhomedir",
|
||||
"policycoreutils",
|
||||
"psmisc",
|
||||
"python3-jsonschema",
|
||||
"qemu-guest-agent",
|
||||
"redhat-release",
|
||||
"redhat-release-eula",
|
||||
"rsync",
|
||||
"selinux-policy-targeted",
|
||||
"tar",
|
||||
"tcpdump",
|
||||
"yum",
|
||||
},
|
||||
Exclude: nil,
|
||||
}.Append(distroSpecificPackageSet(t))
|
||||
|
||||
// Ensure to not pull in subscription-manager on non-RHEL distro
|
||||
if t.arch.distro.isRHEL() {
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"subscription-manager-cockpit",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
func installerPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"anaconda-dracut",
|
||||
"curl",
|
||||
"dracut-config-generic",
|
||||
"dracut-network",
|
||||
"hostname",
|
||||
"iwl100-firmware",
|
||||
"iwl1000-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6000-firmware",
|
||||
"iwl6050-firmware",
|
||||
"iwl7260-firmware",
|
||||
"kernel",
|
||||
"less",
|
||||
"nfs-utils",
|
||||
"openssh-clients",
|
||||
"ostree",
|
||||
"plymouth",
|
||||
"prefixdevname",
|
||||
"rng-tools",
|
||||
"rpcbind",
|
||||
"selinux-policy-targeted",
|
||||
"systemd",
|
||||
"tar",
|
||||
"xfsprogs",
|
||||
"xz",
|
||||
},
|
||||
}
|
||||
|
||||
switch t.arch.Name() {
|
||||
case platform.ARCH_X86_64.String():
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"biosdevname",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
func anacondaPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
|
||||
// common installer packages
|
||||
ps := installerPackageSet(t)
|
||||
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"aajohan-comfortaa-fonts",
|
||||
"abattis-cantarell-fonts",
|
||||
"alsa-firmware",
|
||||
"alsa-tools-firmware",
|
||||
"anaconda",
|
||||
"anaconda-install-env-deps",
|
||||
"anaconda-widgets",
|
||||
"audit",
|
||||
"bind-utils",
|
||||
"bitmap-fangsongti-fonts",
|
||||
"bzip2",
|
||||
"cryptsetup",
|
||||
"dbus-x11",
|
||||
"dejavu-sans-fonts",
|
||||
"dejavu-sans-mono-fonts",
|
||||
"device-mapper-persistent-data",
|
||||
"dnf",
|
||||
"dump",
|
||||
"ethtool",
|
||||
"fcoe-utils",
|
||||
"ftp",
|
||||
"gdb-gdbserver",
|
||||
"gdisk",
|
||||
"gfs2-utils",
|
||||
"glibc-all-langpacks",
|
||||
"google-noto-sans-cjk-ttc-fonts",
|
||||
"gsettings-desktop-schemas",
|
||||
"hdparm",
|
||||
"hexedit",
|
||||
"initscripts",
|
||||
"ipmitool",
|
||||
"iwl3945-firmware",
|
||||
"iwl4965-firmware",
|
||||
"iwl6000g2a-firmware",
|
||||
"iwl6000g2b-firmware",
|
||||
"jomolhari-fonts",
|
||||
"kacst-farsi-fonts",
|
||||
"kacst-qurn-fonts",
|
||||
"kbd",
|
||||
"kbd-misc",
|
||||
"kdump-anaconda-addon",
|
||||
"khmeros-base-fonts",
|
||||
"libblockdev-lvm-dbus",
|
||||
"libertas-sd8686-firmware",
|
||||
"libertas-sd8787-firmware",
|
||||
"libertas-usb8388-firmware",
|
||||
"libertas-usb8388-olpc-firmware",
|
||||
"libibverbs",
|
||||
"libreport-plugin-bugzilla",
|
||||
"libreport-plugin-reportuploader",
|
||||
"libreport-rhel-anaconda-bugzilla",
|
||||
"librsvg2",
|
||||
"linux-firmware",
|
||||
"lklug-fonts",
|
||||
"lldpad",
|
||||
"lohit-assamese-fonts",
|
||||
"lohit-bengali-fonts",
|
||||
"lohit-devanagari-fonts",
|
||||
"lohit-gujarati-fonts",
|
||||
"lohit-gurmukhi-fonts",
|
||||
"lohit-kannada-fonts",
|
||||
"lohit-odia-fonts",
|
||||
"lohit-tamil-fonts",
|
||||
"lohit-telugu-fonts",
|
||||
"lsof",
|
||||
"madan-fonts",
|
||||
"metacity",
|
||||
"mtr",
|
||||
"mt-st",
|
||||
"net-tools",
|
||||
"nmap-ncat",
|
||||
"nm-connection-editor",
|
||||
"nss-tools",
|
||||
"openssh-server",
|
||||
"oscap-anaconda-addon",
|
||||
"pciutils",
|
||||
"perl-interpreter",
|
||||
"pigz",
|
||||
"python3-pyatspi",
|
||||
"rdma-core",
|
||||
"redhat-release-eula",
|
||||
"rpm-ostree",
|
||||
"rsync",
|
||||
"rsyslog",
|
||||
"sg3_utils",
|
||||
"sil-abyssinica-fonts",
|
||||
"sil-padauk-fonts",
|
||||
"sil-scheherazade-fonts",
|
||||
"smartmontools",
|
||||
"smc-meera-fonts",
|
||||
"spice-vdagent",
|
||||
"strace",
|
||||
"system-storage-manager",
|
||||
"thai-scalable-waree-fonts",
|
||||
"tigervnc-server-minimal",
|
||||
"tigervnc-server-module",
|
||||
"udisks2",
|
||||
"udisks2-iscsi",
|
||||
"usbutils",
|
||||
"vim-minimal",
|
||||
"volume_key",
|
||||
"wget",
|
||||
"xfsdump",
|
||||
"xorg-x11-drivers",
|
||||
"xorg-x11-fonts-misc",
|
||||
"xorg-x11-server-utils",
|
||||
"xorg-x11-server-Xorg",
|
||||
"xorg-x11-xauth",
|
||||
},
|
||||
})
|
||||
|
||||
ps = ps.Append(anacondaBootPackageSet(t))
|
||||
|
||||
switch t.arch.Name() {
|
||||
case platform.ARCH_X86_64.String():
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"biosdevname",
|
||||
"dmidecode",
|
||||
"memtest86+",
|
||||
},
|
||||
})
|
||||
|
||||
case platform.ARCH_AARCH64.String():
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"dmidecode",
|
||||
},
|
||||
})
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported arch: %s", t.arch.Name()))
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
|
@ -1,506 +0,0 @@
|
|||
package rhel8
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/oscap"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/runner"
|
||||
)
|
||||
|
||||
var (
|
||||
// rhel8 allow all
|
||||
oscapProfileAllowList = []oscap.Profile{
|
||||
oscap.AnssiBp28Enhanced,
|
||||
oscap.AnssiBp28High,
|
||||
oscap.AnssiBp28Intermediary,
|
||||
oscap.AnssiBp28Minimal,
|
||||
oscap.Cis,
|
||||
oscap.CisServerL1,
|
||||
oscap.CisWorkstationL1,
|
||||
oscap.CisWorkstationL2,
|
||||
oscap.Cui,
|
||||
oscap.E8,
|
||||
oscap.Hippa,
|
||||
oscap.IsmO,
|
||||
oscap.Ospp,
|
||||
oscap.PciDss,
|
||||
oscap.Stig,
|
||||
oscap.StigGui,
|
||||
}
|
||||
)
|
||||
|
||||
type distribution struct {
|
||||
name string
|
||||
product string
|
||||
osVersion string
|
||||
releaseVersion string
|
||||
modulePlatformID string
|
||||
vendor string
|
||||
ostreeRefTmpl string
|
||||
isolabelTmpl string
|
||||
runner runner.Runner
|
||||
arches map[string]distro.Arch
|
||||
defaultImageConfig *distro.ImageConfig
|
||||
}
|
||||
|
||||
// RHEL-based OS image configuration defaults
|
||||
var defaultDistroImageConfig = &distro.ImageConfig{
|
||||
Timezone: common.ToPtr("America/New_York"),
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
Sysconfig: []*osbuild.SysconfigStageOptions{
|
||||
{
|
||||
Kernel: &osbuild.SysconfigKernelOptions{
|
||||
UpdateDefault: true,
|
||||
DefaultKernel: "kernel",
|
||||
},
|
||||
Network: &osbuild.SysconfigNetworkOptions{
|
||||
Networking: true,
|
||||
NoZeroConf: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func (d *distribution) Name() string {
|
||||
return d.name
|
||||
}
|
||||
|
||||
func (d *distribution) Releasever() string {
|
||||
return d.releaseVersion
|
||||
}
|
||||
|
||||
func (d *distribution) ModulePlatformID() string {
|
||||
return d.modulePlatformID
|
||||
}
|
||||
|
||||
func (d *distribution) OSTreeRef() string {
|
||||
return d.ostreeRefTmpl
|
||||
}
|
||||
|
||||
func (d *distribution) ListArches() []string {
|
||||
archNames := make([]string, 0, len(d.arches))
|
||||
for name := range d.arches {
|
||||
archNames = append(archNames, name)
|
||||
}
|
||||
sort.Strings(archNames)
|
||||
return archNames
|
||||
}
|
||||
|
||||
func (d *distribution) GetArch(name string) (distro.Arch, error) {
|
||||
arch, exists := d.arches[name]
|
||||
if !exists {
|
||||
return nil, errors.New("invalid architecture: " + name)
|
||||
}
|
||||
return arch, nil
|
||||
}
|
||||
|
||||
func (d *distribution) addArches(arches ...architecture) {
|
||||
if d.arches == nil {
|
||||
d.arches = map[string]distro.Arch{}
|
||||
}
|
||||
|
||||
// Do not make copies of architectures, as opposed to image types,
|
||||
// because architecture definitions are not used by more than a single
|
||||
// distro definition.
|
||||
for idx := range arches {
|
||||
d.arches[arches[idx].name] = &arches[idx]
|
||||
}
|
||||
}
|
||||
|
||||
func (d *distribution) isRHEL() bool {
|
||||
return strings.HasPrefix(d.name, "rhel")
|
||||
}
|
||||
|
||||
func (d *distribution) getDefaultImageConfig() *distro.ImageConfig {
|
||||
return d.defaultImageConfig
|
||||
}
|
||||
|
||||
// New creates a new distro object, defining the supported architectures and image types
|
||||
func New() distro.Distro {
|
||||
// default minor: create default minor version (current GA) and rename it
|
||||
d := newDistro("rhel", 7)
|
||||
d.name = "rhel-8"
|
||||
return d
|
||||
|
||||
}
|
||||
|
||||
func NewRHEL84() distro.Distro {
|
||||
return newDistro("rhel", 4)
|
||||
}
|
||||
|
||||
func NewRHEL85() distro.Distro {
|
||||
return newDistro("rhel", 5)
|
||||
}
|
||||
|
||||
func NewRHEL86() distro.Distro {
|
||||
return newDistro("rhel", 6)
|
||||
}
|
||||
|
||||
func NewRHEL87() distro.Distro {
|
||||
return newDistro("rhel", 7)
|
||||
}
|
||||
|
||||
func NewRHEL88() distro.Distro {
|
||||
return newDistro("rhel", 8)
|
||||
}
|
||||
|
||||
func NewRHEL89() distro.Distro {
|
||||
return newDistro("rhel", 9)
|
||||
}
|
||||
|
||||
func NewCentos() distro.Distro {
|
||||
return newDistro("centos", 0)
|
||||
}
|
||||
|
||||
func newDistro(name string, minor int) *distribution {
|
||||
var rd distribution
|
||||
switch name {
|
||||
case "rhel":
|
||||
rd = distribution{
|
||||
name: fmt.Sprintf("rhel-8%d", minor),
|
||||
product: "Red Hat Enterprise Linux",
|
||||
osVersion: fmt.Sprintf("8.%d", minor),
|
||||
releaseVersion: "8",
|
||||
modulePlatformID: "platform:el8",
|
||||
vendor: "redhat",
|
||||
ostreeRefTmpl: "rhel/8/%s/edge",
|
||||
isolabelTmpl: fmt.Sprintf("RHEL-8-%d-0-BaseOS-%%s", minor),
|
||||
runner: &runner.RHEL{Major: uint64(8), Minor: uint64(minor)},
|
||||
defaultImageConfig: defaultDistroImageConfig,
|
||||
}
|
||||
case "centos":
|
||||
rd = distribution{
|
||||
name: "centos-8",
|
||||
product: "CentOS Stream",
|
||||
osVersion: "8-stream",
|
||||
releaseVersion: "8",
|
||||
modulePlatformID: "platform:el8",
|
||||
vendor: "centos",
|
||||
ostreeRefTmpl: "centos/8/%s/edge",
|
||||
isolabelTmpl: "CentOS-Stream-8-%s-dvd",
|
||||
runner: &runner.CentOS{Version: uint64(8)},
|
||||
defaultImageConfig: defaultDistroImageConfig,
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown distro name: %s", name))
|
||||
}
|
||||
|
||||
// Architecture definitions
|
||||
x86_64 := architecture{
|
||||
name: platform.ARCH_X86_64.String(),
|
||||
distro: &rd,
|
||||
}
|
||||
|
||||
aarch64 := architecture{
|
||||
name: platform.ARCH_AARCH64.String(),
|
||||
distro: &rd,
|
||||
}
|
||||
|
||||
ppc64le := architecture{
|
||||
distro: &rd,
|
||||
name: platform.ARCH_PPC64LE.String(),
|
||||
}
|
||||
s390x := architecture{
|
||||
distro: &rd,
|
||||
name: platform.ARCH_S390X.String(),
|
||||
}
|
||||
|
||||
ociImgType := qcow2ImgType(rd)
|
||||
ociImgType.name = "oci"
|
||||
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_QCOW2,
|
||||
QCOW2Compat: "0.10",
|
||||
},
|
||||
},
|
||||
qcow2ImgType(rd),
|
||||
ociImgType,
|
||||
)
|
||||
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_QCOW2,
|
||||
},
|
||||
},
|
||||
openstackImgType(),
|
||||
)
|
||||
|
||||
ec2X86Platform := &platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_RAW,
|
||||
},
|
||||
}
|
||||
x86_64.addImageTypes(
|
||||
ec2X86Platform,
|
||||
amiImgTypeX86_64(rd),
|
||||
)
|
||||
|
||||
bareMetalX86Platform := &platform.X86{
|
||||
BasePlatform: platform.BasePlatform{
|
||||
FirmwarePackages: []string{
|
||||
"microcode_ctl", // ??
|
||||
"iwl1000-firmware",
|
||||
"iwl100-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6050-firmware",
|
||||
},
|
||||
},
|
||||
BIOS: true,
|
||||
UEFIVendor: rd.vendor,
|
||||
}
|
||||
|
||||
x86_64.addImageTypes(
|
||||
bareMetalX86Platform,
|
||||
edgeOCIImgType(rd),
|
||||
edgeCommitImgType(rd),
|
||||
edgeInstallerImgType(rd),
|
||||
imageInstaller(),
|
||||
)
|
||||
|
||||
gceX86Platform := &platform.X86{
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_GCE,
|
||||
},
|
||||
}
|
||||
|
||||
x86_64.addImageTypes(
|
||||
gceX86Platform,
|
||||
gceImgType(rd),
|
||||
)
|
||||
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_VMDK,
|
||||
},
|
||||
},
|
||||
vmdkImgType(),
|
||||
)
|
||||
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_OVA,
|
||||
},
|
||||
},
|
||||
ovaImgType(),
|
||||
)
|
||||
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{},
|
||||
tarImgType(),
|
||||
)
|
||||
|
||||
aarch64.addImageTypes(
|
||||
&platform.Aarch64{
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_QCOW2,
|
||||
QCOW2Compat: "0.10",
|
||||
},
|
||||
},
|
||||
qcow2ImgType(rd),
|
||||
)
|
||||
|
||||
aarch64.addImageTypes(
|
||||
&platform.Aarch64{
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_QCOW2,
|
||||
},
|
||||
},
|
||||
openstackImgType(),
|
||||
)
|
||||
|
||||
aarch64.addImageTypes(
|
||||
&platform.Aarch64{},
|
||||
tarImgType(),
|
||||
)
|
||||
|
||||
bareMetalAarch64Platform := &platform.Aarch64{
|
||||
BasePlatform: platform.BasePlatform{},
|
||||
UEFIVendor: rd.vendor,
|
||||
}
|
||||
|
||||
aarch64.addImageTypes(
|
||||
bareMetalAarch64Platform,
|
||||
edgeOCIImgType(rd),
|
||||
edgeCommitImgType(rd),
|
||||
edgeInstallerImgType(rd),
|
||||
imageInstaller(),
|
||||
)
|
||||
|
||||
rawAarch64Platform := &platform.Aarch64{
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_RAW,
|
||||
},
|
||||
}
|
||||
|
||||
aarch64.addImageTypes(
|
||||
rawAarch64Platform,
|
||||
amiImgTypeAarch64(rd),
|
||||
)
|
||||
|
||||
ppc64le.addImageTypes(
|
||||
&platform.PPC64LE{
|
||||
BIOS: true,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_QCOW2,
|
||||
QCOW2Compat: "0.10",
|
||||
},
|
||||
},
|
||||
qcow2ImgType(rd),
|
||||
)
|
||||
|
||||
ppc64le.addImageTypes(
|
||||
&platform.PPC64LE{},
|
||||
tarImgType(),
|
||||
)
|
||||
|
||||
s390x.addImageTypes(
|
||||
&platform.S390X{
|
||||
Zipl: true,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_QCOW2,
|
||||
QCOW2Compat: "0.10",
|
||||
},
|
||||
},
|
||||
qcow2ImgType(rd),
|
||||
)
|
||||
|
||||
s390x.addImageTypes(
|
||||
&platform.S390X{},
|
||||
tarImgType(),
|
||||
)
|
||||
|
||||
azureX64Platform := &platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_VHD,
|
||||
},
|
||||
}
|
||||
|
||||
azureAarch64Platform := &platform.Aarch64{
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_VHD,
|
||||
},
|
||||
}
|
||||
|
||||
rawUEFIx86Platform := &platform.X86{
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_RAW,
|
||||
},
|
||||
BIOS: false,
|
||||
UEFIVendor: rd.vendor,
|
||||
}
|
||||
|
||||
if rd.isRHEL() {
|
||||
if !common.VersionLessThan(rd.osVersion, "8.6") {
|
||||
// image types only available on 8.6 and later on RHEL
|
||||
// These edge image types require FDO which aren't available on older versions
|
||||
x86_64.addImageTypes(
|
||||
bareMetalX86Platform,
|
||||
edgeRawImgType(),
|
||||
)
|
||||
|
||||
x86_64.addImageTypes(
|
||||
rawUEFIx86Platform,
|
||||
edgeSimplifiedInstallerImgType(rd),
|
||||
)
|
||||
|
||||
azureEap := azureEap7RhuiImgType()
|
||||
x86_64.addImageTypes(azureX64Platform, azureEap)
|
||||
|
||||
aarch64.addImageTypes(
|
||||
rawAarch64Platform,
|
||||
edgeRawImgType(),
|
||||
edgeSimplifiedInstallerImgType(rd),
|
||||
)
|
||||
|
||||
// The Azure image types require hyperv-daemons which isn't available on older versions
|
||||
aarch64.addImageTypes(azureAarch64Platform, azureRhuiImgType(), azureByosImgType())
|
||||
}
|
||||
|
||||
// add azure to RHEL distro only
|
||||
x86_64.addImageTypes(azureX64Platform, azureRhuiImgType(), azureByosImgType(), azureSapRhuiImgType(rd))
|
||||
|
||||
// keep the RHEL EC2 x86_64 images before 8.9 BIOS-only for backward compatibility
|
||||
if common.VersionLessThan(rd.osVersion, "8.9") {
|
||||
ec2X86Platform = &platform.X86{
|
||||
BIOS: true,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_RAW,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// add ec2 image types to RHEL distro only
|
||||
x86_64.addImageTypes(ec2X86Platform, ec2ImgTypeX86_64(rd), ec2HaImgTypeX86_64(rd))
|
||||
aarch64.addImageTypes(rawAarch64Platform, ec2ImgTypeAarch64(rd))
|
||||
|
||||
if rd.osVersion != "8.5" {
|
||||
// NOTE: RHEL 8.5 is going away and these image types require some
|
||||
// work to get working, so we just disable them here until the
|
||||
// whole distro gets deleted
|
||||
x86_64.addImageTypes(ec2X86Platform, ec2SapImgTypeX86_64(rd))
|
||||
}
|
||||
|
||||
// add GCE RHUI image to RHEL only
|
||||
x86_64.addImageTypes(gceX86Platform, gceRhuiImgType(rd))
|
||||
|
||||
// add s390x to RHEL distro only
|
||||
rd.addArches(s390x)
|
||||
} else {
|
||||
x86_64.addImageTypes(
|
||||
bareMetalX86Platform,
|
||||
edgeRawImgType(),
|
||||
)
|
||||
|
||||
x86_64.addImageTypes(
|
||||
rawUEFIx86Platform,
|
||||
edgeSimplifiedInstallerImgType(rd),
|
||||
)
|
||||
|
||||
x86_64.addImageTypes(azureX64Platform, azureImgType())
|
||||
|
||||
aarch64.addImageTypes(
|
||||
rawAarch64Platform,
|
||||
edgeRawImgType(),
|
||||
edgeSimplifiedInstallerImgType(rd),
|
||||
)
|
||||
|
||||
aarch64.addImageTypes(azureAarch64Platform, azureImgType())
|
||||
}
|
||||
rd.addArches(x86_64, aarch64, ppc64le)
|
||||
return &rd
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
package rhel8
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var testBasicImageType = imageType{
|
||||
name: "test",
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
|
||||
var testEc2ImageType = imageType{
|
||||
name: "test_ec2",
|
||||
basePartitionTables: ec2BasePartitionTables,
|
||||
}
|
||||
|
||||
var mountpoints = []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/usr",
|
||||
},
|
||||
}
|
||||
|
||||
// math/rand is good enough in this case
|
||||
/* #nosec G404 */
|
||||
var rng = rand.New(rand.NewSource(0))
|
||||
|
||||
func TestDistro_UnsupportedArch(t *testing.T) {
|
||||
testBasicImageType.arch = &architecture{
|
||||
name: "unsupported_arch",
|
||||
}
|
||||
_, err := testBasicImageType.getPartitionTable(mountpoints, distro.ImageOptions{}, rng)
|
||||
require.EqualError(t, err, fmt.Sprintf("no partition table defined for architecture %q for image type %q", testBasicImageType.arch.name, testBasicImageType.name))
|
||||
}
|
||||
|
||||
func TestDistro_DefaultPartitionTables(t *testing.T) {
|
||||
rhel8distro := New()
|
||||
for _, archName := range rhel8distro.ListArches() {
|
||||
testBasicImageType.arch = &architecture{
|
||||
name: archName,
|
||||
}
|
||||
pt, err := testBasicImageType.getPartitionTable(mountpoints, distro.ImageOptions{}, rng)
|
||||
require.Nil(t, err)
|
||||
for _, m := range mountpoints {
|
||||
assert.True(t, pt.ContainsMountpoint(m.Mountpoint))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_Ec2PartitionTables(t *testing.T) {
|
||||
rhel8distro := New()
|
||||
for _, archName := range rhel8distro.ListArches() {
|
||||
testEc2ImageType.arch = &architecture{
|
||||
name: archName,
|
||||
}
|
||||
pt, err := testEc2ImageType.getPartitionTable(mountpoints, distro.ImageOptions{}, rng)
|
||||
if _, exists := testEc2ImageType.basePartitionTables[archName]; exists {
|
||||
require.Nil(t, err)
|
||||
for _, m := range mountpoints {
|
||||
assert.True(t, pt.ContainsMountpoint(m.Mountpoint))
|
||||
}
|
||||
} else {
|
||||
require.EqualError(t, err, fmt.Sprintf("no partition table defined for architecture %q for image type %q", testEc2ImageType.arch.name, testEc2ImageType.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,907 +0,0 @@
|
|||
package rhel8_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro/distro_test_common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro/rhel8"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
)
|
||||
|
||||
type rhelFamilyDistro struct {
|
||||
name string
|
||||
distro distro.Distro
|
||||
}
|
||||
|
||||
var rhelFamilyDistros = []rhelFamilyDistro{
|
||||
{
|
||||
name: "rhel",
|
||||
distro: rhel8.New(),
|
||||
},
|
||||
}
|
||||
|
||||
func TestFilenameFromType(t *testing.T) {
|
||||
type args struct {
|
||||
outputFormat string
|
||||
}
|
||||
type wantResult struct {
|
||||
filename string
|
||||
mimeType string
|
||||
wantErr bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want wantResult
|
||||
}{
|
||||
{
|
||||
name: "ami",
|
||||
args: args{"ami"},
|
||||
want: wantResult{
|
||||
filename: "image.raw",
|
||||
mimeType: "application/octet-stream",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ec2",
|
||||
args: args{"ec2"},
|
||||
want: wantResult{
|
||||
filename: "image.raw.xz",
|
||||
mimeType: "application/xz",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ec2-ha",
|
||||
args: args{"ec2-ha"},
|
||||
want: wantResult{
|
||||
filename: "image.raw.xz",
|
||||
mimeType: "application/xz",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ec2-sap",
|
||||
args: args{"ec2-sap"},
|
||||
want: wantResult{
|
||||
filename: "image.raw.xz",
|
||||
mimeType: "application/xz",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "qcow2",
|
||||
args: args{"qcow2"},
|
||||
want: wantResult{
|
||||
filename: "disk.qcow2",
|
||||
mimeType: "application/x-qemu-disk",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "openstack",
|
||||
args: args{"openstack"},
|
||||
want: wantResult{
|
||||
filename: "disk.qcow2",
|
||||
mimeType: "application/x-qemu-disk",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "vhd",
|
||||
args: args{"vhd"},
|
||||
want: wantResult{
|
||||
filename: "disk.vhd",
|
||||
mimeType: "application/x-vhd",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "azure-rhui",
|
||||
args: args{"azure-rhui"},
|
||||
want: wantResult{
|
||||
filename: "disk.vhd.xz",
|
||||
mimeType: "application/xz",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "azure-sap-rhui",
|
||||
args: args{"azure-sap-rhui"},
|
||||
want: wantResult{
|
||||
filename: "disk.vhd.xz",
|
||||
mimeType: "application/xz",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "vmdk",
|
||||
args: args{"vmdk"},
|
||||
want: wantResult{
|
||||
filename: "disk.vmdk",
|
||||
mimeType: "application/x-vmdk",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ova",
|
||||
args: args{"ova"},
|
||||
want: wantResult{
|
||||
filename: "image.ova",
|
||||
mimeType: "application/ovf",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "tar",
|
||||
args: args{"tar"},
|
||||
want: wantResult{
|
||||
filename: "root.tar.xz",
|
||||
mimeType: "application/x-tar",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "image-installer",
|
||||
args: args{"image-installer"},
|
||||
want: wantResult{
|
||||
filename: "installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "edge-commit",
|
||||
args: args{"edge-commit"},
|
||||
want: wantResult{
|
||||
filename: "commit.tar",
|
||||
mimeType: "application/x-tar",
|
||||
},
|
||||
},
|
||||
// Alias
|
||||
{
|
||||
name: "rhel-edge-commit",
|
||||
args: args{"rhel-edge-commit"},
|
||||
want: wantResult{
|
||||
filename: "commit.tar",
|
||||
mimeType: "application/x-tar",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "edge-container",
|
||||
args: args{"edge-container"},
|
||||
want: wantResult{
|
||||
filename: "container.tar",
|
||||
mimeType: "application/x-tar",
|
||||
},
|
||||
},
|
||||
// Alias
|
||||
{
|
||||
name: "rhel-edge-container",
|
||||
args: args{"rhel-edge-container"},
|
||||
want: wantResult{
|
||||
filename: "container.tar",
|
||||
mimeType: "application/x-tar",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "edge-installer",
|
||||
args: args{"edge-installer"},
|
||||
want: wantResult{
|
||||
filename: "installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
},
|
||||
},
|
||||
// Alias
|
||||
{
|
||||
name: "rhel-edge-installer",
|
||||
args: args{"rhel-edge-installer"},
|
||||
want: wantResult{
|
||||
filename: "installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "gce",
|
||||
args: args{"gce"},
|
||||
want: wantResult{
|
||||
filename: "image.tar.gz",
|
||||
mimeType: "application/gzip",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "gce-rhui",
|
||||
args: args{"gce-rhui"},
|
||||
want: wantResult{
|
||||
filename: "image.tar.gz",
|
||||
mimeType: "application/gzip",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid-output-type",
|
||||
args: args{"foobar"},
|
||||
want: wantResult{wantErr: true},
|
||||
},
|
||||
}
|
||||
for _, dist := range rhelFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dist := dist.distro
|
||||
arch, _ := dist.GetArch("x86_64")
|
||||
imgType, err := arch.GetImageType(tt.args.outputFormat)
|
||||
if (err != nil) != tt.want.wantErr {
|
||||
t.Errorf("Arch.GetImageType() error = %v, wantErr %v", err, tt.want.wantErr)
|
||||
return
|
||||
}
|
||||
if !tt.want.wantErr {
|
||||
gotFilename := imgType.Filename()
|
||||
gotMIMEType := imgType.MIMEType()
|
||||
if gotFilename != tt.want.filename {
|
||||
t.Errorf("ImageType.Filename() got = %v, want %v", gotFilename, tt.want.filename)
|
||||
}
|
||||
if gotMIMEType != tt.want.mimeType {
|
||||
t.Errorf("ImageType.MIMEType() got1 = %v, want %v", gotMIMEType, tt.want.mimeType)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageType_BuildPackages(t *testing.T) {
|
||||
x8664BuildPackages := []string{
|
||||
"dnf",
|
||||
"dosfstools",
|
||||
"e2fsprogs",
|
||||
"grub2-efi-x64",
|
||||
"grub2-pc",
|
||||
"policycoreutils",
|
||||
"shim-x64",
|
||||
"systemd",
|
||||
"tar",
|
||||
"qemu-img",
|
||||
"xz",
|
||||
}
|
||||
aarch64BuildPackages := []string{
|
||||
"dnf",
|
||||
"dosfstools",
|
||||
"e2fsprogs",
|
||||
"policycoreutils",
|
||||
"qemu-img",
|
||||
"systemd",
|
||||
"tar",
|
||||
"xz",
|
||||
}
|
||||
buildPackages := map[string][]string{
|
||||
"x86_64": x8664BuildPackages,
|
||||
"aarch64": aarch64BuildPackages,
|
||||
}
|
||||
for _, dist := range rhelFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
d := dist.distro
|
||||
for _, archLabel := range d.ListArches() {
|
||||
archStruct, err := d.GetArch(archLabel)
|
||||
if assert.NoErrorf(t, err, "d.GetArch(%v) returned err = %v; expected nil", archLabel, err) {
|
||||
continue
|
||||
}
|
||||
for _, itLabel := range archStruct.ListImageTypes() {
|
||||
itStruct, err := archStruct.GetImageType(itLabel)
|
||||
if assert.NoErrorf(t, err, "d.GetArch(%v) returned err = %v; expected nil", archLabel, err) {
|
||||
continue
|
||||
}
|
||||
manifest, _, err := itStruct.Manifest(&blueprint.Blueprint{}, distro.ImageOptions{}, nil, 0)
|
||||
assert.NoError(t, err)
|
||||
buildPkgs := manifest.GetPackageSetChains()["build"]
|
||||
assert.NotNil(t, buildPkgs)
|
||||
assert.Len(t, buildPkgs, 1)
|
||||
assert.ElementsMatch(t, buildPackages[archLabel], buildPkgs[0].Include)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageType_Name(t *testing.T) {
|
||||
imgMap := []struct {
|
||||
arch string
|
||||
imgNames []string
|
||||
}{
|
||||
{
|
||||
arch: "x86_64",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"openstack",
|
||||
"vhd",
|
||||
"azure-rhui",
|
||||
"azure-sap-rhui",
|
||||
"azure-eap7-rhui",
|
||||
"vmdk",
|
||||
"ova",
|
||||
"ami",
|
||||
"ec2",
|
||||
"ec2-ha",
|
||||
"ec2-sap",
|
||||
"gce",
|
||||
"gce-rhui",
|
||||
"edge-commit",
|
||||
"edge-container",
|
||||
"edge-installer",
|
||||
"tar",
|
||||
"image-installer",
|
||||
},
|
||||
},
|
||||
{
|
||||
arch: "aarch64",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"openstack",
|
||||
"vhd",
|
||||
"azure-rhui",
|
||||
"ami",
|
||||
"ec2",
|
||||
"edge-commit",
|
||||
"edge-container",
|
||||
"tar",
|
||||
},
|
||||
},
|
||||
{
|
||||
arch: "ppc64le",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"tar",
|
||||
},
|
||||
},
|
||||
{
|
||||
arch: "s390x",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"tar",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, dist := range rhelFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, mapping := range imgMap {
|
||||
if mapping.arch == platform.ARCH_S390X.String() && dist.name == "centos" {
|
||||
continue
|
||||
}
|
||||
arch, err := dist.distro.GetArch(mapping.arch)
|
||||
if assert.NoError(t, err) {
|
||||
for _, imgName := range mapping.imgNames {
|
||||
if imgName == "edge-commit" && dist.name == "centos" {
|
||||
continue
|
||||
}
|
||||
imgType, err := arch.GetImageType(imgName)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equalf(t, imgName, imgType.Name(), "arch: %s", mapping.arch)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageTypeAliases(t *testing.T) {
|
||||
type args struct {
|
||||
imageTypeAliases []string
|
||||
}
|
||||
type wantResult struct {
|
||||
imageTypeName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want wantResult
|
||||
}{
|
||||
{
|
||||
name: "edge-commit aliases",
|
||||
args: args{
|
||||
imageTypeAliases: []string{"rhel-edge-commit"},
|
||||
},
|
||||
want: wantResult{
|
||||
imageTypeName: "edge-commit",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "edge-container aliases",
|
||||
args: args{
|
||||
imageTypeAliases: []string{"rhel-edge-container"},
|
||||
},
|
||||
want: wantResult{
|
||||
imageTypeName: "edge-container",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "edge-installer aliases",
|
||||
args: args{
|
||||
imageTypeAliases: []string{"rhel-edge-installer"},
|
||||
},
|
||||
want: wantResult{
|
||||
imageTypeName: "edge-installer",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, dist := range rhelFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dist := dist.distro
|
||||
for _, archName := range dist.ListArches() {
|
||||
t.Run(archName, func(t *testing.T) {
|
||||
arch, err := dist.GetArch(archName)
|
||||
require.Nilf(t, err,
|
||||
"failed to get architecture '%s', previously listed as supported for the distro '%s'",
|
||||
archName, dist.Name())
|
||||
// Test image type aliases only if the aliased image type is supported for the arch
|
||||
if _, err = arch.GetImageType(tt.want.imageTypeName); err != nil {
|
||||
t.Skipf("aliased image type '%s' is not supported for architecture '%s'",
|
||||
tt.want.imageTypeName, archName)
|
||||
}
|
||||
for _, alias := range tt.args.imageTypeAliases {
|
||||
t.Run(fmt.Sprintf("'%s' alias for image type '%s'", alias, tt.want.imageTypeName),
|
||||
func(t *testing.T) {
|
||||
gotImage, err := arch.GetImageType(alias)
|
||||
require.Nilf(t, err, "arch.GetImageType() for image type alias '%s' failed: %v",
|
||||
alias, err)
|
||||
assert.Equalf(t, tt.want.imageTypeName, gotImage.Name(),
|
||||
"got unexpected image type name for alias '%s'. got = %s, want = %s",
|
||||
alias, tt.want.imageTypeName, gotImage.Name())
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Check that Manifest() function returns an error for unsupported
|
||||
// configurations.
|
||||
func TestDistro_ManifestError(t *testing.T) {
|
||||
// Currently, the only unsupported configuration is OSTree commit types
|
||||
// with Kernel boot options
|
||||
r8distro := rhel8.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Kernel: &blueprint.KernelCustomization{
|
||||
Append: "debug",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, archName := range r8distro.ListArches() {
|
||||
arch, _ := r8distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
imgOpts := distro.ImageOptions{
|
||||
Size: imgType.Size(0),
|
||||
}
|
||||
_, _, err := imgType.Manifest(&bp, imgOpts, nil, 0)
|
||||
if imgTypeName == "edge-commit" || imgTypeName == "edge-container" {
|
||||
assert.EqualError(t, err, "kernel boot parameter customizations are not supported for ostree types")
|
||||
} else if imgTypeName == "edge-raw-image" {
|
||||
assert.EqualError(t, err, "edge raw images require specifying a URL from which to retrieve the OSTree commit")
|
||||
} else if imgTypeName == "edge-installer" || imgTypeName == "edge-simplified-installer" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("boot ISO image type \"%s\" requires specifying a URL from which to retrieve the OSTree commit", imgTypeName))
|
||||
} else if imgTypeName == "azure-eap7-rhui" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("image type \"%s\" does not support customizations", imgTypeName))
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestArchitecture_ListImageTypes(t *testing.T) {
|
||||
imgMap := []struct {
|
||||
arch string
|
||||
imgNames []string
|
||||
rhelAdditionalImageTypes []string
|
||||
}{
|
||||
{
|
||||
arch: "x86_64",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"openstack",
|
||||
"vhd",
|
||||
"azure-rhui",
|
||||
"azure-sap-rhui",
|
||||
"azure-eap7-rhui",
|
||||
"vmdk",
|
||||
"ova",
|
||||
"ami",
|
||||
"ec2",
|
||||
"ec2-ha",
|
||||
"ec2-sap",
|
||||
"gce",
|
||||
"gce-rhui",
|
||||
"edge-commit",
|
||||
"edge-container",
|
||||
"edge-installer",
|
||||
"edge-raw-image",
|
||||
"edge-simplified-installer",
|
||||
"tar",
|
||||
"image-installer",
|
||||
"oci",
|
||||
},
|
||||
},
|
||||
{
|
||||
arch: "aarch64",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"openstack",
|
||||
"vhd",
|
||||
"azure-rhui",
|
||||
"ami",
|
||||
"ec2",
|
||||
"edge-commit",
|
||||
"edge-container",
|
||||
"edge-installer",
|
||||
"edge-simplified-installer",
|
||||
"edge-raw-image",
|
||||
"tar",
|
||||
"image-installer",
|
||||
},
|
||||
},
|
||||
{
|
||||
arch: "ppc64le",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"tar",
|
||||
},
|
||||
},
|
||||
{
|
||||
arch: "s390x",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"tar",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, dist := range rhelFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, mapping := range imgMap {
|
||||
arch, err := dist.distro.GetArch(mapping.arch)
|
||||
require.NoError(t, err)
|
||||
imageTypes := arch.ListImageTypes()
|
||||
|
||||
var expectedImageTypes []string
|
||||
expectedImageTypes = append(expectedImageTypes, mapping.imgNames...)
|
||||
if dist.name == "rhel" {
|
||||
expectedImageTypes = append(expectedImageTypes, mapping.rhelAdditionalImageTypes...)
|
||||
}
|
||||
|
||||
require.ElementsMatch(t, expectedImageTypes, imageTypes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRHEL8_ListArches(t *testing.T) {
|
||||
arches := rhel8.New().ListArches()
|
||||
assert.Equal(t, []string{"aarch64", "ppc64le", "s390x", "x86_64"}, arches)
|
||||
}
|
||||
|
||||
func TestRHEL8_GetArch(t *testing.T) {
|
||||
arches := []struct {
|
||||
name string
|
||||
errorExpected bool
|
||||
errorExpectedInCentos bool
|
||||
}{
|
||||
{
|
||||
name: "x86_64",
|
||||
},
|
||||
{
|
||||
name: "aarch64",
|
||||
},
|
||||
{
|
||||
name: "ppc64le",
|
||||
},
|
||||
{
|
||||
name: "s390x",
|
||||
},
|
||||
{
|
||||
name: "foo-arch",
|
||||
errorExpected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, dist := range rhelFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, a := range arches {
|
||||
actualArch, err := dist.distro.GetArch(a.name)
|
||||
if a.errorExpected || (a.errorExpectedInCentos && dist.name == "centos") {
|
||||
assert.Nil(t, actualArch)
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.Equal(t, a.name, actualArch.Name())
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRhel8_Name(t *testing.T) {
|
||||
distro := rhel8.New()
|
||||
assert.Equal(t, "rhel-8", distro.Name())
|
||||
}
|
||||
|
||||
func TestRhel8_ModulePlatformID(t *testing.T) {
|
||||
distro := rhel8.New()
|
||||
assert.Equal(t, "platform:el8", distro.ModulePlatformID())
|
||||
}
|
||||
|
||||
func TestRhel86_KernelOption(t *testing.T) {
|
||||
distro_test_common.TestDistro_KernelOption(t, rhel8.New())
|
||||
}
|
||||
|
||||
func TestRhel8_OSTreeOptions(t *testing.T) {
|
||||
distro_test_common.TestDistro_OSTreeOptions(t, rhel8.New())
|
||||
}
|
||||
|
||||
func TestDistro_CustomFileSystemManifestError(t *testing.T) {
|
||||
r8distro := rhel8.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/etc",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
unsupported := map[string]bool{
|
||||
"edge-installer": true,
|
||||
"edge-simplified-installer": true,
|
||||
"edge-raw-image": true,
|
||||
"azure-eap7-rhui": true,
|
||||
}
|
||||
for _, archName := range r8distro.ListArches() {
|
||||
arch, _ := r8distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if imgTypeName == "edge-commit" || imgTypeName == "edge-container" {
|
||||
assert.EqualError(t, err, "Custom mountpoints are not supported for ostree types")
|
||||
} else if unsupported[imgTypeName] {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.EqualError(t, err, "The following custom mountpoints are not supported [\"/etc\"]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_TestRootMountPoint(t *testing.T) {
|
||||
r8distro := rhel8.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
unsupported := map[string]bool{
|
||||
"edge-installer": true,
|
||||
"edge-simplified-installer": true,
|
||||
"edge-raw-image": true,
|
||||
"azure-eap7-rhui": true,
|
||||
}
|
||||
for _, archName := range r8distro.ListArches() {
|
||||
arch, _ := r8distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if imgTypeName == "edge-commit" || imgTypeName == "edge-container" {
|
||||
assert.EqualError(t, err, "Custom mountpoints are not supported for ostree types")
|
||||
} else if unsupported[imgTypeName] {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_CustomFileSystemSubDirectories(t *testing.T) {
|
||||
r8distro := rhel8.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/log",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/log/audit",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
unsupported := map[string]bool{
|
||||
"edge-commit": true,
|
||||
"edge-container": true,
|
||||
"edge-installer": true,
|
||||
"edge-simplified-installer": true,
|
||||
"edge-raw-image": true,
|
||||
"azure-eap7-rhui": true,
|
||||
}
|
||||
for _, archName := range r8distro.ListArches() {
|
||||
arch, _ := r8distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if unsupported[imgTypeName] {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_MountpointsWithArbitraryDepthAllowed(t *testing.T) {
|
||||
r8distro := rhel8.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/a",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/a/b",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/a/b/c",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/a/b/c/d",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
unsupported := map[string]bool{
|
||||
"edge-commit": true,
|
||||
"edge-container": true,
|
||||
"edge-installer": true,
|
||||
"edge-simplified-installer": true,
|
||||
"edge-raw-image": true,
|
||||
"azure-eap7-rhui": true,
|
||||
}
|
||||
for _, archName := range r8distro.ListArches() {
|
||||
arch, _ := r8distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if unsupported[imgTypeName] {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_DirtyMountpointsNotAllowed(t *testing.T) {
|
||||
r8distro := rhel8.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "//",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var//",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var//log/audit/",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
unsupported := map[string]bool{
|
||||
"edge-commit": true,
|
||||
"edge-container": true,
|
||||
"edge-installer": true,
|
||||
"edge-simplified-installer": true,
|
||||
"edge-raw-image": true,
|
||||
"azure-eap7-rhui": true,
|
||||
}
|
||||
for _, archName := range r8distro.ListArches() {
|
||||
arch, _ := r8distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if unsupported[imgTypeName] {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.EqualError(t, err, "The following custom mountpoints are not supported [\"//\" \"/var//\" \"/var//log/audit/\"]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_CustomFileSystemPatternMatching(t *testing.T) {
|
||||
r8distro := rhel8.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/variable",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/variable/log/audit",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
unsupported := map[string]bool{
|
||||
"edge-installer": true,
|
||||
"edge-simplified-installer": true,
|
||||
"edge-raw-image": true,
|
||||
"azure-eap7-rhui": true,
|
||||
}
|
||||
for _, archName := range r8distro.ListArches() {
|
||||
arch, _ := r8distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if imgTypeName == "edge-commit" || imgTypeName == "edge-container" {
|
||||
assert.EqualError(t, err, "Custom mountpoints are not supported for ostree types")
|
||||
} else if unsupported[imgTypeName] {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.EqualError(t, err, "The following custom mountpoints are not supported [\"/variable\" \"/variable/log/audit\"]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_CustomUsrPartitionNotLargeEnough(t *testing.T) {
|
||||
r8distro := rhel8.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/usr",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
unsupported := map[string]bool{
|
||||
"edge-installer": true,
|
||||
"edge-simplified-installer": true,
|
||||
"edge-raw-image": true,
|
||||
"azure-eap7-rhui": true,
|
||||
}
|
||||
for _, archName := range r8distro.ListArches() {
|
||||
arch, _ := r8distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if imgTypeName == "edge-commit" || imgTypeName == "edge-container" {
|
||||
assert.EqualError(t, err, "Custom mountpoints are not supported for ostree types")
|
||||
} else if unsupported[imgTypeName] {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,381 +0,0 @@
|
|||
package rhel8
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
func edgeCommitImgType(rd distribution) imageType {
|
||||
it := imageType{
|
||||
name: "edge-commit",
|
||||
nameAliases: []string{"rhel-edge-commit"},
|
||||
filename: "commit.tar",
|
||||
mimeType: "application/x-tar",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: edgeCommitPackageSet,
|
||||
},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
EnabledServices: edgeServices(rd),
|
||||
},
|
||||
rpmOstree: true,
|
||||
image: edgeCommitImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "ostree-commit", "commit-archive"},
|
||||
exports: []string{"commit-archive"},
|
||||
}
|
||||
return it
|
||||
}
|
||||
|
||||
func edgeOCIImgType(rd distribution) imageType {
|
||||
it := imageType{
|
||||
name: "edge-container",
|
||||
nameAliases: []string{"rhel-edge-container"},
|
||||
filename: "container.tar",
|
||||
mimeType: "application/x-tar",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: edgeCommitPackageSet,
|
||||
containerPkgsKey: func(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{"nginx"},
|
||||
}
|
||||
},
|
||||
},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
EnabledServices: edgeServices(rd),
|
||||
},
|
||||
rpmOstree: true,
|
||||
bootISO: false,
|
||||
image: edgeContainerImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "ostree-commit", "container-tree", "container"},
|
||||
exports: []string{"container"},
|
||||
}
|
||||
return it
|
||||
}
|
||||
|
||||
func edgeRawImgType() imageType {
|
||||
it := imageType{
|
||||
name: "edge-raw-image",
|
||||
nameAliases: []string{"rhel-edge-raw-image"},
|
||||
filename: "image.raw.xz",
|
||||
compression: "xz",
|
||||
mimeType: "application/xz",
|
||||
packageSets: nil,
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
rpmOstree: true,
|
||||
bootable: true,
|
||||
bootISO: false,
|
||||
image: edgeRawImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"ostree-deployment", "image", "xz"},
|
||||
exports: []string{"xz"},
|
||||
basePartitionTables: edgeBasePartitionTables,
|
||||
}
|
||||
return it
|
||||
}
|
||||
|
||||
func edgeInstallerImgType(rd distribution) imageType {
|
||||
it := imageType{
|
||||
name: "edge-installer",
|
||||
nameAliases: []string{"rhel-edge-installer"},
|
||||
filename: "installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
// TODO: non-arch-specific package set handling for installers
|
||||
// This image type requires build packages for installers and
|
||||
// ostree/edge. For now we only have x86-64 installer build
|
||||
// package sets defined. When we add installer build package sets
|
||||
// for other architectures, this will need to be moved to the
|
||||
// architecture and the merging will happen in the PackageSets()
|
||||
// method like the other sets.
|
||||
installerPkgsKey: edgeInstallerPackageSet,
|
||||
},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
EnabledServices: edgeServices(rd),
|
||||
},
|
||||
rpmOstree: true,
|
||||
bootISO: true,
|
||||
image: edgeInstallerImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"anaconda-tree", "rootfs-image", "efiboot-tree", "bootiso-tree", "bootiso"},
|
||||
exports: []string{"bootiso"},
|
||||
}
|
||||
return it
|
||||
}
|
||||
|
||||
func edgeSimplifiedInstallerImgType(rd distribution) imageType {
|
||||
it := imageType{
|
||||
name: "edge-simplified-installer",
|
||||
nameAliases: []string{"rhel-edge-simplified-installer"},
|
||||
filename: "simplified-installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
// TODO: non-arch-specific package set handling for installers
|
||||
// This image type requires build packages for installers and
|
||||
// ostree/edge. For now we only have x86-64 installer build
|
||||
// package sets defined. When we add installer build package sets
|
||||
// for other architectures, this will need to be moved to the
|
||||
// architecture and the merging will happen in the PackageSets()
|
||||
// method like the other sets.
|
||||
installerPkgsKey: edgeSimplifiedInstallerPackageSet,
|
||||
},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
EnabledServices: edgeServices(rd),
|
||||
},
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
rpmOstree: true,
|
||||
bootable: true,
|
||||
bootISO: true,
|
||||
image: edgeSimplifiedInstallerImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"ostree-deployment", "image", "xz", "coi-tree", "efiboot-tree", "bootiso-tree", "bootiso"},
|
||||
exports: []string{"bootiso"},
|
||||
basePartitionTables: edgeBasePartitionTables,
|
||||
}
|
||||
return it
|
||||
}
|
||||
|
||||
// edge commit OS package set
|
||||
func edgeCommitPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"attr",
|
||||
"audit",
|
||||
"basesystem",
|
||||
"bash",
|
||||
"bash-completion",
|
||||
"chrony",
|
||||
"clevis",
|
||||
"clevis-dracut",
|
||||
"clevis-luks",
|
||||
"container-selinux",
|
||||
"coreutils",
|
||||
"criu",
|
||||
"cryptsetup",
|
||||
"curl",
|
||||
"dnsmasq",
|
||||
"dosfstools",
|
||||
"dracut-config-generic",
|
||||
"dracut-network",
|
||||
"e2fsprogs",
|
||||
"firewalld",
|
||||
"fuse-overlayfs",
|
||||
"fwupd",
|
||||
"glibc",
|
||||
"glibc-minimal-langpack",
|
||||
"gnupg2",
|
||||
"greenboot",
|
||||
"gzip",
|
||||
"hostname",
|
||||
"ima-evm-utils",
|
||||
"iproute",
|
||||
"iptables",
|
||||
"iputils",
|
||||
"keyutils",
|
||||
"less",
|
||||
"lvm2",
|
||||
"NetworkManager",
|
||||
"NetworkManager-wifi",
|
||||
"NetworkManager-wwan",
|
||||
"nss-altfiles",
|
||||
"openssh-clients",
|
||||
"openssh-server",
|
||||
"passwd",
|
||||
"pinentry",
|
||||
"platform-python",
|
||||
"podman",
|
||||
"policycoreutils",
|
||||
"policycoreutils-python-utils",
|
||||
"polkit",
|
||||
"procps-ng",
|
||||
"redhat-release",
|
||||
"rootfiles",
|
||||
"rpm",
|
||||
"rpm-ostree",
|
||||
"rsync",
|
||||
"selinux-policy-targeted",
|
||||
"setools-console",
|
||||
"setup",
|
||||
"shadow-utils",
|
||||
"shadow-utils",
|
||||
"skopeo",
|
||||
"slirp4netns",
|
||||
"sudo",
|
||||
"systemd",
|
||||
"tar",
|
||||
"tmux",
|
||||
"traceroute",
|
||||
"usbguard",
|
||||
"util-linux",
|
||||
"vim-minimal",
|
||||
"wpa_supplicant",
|
||||
"xz",
|
||||
},
|
||||
Exclude: []string{"rng-tools"},
|
||||
}
|
||||
|
||||
switch t.arch.Name() {
|
||||
case platform.ARCH_X86_64.String():
|
||||
ps = ps.Append(x8664EdgeCommitPackageSet(t))
|
||||
|
||||
case platform.ARCH_AARCH64.String():
|
||||
ps = ps.Append(aarch64EdgeCommitPackageSet(t))
|
||||
}
|
||||
|
||||
if t.arch.distro.isRHEL() && common.VersionLessThan(t.arch.distro.osVersion, "8.6") {
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"greenboot-grub2",
|
||||
"greenboot-reboot",
|
||||
"greenboot-rpm-ostree-grub2",
|
||||
"greenboot-status",
|
||||
},
|
||||
})
|
||||
} else {
|
||||
// 8.6+ and CS8
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"fdo-client",
|
||||
"fdo-owner-cli",
|
||||
"greenboot-default-health-checks",
|
||||
"sos",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return ps
|
||||
|
||||
}
|
||||
|
||||
func x8664EdgeCommitPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"efibootmgr",
|
||||
"grub2",
|
||||
"grub2-efi-x64",
|
||||
"iwl1000-firmware",
|
||||
"iwl100-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6000-firmware",
|
||||
"iwl6050-firmware",
|
||||
"iwl7260-firmware",
|
||||
"microcode_ctl",
|
||||
"shim-x64",
|
||||
},
|
||||
Exclude: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func aarch64EdgeCommitPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"efibootmgr",
|
||||
"grub2-efi-aa64",
|
||||
"iwl7260-firmware",
|
||||
"shim-aa64",
|
||||
},
|
||||
Exclude: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func edgeInstallerPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return anacondaPackageSet(t)
|
||||
}
|
||||
|
||||
func edgeSimplifiedInstallerPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
// common installer packages
|
||||
ps := installerPackageSet(t)
|
||||
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"attr",
|
||||
"basesystem",
|
||||
"binutils",
|
||||
"bsdtar",
|
||||
"clevis-dracut",
|
||||
"clevis-luks",
|
||||
"cloud-utils-growpart",
|
||||
"coreos-installer",
|
||||
"coreos-installer-dracut",
|
||||
"coreutils",
|
||||
"device-mapper-multipath",
|
||||
"dnsmasq",
|
||||
"dosfstools",
|
||||
"dracut-live",
|
||||
"e2fsprogs",
|
||||
"fcoe-utils",
|
||||
"fdo-init",
|
||||
"gzip",
|
||||
"ima-evm-utils",
|
||||
"iproute",
|
||||
"iptables",
|
||||
"iputils",
|
||||
"iscsi-initiator-utils",
|
||||
"keyutils",
|
||||
"lldpad",
|
||||
"lvm2",
|
||||
"passwd",
|
||||
"policycoreutils",
|
||||
"policycoreutils-python-utils",
|
||||
"procps-ng",
|
||||
"redhat-logos",
|
||||
"rootfiles",
|
||||
"setools-console",
|
||||
"sudo",
|
||||
"traceroute",
|
||||
"util-linux",
|
||||
},
|
||||
Exclude: nil,
|
||||
})
|
||||
|
||||
switch t.arch.Name() {
|
||||
|
||||
case platform.ARCH_X86_64.String():
|
||||
ps = ps.Append(x8664EdgeCommitPackageSet(t))
|
||||
case platform.ARCH_AARCH64.String():
|
||||
ps = ps.Append(aarch64EdgeCommitPackageSet(t))
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported arch: %s", t.arch.Name()))
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
func edgeServices(rd distribution) []string {
|
||||
// Common Services
|
||||
var edgeServices = []string{"NetworkManager.service", "firewalld.service", "sshd.service"}
|
||||
|
||||
if rd.osVersion == "8.4" {
|
||||
// greenboot services aren't enabled by default in 8.4
|
||||
edgeServices = append(edgeServices,
|
||||
"greenboot-grub2-set-counter",
|
||||
"greenboot-grub2-set-success",
|
||||
"greenboot-healthcheck",
|
||||
"greenboot-rpm-ostree-grub2-check-fallback",
|
||||
"greenboot-status",
|
||||
"greenboot-task-runner",
|
||||
"redboot-auto-reboot",
|
||||
"redboot-task-runner")
|
||||
|
||||
}
|
||||
|
||||
if !(rd.isRHEL() && common.VersionLessThan(rd.osVersion, "8.6")) {
|
||||
// enable fdo-client only on RHEL 8.6+ and CS8
|
||||
|
||||
// TODO(runcom): move fdo-client-linuxapp.service to presets?
|
||||
edgeServices = append(edgeServices, "fdo-client-linuxapp.service")
|
||||
}
|
||||
|
||||
return edgeServices
|
||||
}
|
||||
|
|
@ -1,299 +0,0 @@
|
|||
package rhel8
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/subscription"
|
||||
)
|
||||
|
||||
const gceKernelOptions = "net.ifnames=0 biosdevname=0 scsi_mod.use_blk_mq=Y crashkernel=auto console=ttyS0,38400n8d"
|
||||
|
||||
func gceImgType(rd distribution) imageType {
|
||||
return imageType{
|
||||
name: "gce",
|
||||
filename: "image.tar.gz",
|
||||
mimeType: "application/gzip",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: gcePackageSet,
|
||||
},
|
||||
defaultImageConfig: defaultGceByosImageConfig(rd),
|
||||
kernelOptions: gceKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 20 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "archive"},
|
||||
exports: []string{"archive"},
|
||||
// TODO: the base partition table still contains the BIOS boot partition, but the image is UEFI-only
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
}
|
||||
|
||||
func gceRhuiImgType(rd distribution) imageType {
|
||||
return imageType{
|
||||
name: "gce-rhui",
|
||||
filename: "image.tar.gz",
|
||||
mimeType: "application/gzip",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: gceRhuiPackageSet,
|
||||
},
|
||||
defaultImageConfig: defaultGceRhuiImageConfig(rd),
|
||||
kernelOptions: gceKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 20 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "archive"},
|
||||
exports: []string{"archive"},
|
||||
// TODO: the base partition table still contains the BIOS boot partition, but the image is UEFI-only
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
}
|
||||
|
||||
func defaultGceByosImageConfig(rd distribution) *distro.ImageConfig {
|
||||
ic := &distro.ImageConfig{
|
||||
Timezone: common.ToPtr("UTC"),
|
||||
TimeSynchronization: &osbuild.ChronyStageOptions{
|
||||
Servers: []osbuild.ChronyConfigServer{{Hostname: "metadata.google.internal"}},
|
||||
},
|
||||
Firewall: &osbuild.FirewallStageOptions{
|
||||
DefaultZone: "trusted",
|
||||
},
|
||||
EnabledServices: []string{
|
||||
"sshd",
|
||||
"rngd",
|
||||
"dnf-automatic.timer",
|
||||
},
|
||||
DisabledServices: []string{
|
||||
"sshd-keygen@",
|
||||
"reboot.target",
|
||||
},
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
Keyboard: &osbuild.KeymapStageOptions{
|
||||
Keymap: "us",
|
||||
},
|
||||
DNFConfig: []*osbuild.DNFConfigStageOptions{
|
||||
{
|
||||
Config: &osbuild.DNFConfig{
|
||||
Main: &osbuild.DNFConfigMain{
|
||||
IPResolve: "4",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
DNFAutomaticConfig: &osbuild.DNFAutomaticConfigStageOptions{
|
||||
Config: &osbuild.DNFAutomaticConfig{
|
||||
Commands: &osbuild.DNFAutomaticConfigCommands{
|
||||
ApplyUpdates: common.ToPtr(true),
|
||||
UpgradeType: osbuild.DNFAutomaticUpgradeTypeSecurity,
|
||||
},
|
||||
},
|
||||
},
|
||||
YUMRepos: []*osbuild.YumReposStageOptions{
|
||||
{
|
||||
Filename: "google-cloud.repo",
|
||||
Repos: []osbuild.YumRepository{
|
||||
{
|
||||
Id: "google-compute-engine",
|
||||
Name: "Google Compute Engine",
|
||||
BaseURLs: []string{"https://packages.cloud.google.com/yum/repos/google-compute-engine-el8-x86_64-stable"},
|
||||
Enabled: common.ToPtr(true),
|
||||
GPGCheck: common.ToPtr(true),
|
||||
RepoGPGCheck: common.ToPtr(false),
|
||||
GPGKey: []string{
|
||||
"https://packages.cloud.google.com/yum/doc/yum-key.gpg",
|
||||
"https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
SshdConfig: &osbuild.SshdConfigStageOptions{
|
||||
Config: osbuild.SshdConfigConfig{
|
||||
PasswordAuthentication: common.ToPtr(false),
|
||||
ClientAliveInterval: common.ToPtr(420),
|
||||
PermitRootLogin: osbuild.PermitRootLoginValueNo,
|
||||
},
|
||||
},
|
||||
Sysconfig: []*osbuild.SysconfigStageOptions{
|
||||
{
|
||||
Kernel: &osbuild.SysconfigKernelOptions{
|
||||
DefaultKernel: "kernel-core",
|
||||
UpdateDefault: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Modprobe: []*osbuild.ModprobeStageOptions{
|
||||
{
|
||||
Filename: "blacklist-floppy.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("floppy"),
|
||||
},
|
||||
},
|
||||
},
|
||||
GCPGuestAgentConfig: &osbuild.GcpGuestAgentConfigOptions{
|
||||
ConfigScope: osbuild.GcpGuestAgentConfigScopeDistro,
|
||||
Config: &osbuild.GcpGuestAgentConfig{
|
||||
InstanceSetup: &osbuild.GcpGuestAgentConfigInstanceSetup{
|
||||
SetBotoConfig: common.ToPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if rd.osVersion == "8.4" {
|
||||
// NOTE(akoutsou): these are enabled in the package preset, but for
|
||||
// some reason do not get enabled on 8.4.
|
||||
// the reason is unknown and deeply mysterious
|
||||
ic.EnabledServices = append(ic.EnabledServices,
|
||||
"google-oslogin-cache.timer",
|
||||
"google-guest-agent.service",
|
||||
"google-shutdown-scripts.service",
|
||||
"google-startup-scripts.service",
|
||||
"google-osconfig-agent.service",
|
||||
)
|
||||
}
|
||||
|
||||
if rd.isRHEL() {
|
||||
ic.RHSMConfig = map[subscription.RHSMStatus]*osbuild.RHSMStageOptions{
|
||||
subscription.RHSMConfigNoSubscription: {
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
// Don't disable RHSM redhat.repo management on the GCE
|
||||
// image, which is BYOS and does not use RHUI for content.
|
||||
// Otherwise subscribing the system manually after booting
|
||||
// it would result in empty redhat.repo. Without RHUI, such
|
||||
// system would have no way to get Red Hat content, but
|
||||
// enable the repo management manually, which would be very
|
||||
// confusing.
|
||||
},
|
||||
},
|
||||
subscription.RHSMConfigWithSubscription: {
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
// do not disable the redhat.repo management if the user
|
||||
// explicitly request the system to be subscribed
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return ic
|
||||
}
|
||||
|
||||
func defaultGceRhuiImageConfig(rd distribution) *distro.ImageConfig {
|
||||
ic := &distro.ImageConfig{
|
||||
RHSMConfig: map[subscription.RHSMStatus]*osbuild.RHSMStageOptions{
|
||||
subscription.RHSMConfigNoSubscription: {
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
Rhsm: &osbuild.SubManConfigRHSMSection{
|
||||
ManageRepos: common.ToPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
subscription.RHSMConfigWithSubscription: {
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
// do not disable the redhat.repo management if the user
|
||||
// explicitly request the system to be subscribed
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
ic = ic.InheritFrom(defaultGceByosImageConfig(rd))
|
||||
return ic
|
||||
}
|
||||
|
||||
// common GCE image
|
||||
func gceCommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"@core",
|
||||
"langpacks-en", // not in Google's KS
|
||||
"acpid",
|
||||
"dhcp-client",
|
||||
"dnf-automatic",
|
||||
"net-tools",
|
||||
//"openssh-server", included in core
|
||||
"python3",
|
||||
"rng-tools",
|
||||
"tar",
|
||||
"vim",
|
||||
|
||||
// GCE guest tools
|
||||
"google-compute-engine",
|
||||
"google-osconfig-agent",
|
||||
"gce-disk-expand",
|
||||
|
||||
// Not explicitly included in GCP kickstart, but present on the image
|
||||
// for time synchronization
|
||||
"chrony",
|
||||
"timedatex",
|
||||
// EFI
|
||||
"grub2-tools-efi",
|
||||
},
|
||||
Exclude: []string{
|
||||
"alsa-utils",
|
||||
"b43-fwcutter",
|
||||
"dmraid",
|
||||
"eject",
|
||||
"gpm",
|
||||
"irqbalance",
|
||||
"microcode_ctl",
|
||||
"smartmontools",
|
||||
"aic94xx-firmware",
|
||||
"atmel-firmware",
|
||||
"b43-openfwwf",
|
||||
"bfa-firmware",
|
||||
"ipw2100-firmware",
|
||||
"ipw2200-firmware",
|
||||
"ivtv-firmware",
|
||||
"iwl100-firmware",
|
||||
"iwl1000-firmware",
|
||||
"iwl3945-firmware",
|
||||
"iwl4965-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6000-firmware",
|
||||
"iwl6000g2a-firmware",
|
||||
"iwl6050-firmware",
|
||||
"kernel-firmware",
|
||||
"libertas-usb8388-firmware",
|
||||
"ql2100-firmware",
|
||||
"ql2200-firmware",
|
||||
"ql23xx-firmware",
|
||||
"ql2400-firmware",
|
||||
"ql2500-firmware",
|
||||
"rt61pci-firmware",
|
||||
"rt73usb-firmware",
|
||||
"xorg-x11-drv-ati-firmware",
|
||||
"zd1211-firmware",
|
||||
// RHBZ#2075815
|
||||
"qemu-guest-agent",
|
||||
},
|
||||
}.Append(distroSpecificPackageSet(t))
|
||||
}
|
||||
|
||||
// GCE BYOS image
|
||||
func gcePackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return gceCommonPackageSet(t)
|
||||
}
|
||||
|
||||
// GCE RHUI image
|
||||
func gceRhuiPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"google-rhui-client-rhel8",
|
||||
},
|
||||
}.Append(gceCommonPackageSet(t))
|
||||
}
|
||||
|
|
@ -1,573 +0,0 @@
|
|||
package rhel8
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/container"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/fdo"
|
||||
"github.com/osbuild/osbuild-composer/internal/ignition"
|
||||
"github.com/osbuild/osbuild-composer/internal/image"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/oscap"
|
||||
"github.com/osbuild/osbuild-composer/internal/ostree"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/users"
|
||||
"github.com/osbuild/osbuild-composer/internal/workload"
|
||||
)
|
||||
|
||||
func osCustomizations(
|
||||
t *imageType,
|
||||
osPackageSet rpmmd.PackageSet,
|
||||
options distro.ImageOptions,
|
||||
containers []container.SourceSpec,
|
||||
c *blueprint.Customizations,
|
||||
) manifest.OSCustomizations {
|
||||
|
||||
imageConfig := t.getDefaultImageConfig()
|
||||
|
||||
osc := manifest.OSCustomizations{}
|
||||
|
||||
if t.bootable || t.rpmOstree {
|
||||
osc.KernelName = c.GetKernel().Name
|
||||
|
||||
var kernelOptions []string
|
||||
if t.kernelOptions != "" {
|
||||
kernelOptions = append(kernelOptions, t.kernelOptions)
|
||||
}
|
||||
if bpKernel := c.GetKernel(); bpKernel.Append != "" {
|
||||
kernelOptions = append(kernelOptions, bpKernel.Append)
|
||||
}
|
||||
osc.KernelOptionsAppend = kernelOptions
|
||||
if t.platform.GetArch() != platform.ARCH_S390X {
|
||||
osc.KernelOptionsBootloader = true
|
||||
}
|
||||
}
|
||||
|
||||
osc.ExtraBasePackages = osPackageSet.Include
|
||||
osc.ExcludeBasePackages = osPackageSet.Exclude
|
||||
osc.ExtraBaseRepos = osPackageSet.Repositories
|
||||
|
||||
osc.Containers = containers
|
||||
|
||||
osc.GPGKeyFiles = imageConfig.GPGKeyFiles
|
||||
if imageConfig.ExcludeDocs != nil {
|
||||
osc.ExcludeDocs = *imageConfig.ExcludeDocs
|
||||
}
|
||||
|
||||
if !t.bootISO {
|
||||
// don't put users and groups in the payload of an installer
|
||||
// add them via kickstart instead
|
||||
osc.Groups = users.GroupsFromBP(c.GetGroups())
|
||||
osc.Users = users.UsersFromBP(c.GetUsers())
|
||||
}
|
||||
|
||||
osc.EnabledServices = imageConfig.EnabledServices
|
||||
osc.DisabledServices = imageConfig.DisabledServices
|
||||
if imageConfig.DefaultTarget != nil {
|
||||
osc.DefaultTarget = *imageConfig.DefaultTarget
|
||||
}
|
||||
|
||||
osc.Firewall = imageConfig.Firewall
|
||||
if fw := c.GetFirewall(); fw != nil {
|
||||
options := osbuild.FirewallStageOptions{
|
||||
Ports: fw.Ports,
|
||||
}
|
||||
|
||||
if fw.Services != nil {
|
||||
options.EnabledServices = fw.Services.Enabled
|
||||
options.DisabledServices = fw.Services.Disabled
|
||||
}
|
||||
if fw.Zones != nil {
|
||||
for _, z := range fw.Zones {
|
||||
options.Zones = append(options.Zones, osbuild.FirewallZone{
|
||||
Name: *z.Name,
|
||||
Sources: z.Sources,
|
||||
})
|
||||
}
|
||||
}
|
||||
osc.Firewall = &options
|
||||
}
|
||||
|
||||
language, keyboard := c.GetPrimaryLocale()
|
||||
if language != nil {
|
||||
osc.Language = *language
|
||||
} else if imageConfig.Locale != nil {
|
||||
osc.Language = *imageConfig.Locale
|
||||
}
|
||||
if keyboard != nil {
|
||||
osc.Keyboard = keyboard
|
||||
} else if imageConfig.Keyboard != nil {
|
||||
osc.Keyboard = &imageConfig.Keyboard.Keymap
|
||||
if imageConfig.Keyboard.X11Keymap != nil {
|
||||
osc.X11KeymapLayouts = imageConfig.Keyboard.X11Keymap.Layouts
|
||||
}
|
||||
}
|
||||
|
||||
if hostname := c.GetHostname(); hostname != nil {
|
||||
osc.Hostname = *hostname
|
||||
}
|
||||
|
||||
timezone, ntpServers := c.GetTimezoneSettings()
|
||||
if timezone != nil {
|
||||
osc.Timezone = *timezone
|
||||
} else if imageConfig.Timezone != nil {
|
||||
osc.Timezone = *imageConfig.Timezone
|
||||
}
|
||||
|
||||
if len(ntpServers) > 0 {
|
||||
for _, server := range ntpServers {
|
||||
osc.NTPServers = append(osc.NTPServers, osbuild.ChronyConfigServer{Hostname: server})
|
||||
}
|
||||
} else if imageConfig.TimeSynchronization != nil {
|
||||
osc.NTPServers = imageConfig.TimeSynchronization.Servers
|
||||
osc.LeapSecTZ = imageConfig.TimeSynchronization.LeapsecTz
|
||||
}
|
||||
|
||||
// Relabel the tree, unless the `NoSElinux` flag is explicitly set to `true`
|
||||
if imageConfig.NoSElinux == nil || imageConfig.NoSElinux != nil && !*imageConfig.NoSElinux {
|
||||
osc.SElinux = "targeted"
|
||||
}
|
||||
|
||||
if oscapConfig := c.GetOpenSCAP(); oscapConfig != nil {
|
||||
if t.rpmOstree {
|
||||
panic("unexpected oscap options for ostree image type")
|
||||
}
|
||||
var datastream = oscapConfig.DataStream
|
||||
if datastream == "" {
|
||||
datastream = oscap.DefaultRHEL8Datastream(t.arch.distro.isRHEL())
|
||||
}
|
||||
osc.OpenSCAPConfig = osbuild.NewOscapRemediationStageOptions(
|
||||
osbuild.OscapConfig{
|
||||
Datastream: datastream,
|
||||
ProfileID: oscapConfig.ProfileID,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if t.arch.distro.isRHEL() && options.Facts != nil {
|
||||
osc.FactAPIType = &options.Facts.APIType
|
||||
}
|
||||
|
||||
var err error
|
||||
osc.Directories, err = blueprint.DirectoryCustomizationsToFsNodeDirectories(c.GetDirectories())
|
||||
if err != nil {
|
||||
// In theory this should never happen, because the blueprint directory customizations
|
||||
// should have been validated before this point.
|
||||
panic(fmt.Sprintf("failed to convert directory customizations to fs node directories: %v", err))
|
||||
}
|
||||
|
||||
osc.Files, err = blueprint.FileCustomizationsToFsNodeFiles(c.GetFiles())
|
||||
if err != nil {
|
||||
// In theory this should never happen, because the blueprint file customizations
|
||||
// should have been validated before this point.
|
||||
panic(fmt.Sprintf("failed to convert file customizations to fs node files: %v", err))
|
||||
}
|
||||
|
||||
// set yum repos first, so it doesn't get overridden by
|
||||
// imageConfig.YUMRepos
|
||||
osc.YUMRepos = imageConfig.YUMRepos
|
||||
|
||||
customRepos, err := c.GetRepositories()
|
||||
if err != nil {
|
||||
// This shouldn't happen and since the repos
|
||||
// should have already been validated
|
||||
panic(fmt.Sprintf("failed to get custom repos: %v", err))
|
||||
}
|
||||
|
||||
// This function returns a map of filename and corresponding yum repos
|
||||
// and a list of fs node files for the inline gpg keys so we can save
|
||||
// them to disk. This step also swaps the inline gpg key with the path
|
||||
// to the file in the os file tree
|
||||
yumRepos, gpgKeyFiles, err := blueprint.RepoCustomizationsToRepoConfigAndGPGKeyFiles(customRepos)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to convert inline gpgkeys to fs node files: %v", err))
|
||||
}
|
||||
|
||||
// add the gpg key files to the list of files to be added to the tree
|
||||
if len(gpgKeyFiles) > 0 {
|
||||
osc.Files = append(osc.Files, gpgKeyFiles...)
|
||||
}
|
||||
|
||||
for filename, repos := range yumRepos {
|
||||
osc.YUMRepos = append(osc.YUMRepos, osbuild.NewYumReposStageOptions(filename, repos))
|
||||
}
|
||||
|
||||
osc.ShellInit = imageConfig.ShellInit
|
||||
|
||||
osc.Grub2Config = imageConfig.Grub2Config
|
||||
osc.Sysconfig = imageConfig.Sysconfig
|
||||
osc.SystemdLogind = imageConfig.SystemdLogind
|
||||
osc.CloudInit = imageConfig.CloudInit
|
||||
osc.Modprobe = imageConfig.Modprobe
|
||||
osc.DracutConf = imageConfig.DracutConf
|
||||
osc.SystemdUnit = imageConfig.SystemdUnit
|
||||
osc.Authselect = imageConfig.Authselect
|
||||
osc.SELinuxConfig = imageConfig.SELinuxConfig
|
||||
osc.Tuned = imageConfig.Tuned
|
||||
osc.Tmpfilesd = imageConfig.Tmpfilesd
|
||||
osc.PamLimitsConf = imageConfig.PamLimitsConf
|
||||
osc.Sysctld = imageConfig.Sysctld
|
||||
osc.DNFConfig = imageConfig.DNFConfig
|
||||
osc.DNFAutomaticConfig = imageConfig.DNFAutomaticConfig
|
||||
osc.SshdConfig = imageConfig.SshdConfig
|
||||
osc.AuthConfig = imageConfig.Authconfig
|
||||
osc.PwQuality = imageConfig.PwQuality
|
||||
osc.RHSMConfig = imageConfig.RHSMConfig
|
||||
osc.Subscription = options.Subscription
|
||||
osc.WAAgentConfig = imageConfig.WAAgentConfig
|
||||
osc.UdevRules = imageConfig.UdevRules
|
||||
osc.GCPGuestAgentConfig = imageConfig.GCPGuestAgentConfig
|
||||
|
||||
return osc
|
||||
}
|
||||
|
||||
func liveImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
img := image.NewLiveImage()
|
||||
img.Platform = t.platform
|
||||
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], options, containers, customizations)
|
||||
img.Environment = t.environment
|
||||
img.Workload = workload
|
||||
img.Compression = t.compression
|
||||
// TODO: move generation into LiveImage
|
||||
pt, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img.PartitionTable = pt
|
||||
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func imageInstallerImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
img := image.NewAnacondaTarInstaller()
|
||||
|
||||
img.Platform = t.platform
|
||||
img.Workload = workload
|
||||
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], options, containers, customizations)
|
||||
img.ExtraBasePackages = packageSets[installerPkgsKey]
|
||||
img.Users = users.UsersFromBP(customizations.GetUsers())
|
||||
img.Groups = users.GroupsFromBP(customizations.GetGroups())
|
||||
|
||||
img.AdditionalDracutModules = []string{"prefixdevname", "prefixdevname-tools"}
|
||||
img.AdditionalAnacondaModules = []string{"org.fedoraproject.Anaconda.Modules.Users"}
|
||||
|
||||
img.SquashfsCompression = "xz"
|
||||
|
||||
// put the kickstart file in the root of the iso
|
||||
img.ISORootKickstart = true
|
||||
|
||||
d := t.arch.distro
|
||||
|
||||
img.ISOLabelTempl = d.isolabelTmpl
|
||||
img.Product = d.product
|
||||
img.OSName = "redhat"
|
||||
img.OSVersion = d.osVersion
|
||||
img.Release = fmt.Sprintf("%s %s", d.product, d.osVersion)
|
||||
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func tarImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
img := image.NewArchive()
|
||||
img.Platform = t.platform
|
||||
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], options, containers, customizations)
|
||||
img.Environment = t.environment
|
||||
img.Workload = workload
|
||||
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
|
||||
}
|
||||
|
||||
func edgeCommitImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
parentCommit, commitRef := makeOSTreeParentCommit(options.OSTree, t.OSTreeRef())
|
||||
img := image.NewOSTreeArchive(commitRef)
|
||||
|
||||
img.Platform = t.platform
|
||||
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], options, containers, customizations)
|
||||
img.Environment = t.environment
|
||||
img.Workload = workload
|
||||
img.OSTreeParent = parentCommit
|
||||
img.OSVersion = t.arch.distro.osVersion
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func edgeContainerImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
parentCommit, commitRef := makeOSTreeParentCommit(options.OSTree, t.OSTreeRef())
|
||||
img := image.NewOSTreeContainer(commitRef)
|
||||
|
||||
img.Platform = t.platform
|
||||
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], options, containers, customizations)
|
||||
img.ContainerLanguage = img.OSCustomizations.Language
|
||||
img.Environment = t.environment
|
||||
img.Workload = workload
|
||||
img.OSTreeParent = parentCommit
|
||||
img.OSVersion = t.arch.distro.osVersion
|
||||
img.ExtraContainerPackages = packageSets[containerPkgsKey]
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func edgeInstallerImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
d := t.arch.distro
|
||||
|
||||
commit, err := makeOSTreePayloadCommit(options.OSTree, t.OSTreeRef())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", t.Name(), err.Error())
|
||||
}
|
||||
|
||||
img := image.NewAnacondaOSTreeInstaller(commit)
|
||||
|
||||
img.Platform = t.platform
|
||||
img.ExtraBasePackages = packageSets[installerPkgsKey]
|
||||
img.Users = users.UsersFromBP(customizations.GetUsers())
|
||||
img.Groups = users.GroupsFromBP(customizations.GetGroups())
|
||||
|
||||
img.SquashfsCompression = "xz"
|
||||
img.AdditionalDracutModules = []string{"prefixdevname", "prefixdevname-tools"}
|
||||
|
||||
if len(img.Users)+len(img.Groups) > 0 {
|
||||
// only enable the users module if needed
|
||||
img.AdditionalAnacondaModules = []string{"org.fedoraproject.Anaconda.Modules.Users"}
|
||||
}
|
||||
|
||||
img.ISOLabelTempl = d.isolabelTmpl
|
||||
img.Product = d.product
|
||||
img.Variant = "edge"
|
||||
img.OSName = "rhel"
|
||||
img.OSVersion = d.osVersion
|
||||
img.Release = fmt.Sprintf("%s %s", d.product, d.osVersion)
|
||||
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func edgeRawImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
commit, err := makeOSTreePayloadCommit(options.OSTree, t.OSTreeRef())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", t.Name(), err.Error())
|
||||
}
|
||||
|
||||
img := image.NewOSTreeRawImage(commit)
|
||||
|
||||
img.Users = users.UsersFromBP(customizations.GetUsers())
|
||||
img.Groups = users.GroupsFromBP(customizations.GetGroups())
|
||||
|
||||
img.KernelOptionsAppend = []string{"modprobe.blacklist=vc4"}
|
||||
// TODO: move to image config
|
||||
img.Keyboard = "us"
|
||||
img.Locale = "C.UTF-8"
|
||||
|
||||
img.Platform = t.platform
|
||||
img.Workload = workload
|
||||
img.Remote = ostree.Remote{
|
||||
Name: "rhel-edge",
|
||||
URL: options.OSTree.URL,
|
||||
ContentURL: options.OSTree.ContentURL,
|
||||
}
|
||||
img.OSName = "redhat"
|
||||
|
||||
// TODO: move generation into LiveImage
|
||||
pt, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img.PartitionTable = pt
|
||||
|
||||
img.Filename = t.Filename()
|
||||
img.Compression = t.compression
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func edgeSimplifiedInstallerImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
commit, err := makeOSTreePayloadCommit(options.OSTree, t.OSTreeRef())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", t.Name(), err.Error())
|
||||
}
|
||||
|
||||
rawImg := image.NewOSTreeRawImage(commit)
|
||||
|
||||
rawImg.Users = users.UsersFromBP(customizations.GetUsers())
|
||||
rawImg.Groups = users.GroupsFromBP(customizations.GetGroups())
|
||||
|
||||
rawImg.KernelOptionsAppend = []string{"modprobe.blacklist=vc4"}
|
||||
rawImg.Keyboard = "us"
|
||||
rawImg.Locale = "C.UTF-8"
|
||||
|
||||
rawImg.Platform = t.platform
|
||||
rawImg.Workload = workload
|
||||
rawImg.Remote = ostree.Remote{
|
||||
Name: "rhel-edge",
|
||||
URL: options.OSTree.URL,
|
||||
ContentURL: options.OSTree.ContentURL,
|
||||
}
|
||||
rawImg.OSName = "redhat"
|
||||
|
||||
// TODO: move generation into LiveImage
|
||||
pt, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawImg.PartitionTable = pt
|
||||
|
||||
rawImg.Filename = t.Filename()
|
||||
|
||||
img := image.NewOSTreeSimplifiedInstaller(rawImg, customizations.InstallationDevice)
|
||||
img.ExtraBasePackages = packageSets[installerPkgsKey]
|
||||
// img.Workload = workload
|
||||
img.Platform = t.platform
|
||||
img.Filename = t.Filename()
|
||||
if bpFDO := customizations.GetFDO(); bpFDO != nil {
|
||||
img.FDO = fdo.FromBP(*bpFDO)
|
||||
}
|
||||
// ignition configs from blueprint
|
||||
if bpIgnition := customizations.GetIgnition(); bpIgnition != nil {
|
||||
if bpIgnition.FirstBoot != nil {
|
||||
img.IgnitionFirstBoot = ignition.FirstbootOptionsFromBP(*bpIgnition.FirstBoot)
|
||||
}
|
||||
if bpIgnition.Embedded != nil {
|
||||
var err error
|
||||
img.IgnitionEmbedded, err = ignition.EmbeddedOptionsFromBP(*bpIgnition.Embedded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d := t.arch.distro
|
||||
img.ISOLabelTempl = d.isolabelTmpl
|
||||
img.Product = d.product
|
||||
img.Variant = "edge"
|
||||
img.OSName = "redhat"
|
||||
img.OSVersion = d.osVersion
|
||||
img.AdditionalDracutModules = []string{"prefixdevname", "prefixdevname-tools"}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// Create an ostree SourceSpec to define an ostree parent commit using the user
|
||||
// options and the default ref for the image type. Additionally returns the
|
||||
// ref to be used for the new commit to be created.
|
||||
func makeOSTreeParentCommit(options *ostree.ImageOptions, defaultRef string) (*ostree.SourceSpec, string) {
|
||||
commitRef := defaultRef
|
||||
if options == nil {
|
||||
// nothing to do
|
||||
return nil, commitRef
|
||||
}
|
||||
if options.ImageRef != "" {
|
||||
// user option overrides default commit ref
|
||||
commitRef = options.ImageRef
|
||||
}
|
||||
|
||||
var parentCommit *ostree.SourceSpec
|
||||
if options.URL == "" {
|
||||
// no parent
|
||||
return nil, commitRef
|
||||
}
|
||||
|
||||
// ostree URL specified: set source spec for parent commit
|
||||
parentRef := options.ParentRef
|
||||
if parentRef == "" {
|
||||
// parent ref not set: use image ref
|
||||
parentRef = commitRef
|
||||
|
||||
}
|
||||
parentCommit = &ostree.SourceSpec{
|
||||
URL: options.URL,
|
||||
Ref: parentRef,
|
||||
RHSM: options.RHSM,
|
||||
}
|
||||
return parentCommit, commitRef
|
||||
}
|
||||
|
||||
// Create an ostree SourceSpec to define an ostree payload using the user options and the default ref for the image type.
|
||||
func makeOSTreePayloadCommit(options *ostree.ImageOptions, defaultRef string) (ostree.SourceSpec, error) {
|
||||
if options == nil || options.URL == "" {
|
||||
// this should be caught by checkOptions() in distro, but it's good
|
||||
// to guard against it here as well
|
||||
return ostree.SourceSpec{}, fmt.Errorf("ostree commit URL required")
|
||||
}
|
||||
|
||||
commitRef := defaultRef
|
||||
if options.ImageRef != "" {
|
||||
// user option overrides default commit ref
|
||||
commitRef = options.ImageRef
|
||||
}
|
||||
|
||||
return ostree.SourceSpec{
|
||||
URL: options.URL,
|
||||
Ref: commitRef,
|
||||
RHSM: options.RHSM,
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -1,418 +0,0 @@
|
|||
package rhel8
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/container"
|
||||
"github.com/osbuild/osbuild-composer/internal/disk"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/environment"
|
||||
"github.com/osbuild/osbuild-composer/internal/image"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/oscap"
|
||||
"github.com/osbuild/osbuild-composer/internal/ostree"
|
||||
"github.com/osbuild/osbuild-composer/internal/pathpolicy"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/workload"
|
||||
)
|
||||
|
||||
const (
|
||||
// package set names
|
||||
|
||||
// main/common os image package set name
|
||||
osPkgsKey = "os"
|
||||
|
||||
// container package set name
|
||||
containerPkgsKey = "container"
|
||||
|
||||
// installer package set name
|
||||
installerPkgsKey = "installer"
|
||||
|
||||
// blueprint package set name
|
||||
blueprintPkgsKey = "blueprint"
|
||||
)
|
||||
|
||||
type imageFunc func(workload workload.Workload, t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, packageSets map[string]rpmmd.PackageSet, containers []container.SourceSpec, rng *rand.Rand) (image.ImageKind, error)
|
||||
|
||||
type packageSetFunc func(t *imageType) rpmmd.PackageSet
|
||||
|
||||
type imageType struct {
|
||||
arch *architecture
|
||||
platform platform.Platform
|
||||
environment environment.Environment
|
||||
workload workload.Workload
|
||||
name string
|
||||
nameAliases []string
|
||||
filename string
|
||||
compression string // TODO: remove from image definition and make it a transport option
|
||||
mimeType string
|
||||
packageSets map[string]packageSetFunc
|
||||
defaultImageConfig *distro.ImageConfig
|
||||
kernelOptions string
|
||||
defaultSize uint64
|
||||
buildPipelines []string
|
||||
payloadPipelines []string
|
||||
exports []string
|
||||
image imageFunc
|
||||
|
||||
// bootISO: installable ISO
|
||||
bootISO bool
|
||||
// rpmOstree: edge/ostree
|
||||
rpmOstree bool
|
||||
// bootable image
|
||||
bootable bool
|
||||
// List of valid arches for the image type
|
||||
basePartitionTables distro.BasePartitionTableMap
|
||||
}
|
||||
|
||||
func (t *imageType) Name() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *imageType) Arch() distro.Arch {
|
||||
return t.arch
|
||||
}
|
||||
|
||||
func (t *imageType) Filename() string {
|
||||
return t.filename
|
||||
}
|
||||
|
||||
func (t *imageType) MIMEType() string {
|
||||
return t.mimeType
|
||||
}
|
||||
|
||||
func (t *imageType) OSTreeRef() string {
|
||||
d := t.arch.distro
|
||||
if t.rpmOstree {
|
||||
return fmt.Sprintf(d.ostreeRefTmpl, t.Arch().Name())
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t *imageType) Size(size uint64) uint64 {
|
||||
// Microsoft Azure requires vhd images to be rounded up to the nearest MB
|
||||
if t.name == "vhd" && size%common.MebiByte != 0 {
|
||||
size = (size/common.MebiByte + 1) * common.MebiByte
|
||||
}
|
||||
if size == 0 {
|
||||
size = t.defaultSize
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func (t *imageType) BuildPipelines() []string {
|
||||
return t.buildPipelines
|
||||
}
|
||||
|
||||
func (t *imageType) PayloadPipelines() []string {
|
||||
return t.payloadPipelines
|
||||
}
|
||||
|
||||
func (t *imageType) PayloadPackageSets() []string {
|
||||
return []string{blueprintPkgsKey}
|
||||
}
|
||||
|
||||
func (t *imageType) PackageSetsChains() map[string][]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *imageType) Exports() []string {
|
||||
if len(t.exports) > 0 {
|
||||
return t.exports
|
||||
}
|
||||
return []string{"assembler"}
|
||||
}
|
||||
|
||||
func (t *imageType) BootMode() distro.BootMode {
|
||||
if t.platform.GetUEFIVendor() != "" && t.platform.GetBIOSPlatform() != "" {
|
||||
return distro.BOOT_HYBRID
|
||||
} else if t.platform.GetUEFIVendor() != "" {
|
||||
return distro.BOOT_UEFI
|
||||
} else if t.platform.GetBIOSPlatform() != "" || t.platform.GetZiplSupport() {
|
||||
return distro.BOOT_LEGACY
|
||||
}
|
||||
return distro.BOOT_NONE
|
||||
}
|
||||
|
||||
func (t *imageType) getPartitionTable(
|
||||
mountpoints []blueprint.FilesystemCustomization,
|
||||
options distro.ImageOptions,
|
||||
rng *rand.Rand,
|
||||
) (*disk.PartitionTable, error) {
|
||||
archName := t.arch.Name()
|
||||
|
||||
basePartitionTable, exists := t.basePartitionTables[archName]
|
||||
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("no partition table defined for architecture %q for image type %q", archName, t.Name())
|
||||
}
|
||||
|
||||
imageSize := t.Size(options.Size)
|
||||
|
||||
lvmify := !t.rpmOstree
|
||||
|
||||
return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, lvmify, nil, rng)
|
||||
}
|
||||
|
||||
func (t *imageType) getDefaultImageConfig() *distro.ImageConfig {
|
||||
// ensure that image always returns non-nil default config
|
||||
imageConfig := t.defaultImageConfig
|
||||
if imageConfig == nil {
|
||||
imageConfig = &distro.ImageConfig{}
|
||||
}
|
||||
return imageConfig.InheritFrom(t.arch.distro.getDefaultImageConfig())
|
||||
|
||||
}
|
||||
|
||||
func (t *imageType) PartitionType() string {
|
||||
archName := t.arch.Name()
|
||||
basePartitionTable, exists := t.basePartitionTables[archName]
|
||||
if !exists {
|
||||
return ""
|
||||
}
|
||||
|
||||
return basePartitionTable.Type
|
||||
}
|
||||
|
||||
func (t *imageType) Manifest(bp *blueprint.Blueprint,
|
||||
options distro.ImageOptions,
|
||||
repos []rpmmd.RepoConfig,
|
||||
seed int64) (*manifest.Manifest, []string, error) {
|
||||
|
||||
warnings, err := t.checkOptions(bp, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// merge package sets that appear in the image type with the package sets
|
||||
// of the same name from the distro and arch
|
||||
staticPackageSets := make(map[string]rpmmd.PackageSet)
|
||||
|
||||
for name, getter := range t.packageSets {
|
||||
staticPackageSets[name] = getter(t)
|
||||
}
|
||||
|
||||
// amend with repository information and collect payload repos
|
||||
payloadRepos := make([]rpmmd.RepoConfig, 0)
|
||||
for _, repo := range repos {
|
||||
if len(repo.PackageSets) > 0 {
|
||||
// only apply the repo to the listed package sets
|
||||
for _, psName := range repo.PackageSets {
|
||||
if slices.Contains(t.PayloadPackageSets(), psName) {
|
||||
payloadRepos = append(payloadRepos, repo)
|
||||
}
|
||||
ps := staticPackageSets[psName]
|
||||
ps.Repositories = append(ps.Repositories, repo)
|
||||
staticPackageSets[psName] = ps
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w := t.workload
|
||||
if w == nil {
|
||||
cw := &workload.Custom{
|
||||
BaseWorkload: workload.BaseWorkload{
|
||||
Repos: payloadRepos,
|
||||
},
|
||||
Packages: bp.GetPackagesEx(false),
|
||||
}
|
||||
if services := bp.Customizations.GetServices(); services != nil {
|
||||
cw.Services = services.Enabled
|
||||
cw.DisabledServices = services.Disabled
|
||||
}
|
||||
w = cw
|
||||
}
|
||||
|
||||
containerSources := make([]container.SourceSpec, len(bp.Containers))
|
||||
for idx := range bp.Containers {
|
||||
containerSources[idx] = container.SourceSpec(bp.Containers[idx])
|
||||
}
|
||||
|
||||
source := rand.NewSource(seed)
|
||||
// math/rand is good enough in this case
|
||||
/* #nosec G404 */
|
||||
rng := rand.New(source)
|
||||
|
||||
if t.image == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
img, err := t.image(w, t, bp.Customizations, options, staticPackageSets, containerSources, rng)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
mf := manifest.New()
|
||||
mf.Distro = manifest.DISTRO_EL8
|
||||
_, err = img.InstantiateManifest(&mf, repos, t.arch.distro.runner, rng)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &mf, warnings, err
|
||||
}
|
||||
|
||||
// checkOptions checks the validity and compatibility of options and customizations for the image type.
|
||||
// Returns ([]string, error) where []string, if non-nil, will hold any generated warnings (e.g. deprecation notices).
|
||||
func (t *imageType) checkOptions(bp *blueprint.Blueprint, options distro.ImageOptions) ([]string, error) {
|
||||
customizations := bp.Customizations
|
||||
// holds warnings (e.g. deprecation notices)
|
||||
var warnings []string
|
||||
if t.workload != nil {
|
||||
// For now, if an image type defines its own workload, don't allow any
|
||||
// user customizations.
|
||||
// Soon we will have more workflows and each will define its allowed
|
||||
// set of customizations. The current set of customizations defined in
|
||||
// the blueprint spec corresponds to the Custom workflow.
|
||||
if customizations != nil {
|
||||
return warnings, fmt.Errorf("image type %q does not support customizations", t.name)
|
||||
}
|
||||
}
|
||||
// we do not support embedding containers on ostree-derived images, only on commits themselves
|
||||
if len(bp.Containers) > 0 && t.rpmOstree && (t.name != "edge-commit" && t.name != "edge-container") {
|
||||
return warnings, fmt.Errorf("embedding containers is not supported for %s on %s", t.name, t.arch.distro.name)
|
||||
}
|
||||
|
||||
ostreeURL := ""
|
||||
if options.OSTree != nil {
|
||||
if options.OSTree.ParentRef != "" && options.OSTree.URL == "" {
|
||||
// specifying parent ref also requires URL
|
||||
return nil, ostree.NewParameterComboError("ostree parent ref specified, but no URL to retrieve it")
|
||||
}
|
||||
ostreeURL = options.OSTree.URL
|
||||
}
|
||||
|
||||
if t.bootISO && t.rpmOstree {
|
||||
// ostree-based ISOs require a URL from which to pull a payload commit
|
||||
if ostreeURL == "" {
|
||||
return warnings, fmt.Errorf("boot ISO image type %q requires specifying a URL from which to retrieve the OSTree commit", t.name)
|
||||
}
|
||||
|
||||
if t.name == "edge-simplified-installer" {
|
||||
allowed := []string{"InstallationDevice", "FDO", "User", "Group"}
|
||||
if err := customizations.CheckAllowed(allowed...); err != nil {
|
||||
return warnings, fmt.Errorf("unsupported blueprint customizations found for boot ISO image type %q: (allowed: %s)", t.name, strings.Join(allowed, ", "))
|
||||
}
|
||||
if customizations.GetInstallationDevice() == "" {
|
||||
return warnings, fmt.Errorf("boot ISO image type %q requires specifying an installation device to install to", t.name)
|
||||
}
|
||||
//making fdo optional so that simplified installer can be composed w/o the FDO section in the blueprint
|
||||
if customizations.GetFDO() != nil {
|
||||
if customizations.GetFDO().ManufacturingServerURL == "" {
|
||||
return warnings, fmt.Errorf("boot ISO image type %q requires specifying FDO.ManufacturingServerURL configuration to install to", t.name)
|
||||
}
|
||||
var diunSet int
|
||||
if customizations.GetFDO().DiunPubKeyHash != "" {
|
||||
diunSet++
|
||||
}
|
||||
if customizations.GetFDO().DiunPubKeyInsecure != "" {
|
||||
diunSet++
|
||||
}
|
||||
if customizations.GetFDO().DiunPubKeyRootCerts != "" {
|
||||
diunSet++
|
||||
}
|
||||
if diunSet != 1 {
|
||||
return warnings, fmt.Errorf("boot ISO image type %q requires specifying one of [FDO.DiunPubKeyHash,FDO.DiunPubKeyInsecure,FDO.DiunPubKeyRootCerts] configuration to install to", t.name)
|
||||
}
|
||||
}
|
||||
} else if t.name == "edge-installer" {
|
||||
allowed := []string{"User", "Group"}
|
||||
if err := customizations.CheckAllowed(allowed...); err != nil {
|
||||
return warnings, fmt.Errorf("unsupported blueprint customizations found for boot ISO image type %q: (allowed: %s)", t.name, strings.Join(allowed, ", "))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if t.name == "edge-raw-image" {
|
||||
// ostree-based bootable images require a URL from which to pull a payload commit
|
||||
if ostreeURL == "" {
|
||||
return warnings, fmt.Errorf("edge raw images require specifying a URL from which to retrieve the OSTree commit")
|
||||
}
|
||||
|
||||
allowed := []string{"User", "Group"}
|
||||
if err := customizations.CheckAllowed(allowed...); err != nil {
|
||||
return warnings, fmt.Errorf("unsupported blueprint customizations found for image type %q: (allowed: %s)", t.name, strings.Join(allowed, ", "))
|
||||
}
|
||||
// TODO: consider additional checks, such as those in "edge-simplified-installer"
|
||||
}
|
||||
|
||||
// warn that user & group customizations on edge-commit, edge-container are deprecated
|
||||
// TODO(edge): directly error if these options are provided when rhel-9.5's time arrives
|
||||
if t.name == "edge-commit" || t.name == "edge-container" {
|
||||
if customizations.GetUsers() != nil {
|
||||
w := fmt.Sprintf("Please note that user customizations on %q image type are deprecated and will be removed in the near future\n", t.name)
|
||||
log.Print(w)
|
||||
warnings = append(warnings, w)
|
||||
}
|
||||
if customizations.GetGroups() != nil {
|
||||
w := fmt.Sprintf("Please note that group customizations on %q image type are deprecated and will be removed in the near future\n", t.name)
|
||||
log.Print(w)
|
||||
warnings = append(warnings, w)
|
||||
}
|
||||
}
|
||||
|
||||
if kernelOpts := customizations.GetKernel(); kernelOpts.Append != "" && t.rpmOstree && (!t.bootable || t.bootISO) {
|
||||
return warnings, fmt.Errorf("kernel boot parameter customizations are not supported for ostree types")
|
||||
}
|
||||
|
||||
mountpoints := customizations.GetFilesystems()
|
||||
|
||||
if mountpoints != nil && t.rpmOstree {
|
||||
return warnings, fmt.Errorf("Custom mountpoints are not supported for ostree types")
|
||||
}
|
||||
|
||||
err := blueprint.CheckMountpointsPolicy(mountpoints, pathpolicy.MountpointPolicies)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
if osc := customizations.GetOpenSCAP(); osc != nil {
|
||||
// only add support for RHEL 8.7 and above.
|
||||
if common.VersionLessThan(t.arch.distro.osVersion, "8.7") {
|
||||
return warnings, fmt.Errorf(fmt.Sprintf("OpenSCAP unsupported os version: %s", t.arch.distro.osVersion))
|
||||
}
|
||||
supported := oscap.IsProfileAllowed(osc.ProfileID, oscapProfileAllowList)
|
||||
if !supported {
|
||||
return warnings, fmt.Errorf(fmt.Sprintf("OpenSCAP unsupported profile: %s", osc.ProfileID))
|
||||
}
|
||||
if t.rpmOstree {
|
||||
return warnings, fmt.Errorf("OpenSCAP customizations are not supported for ostree types")
|
||||
}
|
||||
if osc.ProfileID == "" {
|
||||
return warnings, fmt.Errorf("OpenSCAP profile cannot be empty")
|
||||
}
|
||||
}
|
||||
|
||||
// Check Directory/File Customizations are valid
|
||||
dc := customizations.GetDirectories()
|
||||
fc := customizations.GetFiles()
|
||||
|
||||
err = blueprint.ValidateDirFileCustomizations(dc, fc)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
err = blueprint.CheckDirectoryCustomizationsPolicy(dc, pathpolicy.CustomDirectoriesPolicies)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
err = blueprint.CheckFileCustomizationsPolicy(fc, pathpolicy.CustomFilesPolicies)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
// check if repository customizations are valid
|
||||
_, err = customizations.GetRepositories()
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
return warnings, nil
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
package rhel8
|
||||
|
||||
// This file defines package sets that are used by more than one image type.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
// installer boot package sets, needed for booting and
|
||||
// also in the build host
|
||||
|
||||
func anacondaBootPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{}
|
||||
|
||||
grubCommon := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"grub2-tools",
|
||||
"grub2-tools-extra",
|
||||
"grub2-tools-minimal",
|
||||
},
|
||||
}
|
||||
|
||||
efiCommon := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"efibootmgr",
|
||||
},
|
||||
}
|
||||
|
||||
switch t.arch.Name() {
|
||||
case platform.ARCH_X86_64.String():
|
||||
ps = ps.Append(grubCommon)
|
||||
ps = ps.Append(efiCommon)
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"grub2-efi-ia32-cdboot",
|
||||
"grub2-efi-x64",
|
||||
"grub2-efi-x64-cdboot",
|
||||
"grub2-pc",
|
||||
"grub2-pc-modules",
|
||||
"shim-ia32",
|
||||
"shim-x64",
|
||||
"syslinux",
|
||||
"syslinux-nonlinux",
|
||||
},
|
||||
})
|
||||
case platform.ARCH_AARCH64.String():
|
||||
ps = ps.Append(grubCommon)
|
||||
ps = ps.Append(efiCommon)
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"grub2-efi-aa64-cdboot",
|
||||
"grub2-efi-aa64",
|
||||
"shim-aa64",
|
||||
},
|
||||
})
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported arch: %s", t.arch.Name()))
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
// packages that are only in some (sub)-distributions
|
||||
func distroSpecificPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
if t.arch.distro.isRHEL() {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{"insights-client"},
|
||||
}
|
||||
}
|
||||
return rpmmd.PackageSet{}
|
||||
}
|
||||
|
|
@ -1,425 +0,0 @@
|
|||
package rhel8
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/disk"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
)
|
||||
|
||||
var defaultBasePartitionTables = distro.BasePartitionTableMap{
|
||||
platform.ARCH_X86_64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 1 * common.MebiByte,
|
||||
Bootable: true,
|
||||
Type: disk.BIOSBootPartitionGUID,
|
||||
UUID: disk.BIOSBootPartitionUUID,
|
||||
},
|
||||
{
|
||||
Size: 100 * common.MebiByte,
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte, // 2 GiB
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
platform.ARCH_AARCH64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 100 * common.MebiByte,
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte, // 2 GiB
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
platform.ARCH_PPC64LE.String(): disk.PartitionTable{
|
||||
UUID: "0x14fc63d2",
|
||||
Type: "dos",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 4 * common.MebiByte,
|
||||
Type: "41",
|
||||
Bootable: true,
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte, // 2 GiB
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
platform.ARCH_S390X.String(): disk.PartitionTable{
|
||||
UUID: "0x14fc63d2",
|
||||
Type: "dos",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 2 * common.GibiByte, // 2 GiB
|
||||
Bootable: true,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var ec2BasePartitionTables = distro.BasePartitionTableMap{
|
||||
platform.ARCH_X86_64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 1 * common.MebiByte,
|
||||
Bootable: true,
|
||||
Type: disk.BIOSBootPartitionGUID,
|
||||
UUID: disk.BIOSBootPartitionUUID,
|
||||
},
|
||||
{
|
||||
Size: 200 * common.MebiByte,
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
Label: "EFI-SYSTEM",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 500 * common.MebiByte,
|
||||
Type: disk.XBootLDRPartitionGUID,
|
||||
UUID: disk.FilesystemDataUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/boot",
|
||||
Label: "boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte,
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
platform.ARCH_AARCH64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 200 * common.MebiByte,
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
Label: "EFI-SYSTEM",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 500 * common.MebiByte,
|
||||
Type: disk.XBootLDRPartitionGUID,
|
||||
UUID: disk.FilesystemDataUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/boot",
|
||||
Label: "boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte,
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// ec2LegacyBasePartitionTables is the partition table layout for RHEL EC2
|
||||
// images prior to 8.9. It is used for backwards compatibility.
|
||||
var ec2LegacyBasePartitionTables = distro.BasePartitionTableMap{
|
||||
platform.ARCH_X86_64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 1 * common.MebiByte,
|
||||
Bootable: true,
|
||||
Type: disk.BIOSBootPartitionGUID,
|
||||
UUID: disk.BIOSBootPartitionUUID,
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte, // 2 GiB
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
platform.ARCH_AARCH64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 200 * common.MebiByte,
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 512 * common.MebiByte,
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.FilesystemDataUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte, // 2 GiB
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var edgeBasePartitionTables = distro.BasePartitionTableMap{
|
||||
platform.ARCH_X86_64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 1 * common.MebiByte,
|
||||
Bootable: true,
|
||||
Type: disk.BIOSBootPartitionGUID,
|
||||
UUID: disk.BIOSBootPartitionUUID,
|
||||
},
|
||||
{
|
||||
Size: 127 * common.MebiByte, // 127 MB
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
Label: "EFI-SYSTEM",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 384 * common.MebiByte, // 384 MB
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.FilesystemDataUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/boot",
|
||||
Label: "boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 1,
|
||||
FSTabPassNo: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte, // 2 GiB
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.LUKSContainer{
|
||||
Label: "crypt_root",
|
||||
Cipher: "cipher_null",
|
||||
Passphrase: "osbuild",
|
||||
PBKDF: disk.Argon2id{
|
||||
Memory: 32,
|
||||
Iterations: 4,
|
||||
Parallelism: 1,
|
||||
},
|
||||
Clevis: &disk.ClevisBind{
|
||||
Pin: "null",
|
||||
Policy: "{}",
|
||||
RemovePassphrase: true,
|
||||
},
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
platform.ARCH_AARCH64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 127 * common.MebiByte, // 127 MB
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
Label: "EFI-SYSTEM",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 384 * common.MebiByte, // 384 MB
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.FilesystemDataUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/boot",
|
||||
Label: "boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 1,
|
||||
FSTabPassNo: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte, // 2 GiB
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.LUKSContainer{
|
||||
Label: "crypt_root",
|
||||
Cipher: "cipher_null",
|
||||
Passphrase: "osbuild",
|
||||
PBKDF: disk.Argon2id{
|
||||
Memory: 32,
|
||||
Iterations: 4,
|
||||
Parallelism: 1,
|
||||
},
|
||||
Clevis: &disk.ClevisBind{
|
||||
Pin: "null",
|
||||
Policy: "{}",
|
||||
RemovePassphrase: true,
|
||||
},
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -1,168 +0,0 @@
|
|||
package rhel8
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/subscription"
|
||||
)
|
||||
|
||||
func qcow2ImgType(rd distribution) imageType {
|
||||
it := imageType{
|
||||
name: "qcow2",
|
||||
filename: "disk.qcow2",
|
||||
mimeType: "application/x-qemu-disk",
|
||||
kernelOptions: "console=tty0 console=ttyS0,115200n8 no_timer_check net.ifnames=0 crashkernel=auto",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: qcow2CommonPackageSet,
|
||||
},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
},
|
||||
bootable: true,
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "qcow2"},
|
||||
exports: []string{"qcow2"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
|
||||
if rd.isRHEL() {
|
||||
it.defaultImageConfig.RHSMConfig = map[subscription.RHSMStatus]*osbuild.RHSMStageOptions{
|
||||
subscription.RHSMConfigNoSubscription: {
|
||||
DnfPlugins: &osbuild.RHSMStageOptionsDnfPlugins{
|
||||
ProductID: &osbuild.RHSMStageOptionsDnfPlugin{
|
||||
Enabled: false,
|
||||
},
|
||||
SubscriptionManager: &osbuild.RHSMStageOptionsDnfPlugin{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return it
|
||||
}
|
||||
|
||||
func openstackImgType() imageType {
|
||||
return imageType{
|
||||
name: "openstack",
|
||||
filename: "disk.qcow2",
|
||||
mimeType: "application/x-qemu-disk",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: openstackCommonPackageSet,
|
||||
},
|
||||
kernelOptions: "ro net.ifnames=0",
|
||||
bootable: true,
|
||||
defaultSize: 4 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "qcow2"},
|
||||
exports: []string{"qcow2"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
}
|
||||
|
||||
func qcow2CommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"@core",
|
||||
"authselect-compat",
|
||||
"chrony",
|
||||
"cloud-init",
|
||||
"cloud-utils-growpart",
|
||||
"cockpit-system",
|
||||
"cockpit-ws",
|
||||
"dhcp-client",
|
||||
"dnf",
|
||||
"dnf-utils",
|
||||
"dosfstools",
|
||||
"dracut-norescue",
|
||||
"net-tools",
|
||||
"NetworkManager",
|
||||
"nfs-utils",
|
||||
"oddjob",
|
||||
"oddjob-mkhomedir",
|
||||
"psmisc",
|
||||
"python3-jsonschema",
|
||||
"qemu-guest-agent",
|
||||
"redhat-release",
|
||||
"redhat-release-eula",
|
||||
"rsync",
|
||||
"tar",
|
||||
"tcpdump",
|
||||
"yum",
|
||||
},
|
||||
Exclude: []string{
|
||||
"aic94xx-firmware",
|
||||
"alsa-firmware",
|
||||
"alsa-lib",
|
||||
"alsa-tools-firmware",
|
||||
"biosdevname",
|
||||
"dnf-plugin-spacewalk",
|
||||
"dracut-config-rescue",
|
||||
"fedora-release",
|
||||
"fedora-repos",
|
||||
"firewalld",
|
||||
"fwupd",
|
||||
"iprutils",
|
||||
"ivtv-firmware",
|
||||
"iwl1000-firmware",
|
||||
"iwl100-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl3945-firmware",
|
||||
"iwl4965-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6000-firmware",
|
||||
"iwl6000g2a-firmware",
|
||||
"iwl6000g2b-firmware",
|
||||
"iwl6050-firmware",
|
||||
"iwl7260-firmware",
|
||||
"langpacks-*",
|
||||
"langpacks-en",
|
||||
"langpacks-en",
|
||||
"libertas-sd8686-firmware",
|
||||
"libertas-sd8787-firmware",
|
||||
"libertas-usb8388-firmware",
|
||||
"nss",
|
||||
"plymouth",
|
||||
"rng-tools",
|
||||
"udisks2",
|
||||
},
|
||||
}.Append(distroSpecificPackageSet(t))
|
||||
|
||||
// Ensure to not pull in subscription-manager on non-RHEL distro
|
||||
if t.arch.distro.isRHEL() {
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"subscription-manager-cockpit",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
func openstackCommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
// Defaults
|
||||
"@Core", "langpacks-en",
|
||||
|
||||
// From the lorax kickstart
|
||||
"selinux-policy-targeted", "cloud-init", "qemu-guest-agent",
|
||||
"spice-vdagent",
|
||||
},
|
||||
Exclude: []string{
|
||||
"dracut-config-rescue", "rng-tools",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -1,174 +0,0 @@
|
|||
package rhel8
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
// sapImageConfig returns the SAP specific ImageConfig data
|
||||
func sapImageConfig(rd distribution) *distro.ImageConfig {
|
||||
return &distro.ImageConfig{
|
||||
SELinuxConfig: &osbuild.SELinuxConfigStageOptions{
|
||||
State: osbuild.SELinuxStatePermissive,
|
||||
},
|
||||
// RHBZ#1960617
|
||||
Tuned: osbuild.NewTunedStageOptions("sap-hana"),
|
||||
// RHBZ#1959979
|
||||
Tmpfilesd: []*osbuild.TmpfilesdStageOptions{
|
||||
osbuild.NewTmpfilesdStageOptions("sap.conf",
|
||||
[]osbuild.TmpfilesdConfigLine{
|
||||
{
|
||||
Type: "x",
|
||||
Path: "/tmp/.sap*",
|
||||
},
|
||||
{
|
||||
Type: "x",
|
||||
Path: "/tmp/.hdb*lock",
|
||||
},
|
||||
{
|
||||
Type: "x",
|
||||
Path: "/tmp/.trex*lock",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
// RHBZ#1959963
|
||||
PamLimitsConf: []*osbuild.PamLimitsConfStageOptions{
|
||||
osbuild.NewPamLimitsConfStageOptions("99-sap.conf",
|
||||
[]osbuild.PamLimitsConfigLine{
|
||||
{
|
||||
Domain: "@sapsys",
|
||||
Type: osbuild.PamLimitsTypeHard,
|
||||
Item: osbuild.PamLimitsItemNofile,
|
||||
Value: osbuild.PamLimitsValueInt(1048576),
|
||||
},
|
||||
{
|
||||
Domain: "@sapsys",
|
||||
Type: osbuild.PamLimitsTypeSoft,
|
||||
Item: osbuild.PamLimitsItemNofile,
|
||||
Value: osbuild.PamLimitsValueInt(1048576),
|
||||
},
|
||||
{
|
||||
Domain: "@dba",
|
||||
Type: osbuild.PamLimitsTypeHard,
|
||||
Item: osbuild.PamLimitsItemNofile,
|
||||
Value: osbuild.PamLimitsValueInt(1048576),
|
||||
},
|
||||
{
|
||||
Domain: "@dba",
|
||||
Type: osbuild.PamLimitsTypeSoft,
|
||||
Item: osbuild.PamLimitsItemNofile,
|
||||
Value: osbuild.PamLimitsValueInt(1048576),
|
||||
},
|
||||
{
|
||||
Domain: "@sapsys",
|
||||
Type: osbuild.PamLimitsTypeHard,
|
||||
Item: osbuild.PamLimitsItemNproc,
|
||||
Value: osbuild.PamLimitsValueUnlimited,
|
||||
},
|
||||
{
|
||||
Domain: "@sapsys",
|
||||
Type: osbuild.PamLimitsTypeSoft,
|
||||
Item: osbuild.PamLimitsItemNproc,
|
||||
Value: osbuild.PamLimitsValueUnlimited,
|
||||
},
|
||||
{
|
||||
Domain: "@dba",
|
||||
Type: osbuild.PamLimitsTypeHard,
|
||||
Item: osbuild.PamLimitsItemNproc,
|
||||
Value: osbuild.PamLimitsValueUnlimited,
|
||||
},
|
||||
{
|
||||
Domain: "@dba",
|
||||
Type: osbuild.PamLimitsTypeSoft,
|
||||
Item: osbuild.PamLimitsItemNproc,
|
||||
Value: osbuild.PamLimitsValueUnlimited,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
// RHBZ#1959962
|
||||
Sysctld: []*osbuild.SysctldStageOptions{
|
||||
osbuild.NewSysctldStageOptions("sap.conf",
|
||||
[]osbuild.SysctldConfigLine{
|
||||
{
|
||||
Key: "kernel.pid_max",
|
||||
Value: "4194304",
|
||||
},
|
||||
{
|
||||
Key: "vm.max_map_count",
|
||||
Value: "2147483647",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
// E4S/EUS
|
||||
DNFConfig: []*osbuild.DNFConfigStageOptions{
|
||||
osbuild.NewDNFConfigStageOptions(
|
||||
[]osbuild.DNFVariable{
|
||||
{
|
||||
Name: "releasever",
|
||||
Value: rd.osVersion,
|
||||
},
|
||||
},
|
||||
nil,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func SapPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
packageSet := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
// RHBZ#2074107
|
||||
"@Server",
|
||||
// SAP System Roles
|
||||
// https://access.redhat.com/sites/default/files/attachments/rhel_system_roles_for_sap_1.pdf
|
||||
"rhel-system-roles-sap",
|
||||
// RHBZ#1959813
|
||||
"bind-utils",
|
||||
"compat-sap-c++-9",
|
||||
"compat-sap-c++-10", // RHBZ#2074114
|
||||
"nfs-utils",
|
||||
"tcsh",
|
||||
// RHBZ#1959955
|
||||
"uuidd",
|
||||
// RHBZ#1959923
|
||||
"cairo",
|
||||
"expect",
|
||||
"graphviz",
|
||||
"gtk2",
|
||||
"iptraf-ng",
|
||||
"krb5-workstation",
|
||||
"libaio",
|
||||
"libatomic",
|
||||
"libcanberra-gtk2",
|
||||
"libicu",
|
||||
"libpng12",
|
||||
"libtool-ltdl",
|
||||
"lm_sensors",
|
||||
"net-tools",
|
||||
"numactl",
|
||||
"PackageKit-gtk3-module",
|
||||
"xorg-x11-xauth",
|
||||
// RHBZ#1960617
|
||||
"tuned-profiles-sap-hana",
|
||||
// RHBZ#1961168
|
||||
"libnsl",
|
||||
},
|
||||
}
|
||||
|
||||
if common.VersionLessThan(t.arch.distro.osVersion, "8.6") {
|
||||
packageSet = packageSet.Append(rpmmd.PackageSet{
|
||||
Include: []string{"ansible"},
|
||||
})
|
||||
} else {
|
||||
// 8.6+ and CS8 (image type does not exist on 8.5)
|
||||
packageSet = packageSet.Append(rpmmd.PackageSet{
|
||||
Include: []string{"ansible-core"}, // RHBZ#2077356
|
||||
})
|
||||
}
|
||||
return packageSet
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
package rhel8
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
const vmdkKernelOptions = "ro net.ifnames=0"
|
||||
|
||||
func vmdkImgType() imageType {
|
||||
return imageType{
|
||||
name: "vmdk",
|
||||
filename: "disk.vmdk",
|
||||
mimeType: "application/x-vmdk",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: vmdkCommonPackageSet,
|
||||
},
|
||||
kernelOptions: vmdkKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 4 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "vmdk"},
|
||||
exports: []string{"vmdk"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
}
|
||||
|
||||
func ovaImgType() imageType {
|
||||
return imageType{
|
||||
name: "ova",
|
||||
filename: "image.ova",
|
||||
mimeType: "application/ovf",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: vmdkCommonPackageSet,
|
||||
},
|
||||
kernelOptions: vmdkKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 4 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "vmdk", "ovf", "archive"},
|
||||
exports: []string{"archive"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
}
|
||||
|
||||
func vmdkCommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"@core",
|
||||
"chrony",
|
||||
"cloud-init",
|
||||
"firewalld",
|
||||
"langpacks-en",
|
||||
"open-vm-tools",
|
||||
"selinux-policy-targeted",
|
||||
},
|
||||
Exclude: []string{
|
||||
"dracut-config-rescue",
|
||||
"rng-tools",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package rhel8
|
||||
|
||||
import "github.com/osbuild/osbuild-composer/internal/workload"
|
||||
|
||||
// rhel8Workload is a RHEL-8-specific implementation of the workload interface
|
||||
// for internal workload variants.
|
||||
type rhel8Workload struct {
|
||||
workload.BaseWorkload
|
||||
packages []string
|
||||
}
|
||||
|
||||
func (w rhel8Workload) GetPackages() []string {
|
||||
return w.packages
|
||||
}
|
||||
|
||||
func eapWorkload() workload.Workload {
|
||||
w := rhel8Workload{}
|
||||
w.packages = []string{
|
||||
"java-1.8.0-openjdk",
|
||||
"java-1.8.0-openjdk-devel",
|
||||
"eap7-wildfly",
|
||||
"eap7-artemis-native-wildfly",
|
||||
}
|
||||
|
||||
return &w
|
||||
}
|
||||
|
|
@ -1,475 +0,0 @@
|
|||
package rhel9
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/subscription"
|
||||
)
|
||||
|
||||
const amiKernelOptions = "console=ttyS0,115200n8 console=tty0 net.ifnames=0 rd.blacklist=nouveau nvme_core.io_timeout=4294967295"
|
||||
|
||||
var (
|
||||
amiImgTypeX86_64 = imageType{
|
||||
name: "ami",
|
||||
filename: "image.raw",
|
||||
mimeType: "application/octet-stream",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: ec2CommonPackageSet,
|
||||
},
|
||||
kernelOptions: amiKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image"},
|
||||
exports: []string{"image"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
|
||||
ec2ImgTypeX86_64 = imageType{
|
||||
name: "ec2",
|
||||
filename: "image.raw.xz",
|
||||
mimeType: "application/xz",
|
||||
compression: "xz",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: rhelEc2PackageSet,
|
||||
},
|
||||
kernelOptions: amiKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "xz"},
|
||||
exports: []string{"xz"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
|
||||
ec2HaImgTypeX86_64 = imageType{
|
||||
name: "ec2-ha",
|
||||
filename: "image.raw.xz",
|
||||
mimeType: "application/xz",
|
||||
compression: "xz",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
buildPkgsKey: ec2BuildPackageSet,
|
||||
osPkgsKey: rhelEc2HaPackageSet,
|
||||
},
|
||||
kernelOptions: amiKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "xz"},
|
||||
exports: []string{"xz"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
|
||||
amiImgTypeAarch64 = imageType{
|
||||
name: "ami",
|
||||
filename: "image.raw",
|
||||
mimeType: "application/octet-stream",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
buildPkgsKey: ec2BuildPackageSet,
|
||||
osPkgsKey: ec2CommonPackageSet,
|
||||
},
|
||||
kernelOptions: "console=ttyS0,115200n8 console=tty0 net.ifnames=0 rd.blacklist=nouveau nvme_core.io_timeout=4294967295 iommu.strict=0",
|
||||
bootable: true,
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image"},
|
||||
exports: []string{"image"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
|
||||
ec2ImgTypeAarch64 = imageType{
|
||||
name: "ec2",
|
||||
filename: "image.raw.xz",
|
||||
mimeType: "application/xz",
|
||||
compression: "xz",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
buildPkgsKey: ec2BuildPackageSet,
|
||||
osPkgsKey: rhelEc2PackageSet,
|
||||
},
|
||||
kernelOptions: "console=ttyS0,115200n8 console=tty0 net.ifnames=0 rd.blacklist=nouveau nvme_core.io_timeout=4294967295 iommu.strict=0",
|
||||
bootable: true,
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "xz"},
|
||||
exports: []string{"xz"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
|
||||
ec2SapImgTypeX86_64 = imageType{
|
||||
name: "ec2-sap",
|
||||
filename: "image.raw.xz",
|
||||
mimeType: "application/xz",
|
||||
compression: "xz",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
buildPkgsKey: ec2BuildPackageSet,
|
||||
osPkgsKey: rhelEc2SapPackageSet,
|
||||
},
|
||||
kernelOptions: "console=ttyS0,115200n8 console=tty0 net.ifnames=0 rd.blacklist=nouveau nvme_core.io_timeout=4294967295 processor.max_cstate=1 intel_idle.max_cstate=1",
|
||||
bootable: true,
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "xz"},
|
||||
exports: []string{"xz"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
)
|
||||
|
||||
// default EC2 images config (common for all architectures)
|
||||
func baseEc2ImageConfig() *distro.ImageConfig {
|
||||
return &distro.ImageConfig{
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
Timezone: common.ToPtr("UTC"),
|
||||
TimeSynchronization: &osbuild.ChronyStageOptions{
|
||||
Servers: []osbuild.ChronyConfigServer{
|
||||
{
|
||||
Hostname: "169.254.169.123",
|
||||
Prefer: common.ToPtr(true),
|
||||
Iburst: common.ToPtr(true),
|
||||
Minpoll: common.ToPtr(4),
|
||||
Maxpoll: common.ToPtr(4),
|
||||
},
|
||||
},
|
||||
// empty string will remove any occurrences of the option from the configuration
|
||||
LeapsecTz: common.ToPtr(""),
|
||||
},
|
||||
Keyboard: &osbuild.KeymapStageOptions{
|
||||
Keymap: "us",
|
||||
X11Keymap: &osbuild.X11KeymapOptions{
|
||||
Layouts: []string{"us"},
|
||||
},
|
||||
},
|
||||
EnabledServices: []string{
|
||||
"sshd",
|
||||
"NetworkManager",
|
||||
"nm-cloud-setup.service",
|
||||
"nm-cloud-setup.timer",
|
||||
"cloud-init",
|
||||
"cloud-init-local",
|
||||
"cloud-config",
|
||||
"cloud-final",
|
||||
"reboot.target",
|
||||
"tuned",
|
||||
},
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
Sysconfig: []*osbuild.SysconfigStageOptions{
|
||||
{
|
||||
Kernel: &osbuild.SysconfigKernelOptions{
|
||||
UpdateDefault: true,
|
||||
DefaultKernel: "kernel",
|
||||
},
|
||||
Network: &osbuild.SysconfigNetworkOptions{
|
||||
Networking: true,
|
||||
NoZeroConf: true,
|
||||
},
|
||||
NetworkScripts: &osbuild.NetworkScriptsOptions{
|
||||
IfcfgFiles: map[string]osbuild.IfcfgFile{
|
||||
"eth0": {
|
||||
Device: "eth0",
|
||||
Bootproto: osbuild.IfcfgBootprotoDHCP,
|
||||
OnBoot: common.ToPtr(true),
|
||||
Type: osbuild.IfcfgTypeEthernet,
|
||||
UserCtl: common.ToPtr(true),
|
||||
PeerDNS: common.ToPtr(true),
|
||||
IPv6Init: common.ToPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
SystemdLogind: []*osbuild.SystemdLogindStageOptions{
|
||||
{
|
||||
Filename: "00-getty-fixes.conf",
|
||||
Config: osbuild.SystemdLogindConfigDropin{
|
||||
|
||||
Login: osbuild.SystemdLogindConfigLoginSection{
|
||||
NAutoVTs: common.ToPtr(0),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
CloudInit: []*osbuild.CloudInitStageOptions{
|
||||
{
|
||||
Filename: "00-rhel-default-user.cfg",
|
||||
Config: osbuild.CloudInitConfigFile{
|
||||
SystemInfo: &osbuild.CloudInitConfigSystemInfo{
|
||||
DefaultUser: &osbuild.CloudInitConfigDefaultUser{
|
||||
Name: "ec2-user",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Modprobe: []*osbuild.ModprobeStageOptions{
|
||||
{
|
||||
Filename: "blacklist-nouveau.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("nouveau"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Filename: "blacklist-amdgpu.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("amdgpu"),
|
||||
},
|
||||
},
|
||||
},
|
||||
// COMPOSER-1807
|
||||
DracutConf: []*osbuild.DracutConfStageOptions{
|
||||
{
|
||||
Filename: "sgdisk.conf",
|
||||
Config: osbuild.DracutConfigFile{
|
||||
Install: []string{"sgdisk"},
|
||||
},
|
||||
},
|
||||
},
|
||||
SystemdUnit: []*osbuild.SystemdUnitStageOptions{
|
||||
// RHBZ#1822863
|
||||
{
|
||||
Unit: "nm-cloud-setup.service",
|
||||
Dropin: "10-rh-enable-for-ec2.conf",
|
||||
Config: osbuild.SystemdServiceUnitDropin{
|
||||
Service: &osbuild.SystemdUnitServiceSection{
|
||||
Environment: "NM_CLOUD_SETUP_EC2=yes",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Authselect: &osbuild.AuthselectStageOptions{
|
||||
Profile: "sssd",
|
||||
},
|
||||
SshdConfig: &osbuild.SshdConfigStageOptions{
|
||||
Config: osbuild.SshdConfigConfig{
|
||||
PasswordAuthentication: common.ToPtr(false),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func defaultEc2ImageConfig(osVersion string, rhsm bool) *distro.ImageConfig {
|
||||
ic := baseEc2ImageConfig()
|
||||
if rhsm && common.VersionLessThan(osVersion, "9.1") {
|
||||
ic = appendRHSM(ic)
|
||||
// Disable RHSM redhat.repo management
|
||||
rhsmConf := ic.RHSMConfig[subscription.RHSMConfigNoSubscription]
|
||||
rhsmConf.SubMan.Rhsm = &osbuild.SubManConfigRHSMSection{ManageRepos: common.ToPtr(false)}
|
||||
ic.RHSMConfig[subscription.RHSMConfigNoSubscription] = rhsmConf
|
||||
}
|
||||
return ic
|
||||
}
|
||||
|
||||
// default AMI (EC2 BYOS) images config
|
||||
func defaultAMIImageConfig(osVersion string, rhsm bool) *distro.ImageConfig {
|
||||
ic := defaultEc2ImageConfig(osVersion, rhsm)
|
||||
if rhsm {
|
||||
// defaultAMIImageConfig() adds the rhsm options only for RHEL < 9.1
|
||||
// Add it unconditionally for AMI
|
||||
ic = appendRHSM(ic)
|
||||
}
|
||||
return ic
|
||||
}
|
||||
|
||||
func defaultEc2ImageConfigX86_64(osVersion string, rhsm bool) *distro.ImageConfig {
|
||||
ic := defaultEc2ImageConfig(osVersion, rhsm)
|
||||
return appendEC2DracutX86_64(ic)
|
||||
}
|
||||
|
||||
func defaultAMIImageConfigX86_64(osVersion string, rhsm bool) *distro.ImageConfig {
|
||||
ic := defaultAMIImageConfig(osVersion, rhsm).InheritFrom(defaultEc2ImageConfigX86_64(osVersion, rhsm))
|
||||
return appendEC2DracutX86_64(ic)
|
||||
}
|
||||
|
||||
// common ec2 image build package set
|
||||
func ec2BuildPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return distroBuildPackageSet(t).Append(
|
||||
rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"python3-pyyaml",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func ec2CommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"authselect-compat",
|
||||
"chrony",
|
||||
"cloud-init",
|
||||
"cloud-utils-growpart",
|
||||
"dhcp-client",
|
||||
"yum-utils",
|
||||
"dracut-config-generic",
|
||||
"gdisk",
|
||||
"grub2",
|
||||
"langpacks-en",
|
||||
"NetworkManager-cloud-setup",
|
||||
"redhat-release",
|
||||
"redhat-release-eula",
|
||||
"rsync",
|
||||
"tar",
|
||||
},
|
||||
Exclude: []string{
|
||||
"aic94xx-firmware",
|
||||
"alsa-firmware",
|
||||
"alsa-tools-firmware",
|
||||
"biosdevname",
|
||||
"iprutils",
|
||||
"ivtv-firmware",
|
||||
"libertas-sd8787-firmware",
|
||||
"plymouth",
|
||||
// RHBZ#2064087
|
||||
"dracut-config-rescue",
|
||||
// RHBZ#2075815
|
||||
"qemu-guest-agent",
|
||||
},
|
||||
}.Append(coreOsCommonPackageSet(t)).Append(distroSpecificPackageSet(t))
|
||||
}
|
||||
|
||||
// common rhel ec2 RHUI image package set
|
||||
func rhelEc2CommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := ec2CommonPackageSet(t)
|
||||
// Include "redhat-cloud-client-configuration" on 9.1+ (COMPOSER-1805)
|
||||
if !common.VersionLessThan(t.arch.distro.osVersion, "9.1") {
|
||||
ps.Include = append(ps.Include, "redhat-cloud-client-configuration")
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
// rhel-ec2 image package set
|
||||
func rhelEc2PackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ec2PackageSet := rhelEc2CommonPackageSet(t)
|
||||
ec2PackageSet = ec2PackageSet.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"rh-amazon-rhui-client",
|
||||
},
|
||||
Exclude: []string{
|
||||
"alsa-lib",
|
||||
},
|
||||
})
|
||||
return ec2PackageSet
|
||||
}
|
||||
|
||||
// rhel-ha-ec2 image package set
|
||||
func rhelEc2HaPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ec2HaPackageSet := rhelEc2CommonPackageSet(t)
|
||||
ec2HaPackageSet = ec2HaPackageSet.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"fence-agents-all",
|
||||
"pacemaker",
|
||||
"pcs",
|
||||
"rh-amazon-rhui-client-ha",
|
||||
},
|
||||
Exclude: []string{
|
||||
"alsa-lib",
|
||||
},
|
||||
})
|
||||
return ec2HaPackageSet
|
||||
}
|
||||
|
||||
// rhel-sap-ec2 image package set
|
||||
// Includes the common ec2 package set, the common SAP packages, and
|
||||
// the amazon rhui sap package
|
||||
func rhelEc2SapPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"rh-amazon-rhui-client-sap-bundle-e4s",
|
||||
},
|
||||
}.Append(rhelEc2CommonPackageSet(t)).Append(SapPackageSet(t))
|
||||
}
|
||||
|
||||
func mkEc2ImgTypeX86_64(osVersion string, rhsm bool) imageType {
|
||||
it := ec2ImgTypeX86_64
|
||||
ic := defaultEc2ImageConfigX86_64(osVersion, rhsm)
|
||||
it.defaultImageConfig = ic
|
||||
return it
|
||||
}
|
||||
|
||||
func mkAMIImgTypeX86_64(osVersion string, rhsm bool) imageType {
|
||||
it := amiImgTypeX86_64
|
||||
ic := defaultAMIImageConfigX86_64(osVersion, rhsm)
|
||||
it.defaultImageConfig = ic
|
||||
return it
|
||||
}
|
||||
|
||||
func mkEC2SapImgTypeX86_64(osVersion string, rhsm bool) imageType {
|
||||
it := ec2SapImgTypeX86_64
|
||||
it.defaultImageConfig = sapImageConfig(osVersion).InheritFrom(defaultEc2ImageConfigX86_64(osVersion, rhsm))
|
||||
return it
|
||||
}
|
||||
|
||||
func mkEc2HaImgTypeX86_64(osVersion string, rhsm bool) imageType {
|
||||
it := ec2HaImgTypeX86_64
|
||||
ic := defaultEc2ImageConfigX86_64(osVersion, rhsm)
|
||||
it.defaultImageConfig = ic
|
||||
return it
|
||||
}
|
||||
|
||||
func mkAMIImgTypeAarch64(osVersion string, rhsm bool) imageType {
|
||||
it := amiImgTypeAarch64
|
||||
ic := defaultAMIImageConfig(osVersion, rhsm)
|
||||
it.defaultImageConfig = ic
|
||||
return it
|
||||
}
|
||||
|
||||
func mkEC2ImgTypeAarch64(osVersion string, rhsm bool) imageType {
|
||||
it := ec2ImgTypeAarch64
|
||||
ic := defaultEc2ImageConfig(osVersion, rhsm)
|
||||
it.defaultImageConfig = ic
|
||||
return it
|
||||
}
|
||||
|
||||
// Add RHSM config options to ImageConfig.
|
||||
// Used for RHEL distros.
|
||||
func appendRHSM(ic *distro.ImageConfig) *distro.ImageConfig {
|
||||
rhsm := &distro.ImageConfig{
|
||||
RHSMConfig: map[subscription.RHSMStatus]*osbuild.RHSMStageOptions{
|
||||
subscription.RHSMConfigNoSubscription: {
|
||||
// RHBZ#1932802
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
// Don't disable RHSM redhat.repo management on the AMI
|
||||
// image, which is BYOS and does not use RHUI for content.
|
||||
// Otherwise subscribing the system manually after booting
|
||||
// it would result in empty redhat.repo. Without RHUI, such
|
||||
// system would have no way to get Red Hat content, but
|
||||
// enable the repo management manually, which would be very
|
||||
// confusing.
|
||||
},
|
||||
},
|
||||
subscription.RHSMConfigWithSubscription: {
|
||||
// RHBZ#1932802
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
// do not disable the redhat.repo management if the user
|
||||
// explicitly request the system to be subscribed
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return rhsm.InheritFrom(ic)
|
||||
}
|
||||
|
||||
func appendEC2DracutX86_64(ic *distro.ImageConfig) *distro.ImageConfig {
|
||||
ic.DracutConf = append(ic.DracutConf,
|
||||
&osbuild.DracutConfStageOptions{
|
||||
Filename: "ec2.conf",
|
||||
Config: osbuild.DracutConfigFile{
|
||||
AddDrivers: []string{
|
||||
"nvme",
|
||||
"xen-blkfront",
|
||||
},
|
||||
},
|
||||
})
|
||||
return ic
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
package rhel9
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
)
|
||||
|
||||
type architecture struct {
|
||||
distro *distribution
|
||||
name string
|
||||
imageTypes map[string]distro.ImageType
|
||||
imageTypeAliases map[string]string
|
||||
}
|
||||
|
||||
func (a *architecture) Name() string {
|
||||
return a.name
|
||||
}
|
||||
|
||||
func (a *architecture) ListImageTypes() []string {
|
||||
itNames := make([]string, 0, len(a.imageTypes))
|
||||
for name := range a.imageTypes {
|
||||
itNames = append(itNames, name)
|
||||
}
|
||||
sort.Strings(itNames)
|
||||
return itNames
|
||||
}
|
||||
|
||||
func (a *architecture) GetImageType(name string) (distro.ImageType, error) {
|
||||
t, exists := a.imageTypes[name]
|
||||
if !exists {
|
||||
aliasForName, exists := a.imageTypeAliases[name]
|
||||
if !exists {
|
||||
return nil, errors.New("invalid image type: " + name)
|
||||
}
|
||||
t, exists = a.imageTypes[aliasForName]
|
||||
if !exists {
|
||||
panic(fmt.Sprintf("image type '%s' is an alias to a non-existing image type '%s'", name, aliasForName))
|
||||
}
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (a *architecture) addImageTypes(platform platform.Platform, imageTypes ...imageType) {
|
||||
if a.imageTypes == nil {
|
||||
a.imageTypes = map[string]distro.ImageType{}
|
||||
}
|
||||
for idx := range imageTypes {
|
||||
it := imageTypes[idx]
|
||||
it.arch = a
|
||||
it.platform = platform
|
||||
a.imageTypes[it.name] = &it
|
||||
for _, alias := range it.nameAliases {
|
||||
if a.imageTypeAliases == nil {
|
||||
a.imageTypeAliases = map[string]string{}
|
||||
}
|
||||
if existingAliasFor, exists := a.imageTypeAliases[alias]; exists {
|
||||
panic(fmt.Sprintf("image type alias '%s' for '%s' is already defined for another image type '%s'", alias, it.name, existingAliasFor))
|
||||
}
|
||||
a.imageTypeAliases[alias] = it.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *architecture) Distro() distro.Distro {
|
||||
return a.distro
|
||||
}
|
||||
|
|
@ -1,596 +0,0 @@
|
|||
package rhel9
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/disk"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/subscription"
|
||||
)
|
||||
|
||||
var (
|
||||
// Azure non-RHEL image type
|
||||
azureImgType = imageType{
|
||||
name: "vhd",
|
||||
filename: "disk.vhd",
|
||||
mimeType: "application/x-vhd",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: azurePackageSet,
|
||||
},
|
||||
defaultImageConfig: defaultAzureImageConfig,
|
||||
kernelOptions: defaultAzureKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 4 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "vpc"},
|
||||
exports: []string{"vpc"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
|
||||
// Azure BYOS image type
|
||||
azureByosImgType = imageType{
|
||||
name: "vhd",
|
||||
filename: "disk.vhd",
|
||||
mimeType: "application/x-vhd",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: azurePackageSet,
|
||||
},
|
||||
defaultImageConfig: defaultAzureByosImageConfig.InheritFrom(defaultAzureImageConfig),
|
||||
kernelOptions: defaultAzureKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 4 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "vpc"},
|
||||
exports: []string{"vpc"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
|
||||
// Azure RHUI image type
|
||||
azureRhuiImgType = imageType{
|
||||
name: "azure-rhui",
|
||||
filename: "disk.vhd.xz",
|
||||
mimeType: "application/xz",
|
||||
compression: "xz",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: azureRhuiPackageSet,
|
||||
},
|
||||
defaultImageConfig: defaultAzureRhuiImageConfig.InheritFrom(defaultAzureImageConfig),
|
||||
kernelOptions: defaultAzureKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 64 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "vpc", "xz"},
|
||||
exports: []string{"xz"},
|
||||
basePartitionTables: azureRhuiBasePartitionTables,
|
||||
}
|
||||
)
|
||||
|
||||
// PACKAGE SETS
|
||||
|
||||
// Common Azure image package set
|
||||
func azureCommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"@Server",
|
||||
"bzip2",
|
||||
"cloud-init",
|
||||
"cloud-utils-growpart",
|
||||
"dracut-config-generic",
|
||||
"efibootmgr",
|
||||
"gdisk",
|
||||
"hyperv-daemons",
|
||||
"kernel-core",
|
||||
"kernel-modules",
|
||||
"kernel",
|
||||
"langpacks-en",
|
||||
"lvm2",
|
||||
"NetworkManager",
|
||||
"NetworkManager-cloud-setup",
|
||||
"nvme-cli",
|
||||
"patch",
|
||||
"rng-tools",
|
||||
"selinux-policy-targeted",
|
||||
"uuid",
|
||||
"WALinuxAgent",
|
||||
"yum-utils",
|
||||
},
|
||||
Exclude: []string{
|
||||
"aic94xx-firmware",
|
||||
"alsa-firmware",
|
||||
"alsa-lib",
|
||||
"alsa-sof-firmware",
|
||||
"alsa-tools-firmware",
|
||||
"biosdevname",
|
||||
"bolt",
|
||||
"buildah",
|
||||
"cockpit-podman",
|
||||
"containernetworking-plugins",
|
||||
"dnf-plugin-spacewalk",
|
||||
"dracut-config-rescue",
|
||||
"glibc-all-langpacks",
|
||||
"iprutils",
|
||||
"ivtv-firmware",
|
||||
"iwl100-firmware",
|
||||
"iwl1000-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl3945-firmware",
|
||||
"iwl4965-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6000-firmware",
|
||||
"iwl6000g2a-firmware",
|
||||
"iwl6000g2b-firmware",
|
||||
"iwl6050-firmware",
|
||||
"iwl7260-firmware",
|
||||
"libertas-sd8686-firmware",
|
||||
"libertas-sd8787-firmware",
|
||||
"libertas-usb8388-firmware",
|
||||
"NetworkManager-config-server",
|
||||
"plymouth",
|
||||
"podman",
|
||||
"python3-dnf-plugin-spacewalk",
|
||||
"python3-hwdata",
|
||||
"python3-rhnlib",
|
||||
"rhn-check",
|
||||
"rhn-client-tools",
|
||||
"rhn-setup",
|
||||
"rhnlib",
|
||||
"rhnsd",
|
||||
"usb_modeswitch",
|
||||
},
|
||||
}.Append(distroSpecificPackageSet(t))
|
||||
|
||||
if t.arch.distro.isRHEL() {
|
||||
ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"rhc",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
// Azure BYOS image package set
|
||||
func azurePackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return azureCommonPackageSet(t)
|
||||
}
|
||||
|
||||
// Azure RHUI image package set
|
||||
func azureRhuiPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"rhui-azure-rhel9",
|
||||
},
|
||||
}.Append(azureCommonPackageSet(t))
|
||||
}
|
||||
|
||||
// PARTITION TABLES
|
||||
|
||||
var azureRhuiBasePartitionTables = distro.BasePartitionTableMap{
|
||||
platform.ARCH_X86_64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Size: 64 * common.GibiByte,
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 500 * common.MebiByte,
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 500 * common.MebiByte,
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.FilesystemDataUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.MebiByte,
|
||||
Bootable: true,
|
||||
Type: disk.BIOSBootPartitionGUID,
|
||||
UUID: disk.BIOSBootPartitionUUID,
|
||||
},
|
||||
{
|
||||
Type: disk.LVMPartitionGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.LVMVolumeGroup{
|
||||
Name: "rootvg",
|
||||
Description: "built with lvm2 and osbuild",
|
||||
LogicalVolumes: []disk.LVMLogicalVolume{
|
||||
{
|
||||
Size: 1 * common.GibiByte,
|
||||
Name: "homelv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "home",
|
||||
Mountpoint: "/home",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte,
|
||||
Name: "rootlv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte,
|
||||
Name: "tmplv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "tmp",
|
||||
Mountpoint: "/tmp",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 10 * common.GibiByte,
|
||||
Name: "usrlv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "usr",
|
||||
Mountpoint: "/usr",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 10 * common.GibiByte,
|
||||
Name: "varlv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "var",
|
||||
Mountpoint: "/var",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
platform.ARCH_AARCH64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Size: 64 * common.GibiByte,
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 500 * common.MebiByte,
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 500 * common.MebiByte,
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.FilesystemDataUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: disk.LVMPartitionGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.LVMVolumeGroup{
|
||||
Name: "rootvg",
|
||||
Description: "built with lvm2 and osbuild",
|
||||
LogicalVolumes: []disk.LVMLogicalVolume{
|
||||
{
|
||||
Size: 1 * common.GibiByte,
|
||||
Name: "homelv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "home",
|
||||
Mountpoint: "/home",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte,
|
||||
Name: "rootlv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte,
|
||||
Name: "tmplv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "tmp",
|
||||
Mountpoint: "/tmp",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 10 * common.GibiByte,
|
||||
Name: "usrlv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "usr",
|
||||
Mountpoint: "/usr",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 10 * common.GibiByte,
|
||||
Name: "varlv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "var",
|
||||
Mountpoint: "/var",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var defaultAzureKernelOptions = "ro console=tty1 console=ttyS0 earlyprintk=ttyS0 rootdelay=300"
|
||||
|
||||
var defaultAzureImageConfig = &distro.ImageConfig{
|
||||
Timezone: common.ToPtr("Etc/UTC"),
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
Keyboard: &osbuild.KeymapStageOptions{
|
||||
Keymap: "us",
|
||||
X11Keymap: &osbuild.X11KeymapOptions{
|
||||
Layouts: []string{"us"},
|
||||
},
|
||||
},
|
||||
Sysconfig: []*osbuild.SysconfigStageOptions{
|
||||
{
|
||||
Kernel: &osbuild.SysconfigKernelOptions{
|
||||
UpdateDefault: true,
|
||||
DefaultKernel: "kernel-core",
|
||||
},
|
||||
Network: &osbuild.SysconfigNetworkOptions{
|
||||
Networking: true,
|
||||
NoZeroConf: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
EnabledServices: []string{
|
||||
"firewalld",
|
||||
"nm-cloud-setup.service",
|
||||
"nm-cloud-setup.timer",
|
||||
"sshd",
|
||||
"waagent",
|
||||
},
|
||||
SshdConfig: &osbuild.SshdConfigStageOptions{
|
||||
Config: osbuild.SshdConfigConfig{
|
||||
ClientAliveInterval: common.ToPtr(180),
|
||||
},
|
||||
},
|
||||
Modprobe: []*osbuild.ModprobeStageOptions{
|
||||
{
|
||||
Filename: "blacklist-amdgpu.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("amdgpu"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Filename: "blacklist-floppy.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("floppy"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Filename: "blacklist-nouveau.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("nouveau"),
|
||||
osbuild.NewModprobeConfigCmdBlacklist("lbm-nouveau"),
|
||||
},
|
||||
},
|
||||
},
|
||||
CloudInit: []*osbuild.CloudInitStageOptions{
|
||||
{
|
||||
Filename: "10-azure-kvp.cfg",
|
||||
Config: osbuild.CloudInitConfigFile{
|
||||
Reporting: &osbuild.CloudInitConfigReporting{
|
||||
Logging: &osbuild.CloudInitConfigReportingHandlers{
|
||||
Type: "log",
|
||||
},
|
||||
Telemetry: &osbuild.CloudInitConfigReportingHandlers{
|
||||
Type: "hyperv",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Filename: "91-azure_datasource.cfg",
|
||||
Config: osbuild.CloudInitConfigFile{
|
||||
Datasource: &osbuild.CloudInitConfigDatasource{
|
||||
Azure: &osbuild.CloudInitConfigDatasourceAzure{
|
||||
ApplyNetworkConfig: false,
|
||||
},
|
||||
},
|
||||
DatasourceList: []string{
|
||||
"Azure",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
PwQuality: &osbuild.PwqualityConfStageOptions{
|
||||
Config: osbuild.PwqualityConfConfig{
|
||||
Minlen: common.ToPtr(6),
|
||||
Minclass: common.ToPtr(3),
|
||||
Dcredit: common.ToPtr(0),
|
||||
Ucredit: common.ToPtr(0),
|
||||
Lcredit: common.ToPtr(0),
|
||||
Ocredit: common.ToPtr(0),
|
||||
},
|
||||
},
|
||||
WAAgentConfig: &osbuild.WAAgentConfStageOptions{
|
||||
Config: osbuild.WAAgentConfig{
|
||||
RDFormat: common.ToPtr(false),
|
||||
RDEnableSwap: common.ToPtr(false),
|
||||
},
|
||||
},
|
||||
Grub2Config: &osbuild.GRUB2Config{
|
||||
TerminalInput: []string{"serial", "console"},
|
||||
TerminalOutput: []string{"serial", "console"},
|
||||
Serial: "serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1",
|
||||
Timeout: 10,
|
||||
},
|
||||
UdevRules: &osbuild.UdevRulesStageOptions{
|
||||
Filename: "/etc/udev/rules.d/68-azure-sriov-nm-unmanaged.rules",
|
||||
Rules: osbuild.UdevRules{
|
||||
osbuild.UdevRuleComment{
|
||||
Comment: []string{
|
||||
"Accelerated Networking on Azure exposes a new SRIOV interface to the VM.",
|
||||
"This interface is transparently bonded to the synthetic interface,",
|
||||
"so NetworkManager should just ignore any SRIOV interfaces.",
|
||||
},
|
||||
},
|
||||
osbuild.NewUdevRule(
|
||||
[]osbuild.UdevKV{
|
||||
{K: "SUBSYSTEM", O: "==", V: "net"},
|
||||
{K: "DRIVERS", O: "==", V: "hv_pci"},
|
||||
{K: "ACTION", O: "==", V: "add"},
|
||||
{K: "ENV", A: "NM_UNMANAGED", O: "=", V: "1"},
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
SystemdUnit: []*osbuild.SystemdUnitStageOptions{
|
||||
{
|
||||
Unit: "nm-cloud-setup.service",
|
||||
Dropin: "10-rh-enable-for-azure.conf",
|
||||
Config: osbuild.SystemdServiceUnitDropin{
|
||||
Service: &osbuild.SystemdUnitServiceSection{
|
||||
Environment: "NM_CLOUD_SETUP_AZURE=yes",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
}
|
||||
|
||||
// Diff of the default Image Config compare to the `defaultAzureImageConfig`
|
||||
var defaultAzureByosImageConfig = &distro.ImageConfig{
|
||||
GPGKeyFiles: []string{
|
||||
"/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release",
|
||||
},
|
||||
RHSMConfig: map[subscription.RHSMStatus]*osbuild.RHSMStageOptions{
|
||||
subscription.RHSMConfigNoSubscription: {
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
// Don't disable RHSM redhat.repo management on the GCE
|
||||
// image, which is BYOS and does not use RHUI for content.
|
||||
// Otherwise subscribing the system manually after booting
|
||||
// it would result in empty redhat.repo. Without RHUI, such
|
||||
// system would have no way to get Red Hat content, but
|
||||
// enable the repo management manually, which would be very
|
||||
// confusing.
|
||||
},
|
||||
},
|
||||
subscription.RHSMConfigWithSubscription: {
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
// do not disable the redhat.repo management if the user
|
||||
// explicitly request the system to be subscribed
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Diff of the default Image Config compare to the `defaultAzureImageConfig`
|
||||
var defaultAzureRhuiImageConfig = &distro.ImageConfig{
|
||||
GPGKeyFiles: []string{
|
||||
"/etc/pki/rpm-gpg/RPM-GPG-KEY-microsoft-azure-release",
|
||||
"/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release",
|
||||
},
|
||||
RHSMConfig: map[subscription.RHSMStatus]*osbuild.RHSMStageOptions{
|
||||
subscription.RHSMConfigNoSubscription: {
|
||||
DnfPlugins: &osbuild.RHSMStageOptionsDnfPlugins{
|
||||
SubscriptionManager: &osbuild.RHSMStageOptionsDnfPlugin{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
Rhsm: &osbuild.SubManConfigRHSMSection{
|
||||
ManageRepos: common.ToPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
subscription.RHSMConfigWithSubscription: {
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
// do not disable the redhat.repo management if the user
|
||||
// explicitly request the system to be subscribed
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -1,317 +0,0 @@
|
|||
package rhel9
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
var (
|
||||
tarImgType = imageType{
|
||||
name: "tar",
|
||||
filename: "root.tar.xz",
|
||||
mimeType: "application/x-tar",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: func(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{"policycoreutils", "selinux-policy-targeted"},
|
||||
Exclude: []string{"rng-tools"},
|
||||
}
|
||||
},
|
||||
},
|
||||
image: tarImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "archive"},
|
||||
exports: []string{"archive"},
|
||||
}
|
||||
|
||||
imageInstaller = imageType{
|
||||
name: "image-installer",
|
||||
filename: "installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: bareMetalPackageSet,
|
||||
installerPkgsKey: anacondaPackageSet,
|
||||
},
|
||||
rpmOstree: false,
|
||||
bootISO: true,
|
||||
bootable: true,
|
||||
image: imageInstallerImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"anaconda-tree", "rootfs-image", "efiboot-tree", "os", "bootiso-tree", "bootiso"},
|
||||
exports: []string{"bootiso"},
|
||||
}
|
||||
)
|
||||
|
||||
func bareMetalPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"authselect-compat",
|
||||
"chrony",
|
||||
"cockpit-system",
|
||||
"cockpit-ws",
|
||||
"dhcp-client",
|
||||
"dnf-utils",
|
||||
"dosfstools",
|
||||
"firewalld",
|
||||
"iwl1000-firmware",
|
||||
"iwl100-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6000g2a-firmware",
|
||||
"iwl6000g2b-firmware",
|
||||
"iwl6050-firmware",
|
||||
"iwl7260-firmware",
|
||||
"lvm2",
|
||||
"net-tools",
|
||||
"nfs-utils",
|
||||
"oddjob",
|
||||
"oddjob-mkhomedir",
|
||||
"policycoreutils",
|
||||
"psmisc",
|
||||
"python3-jsonschema",
|
||||
"qemu-guest-agent",
|
||||
"redhat-release",
|
||||
"redhat-release-eula",
|
||||
"rsync",
|
||||
"tar",
|
||||
"tcpdump",
|
||||
},
|
||||
}.Append(coreOsCommonPackageSet(t)).Append(distroBuildPackageSet(t))
|
||||
|
||||
// Ensure to not pull in subscription-manager on non-RHEL distro
|
||||
if t.arch.distro.isRHEL() {
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"subscription-manager-cockpit",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
func installerPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"anaconda-dracut",
|
||||
"curl",
|
||||
"dracut-config-generic",
|
||||
"dracut-network",
|
||||
"hostname",
|
||||
"iwl100-firmware",
|
||||
"iwl1000-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6050-firmware",
|
||||
"iwl7260-firmware",
|
||||
"kernel",
|
||||
"less",
|
||||
"nfs-utils",
|
||||
"openssh-clients",
|
||||
"ostree",
|
||||
"plymouth",
|
||||
"prefixdevname",
|
||||
"rng-tools",
|
||||
"rpcbind",
|
||||
"selinux-policy-targeted",
|
||||
"systemd",
|
||||
"tar",
|
||||
"xfsprogs",
|
||||
"xz",
|
||||
},
|
||||
}
|
||||
|
||||
switch t.arch.Name() {
|
||||
case platform.ARCH_X86_64.String():
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"biosdevname",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
func anacondaPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
|
||||
// common installer packages
|
||||
ps := installerPackageSet(t)
|
||||
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"aajohan-comfortaa-fonts",
|
||||
"abattis-cantarell-fonts",
|
||||
"alsa-firmware",
|
||||
"alsa-tools-firmware",
|
||||
"anaconda",
|
||||
"anaconda-dracut",
|
||||
"anaconda-install-env-deps",
|
||||
"anaconda-widgets",
|
||||
"audit",
|
||||
"bind-utils",
|
||||
"bitmap-fangsongti-fonts",
|
||||
"bzip2",
|
||||
"cryptsetup",
|
||||
"curl",
|
||||
"dbus-x11",
|
||||
"dejavu-sans-fonts",
|
||||
"dejavu-sans-mono-fonts",
|
||||
"device-mapper-persistent-data",
|
||||
"dmidecode",
|
||||
"dnf",
|
||||
"dracut-config-generic",
|
||||
"dracut-network",
|
||||
"efibootmgr",
|
||||
"ethtool",
|
||||
"fcoe-utils",
|
||||
"ftp",
|
||||
"gdb-gdbserver",
|
||||
"gdisk",
|
||||
"glibc-all-langpacks",
|
||||
"gnome-kiosk",
|
||||
"google-noto-sans-cjk-ttc-fonts",
|
||||
"grub2-tools",
|
||||
"grub2-tools-extra",
|
||||
"grub2-tools-minimal",
|
||||
"grubby",
|
||||
"gsettings-desktop-schemas",
|
||||
"hdparm",
|
||||
"hexedit",
|
||||
"hostname",
|
||||
"initscripts",
|
||||
"ipmitool",
|
||||
"iwl1000-firmware",
|
||||
"iwl100-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6000g2a-firmware",
|
||||
"iwl6000g2b-firmware",
|
||||
"iwl6050-firmware",
|
||||
"iwl7260-firmware",
|
||||
"jomolhari-fonts",
|
||||
"kacst-farsi-fonts",
|
||||
"kacst-qurn-fonts",
|
||||
"kbd",
|
||||
"kbd-misc",
|
||||
"kdump-anaconda-addon",
|
||||
"kernel",
|
||||
"khmeros-base-fonts",
|
||||
"less",
|
||||
"libblockdev-lvm-dbus",
|
||||
"libibverbs",
|
||||
"libreport-plugin-bugzilla",
|
||||
"libreport-plugin-reportuploader",
|
||||
"librsvg2",
|
||||
"linux-firmware",
|
||||
"lklug-fonts",
|
||||
"lldpad",
|
||||
"lohit-assamese-fonts",
|
||||
"lohit-bengali-fonts",
|
||||
"lohit-devanagari-fonts",
|
||||
"lohit-gujarati-fonts",
|
||||
"lohit-gurmukhi-fonts",
|
||||
"lohit-kannada-fonts",
|
||||
"lohit-odia-fonts",
|
||||
"lohit-tamil-fonts",
|
||||
"lohit-telugu-fonts",
|
||||
"lsof",
|
||||
"madan-fonts",
|
||||
"mtr",
|
||||
"mt-st",
|
||||
"net-tools",
|
||||
"nfs-utils",
|
||||
"nmap-ncat",
|
||||
"nm-connection-editor",
|
||||
"nss-tools",
|
||||
"openssh-clients",
|
||||
"openssh-server",
|
||||
"oscap-anaconda-addon",
|
||||
"ostree",
|
||||
"pciutils",
|
||||
"perl-interpreter",
|
||||
"pigz",
|
||||
"plymouth",
|
||||
"prefixdevname",
|
||||
"python3-pyatspi",
|
||||
"rdma-core",
|
||||
"redhat-release-eula",
|
||||
"rng-tools",
|
||||
"rpcbind",
|
||||
"rpm-ostree",
|
||||
"rsync",
|
||||
"rsyslog",
|
||||
"selinux-policy-targeted",
|
||||
"sg3_utils",
|
||||
"sil-abyssinica-fonts",
|
||||
"sil-padauk-fonts",
|
||||
"sil-scheherazade-fonts",
|
||||
"smartmontools",
|
||||
"smc-meera-fonts",
|
||||
"spice-vdagent",
|
||||
"strace",
|
||||
"systemd",
|
||||
"tar",
|
||||
"thai-scalable-waree-fonts",
|
||||
"tigervnc-server-minimal",
|
||||
"tigervnc-server-module",
|
||||
"udisks2",
|
||||
"udisks2-iscsi",
|
||||
"usbutils",
|
||||
"vim-minimal",
|
||||
"volume_key",
|
||||
"wget",
|
||||
"xfsdump",
|
||||
"xfsprogs",
|
||||
"xorg-x11-drivers",
|
||||
"xorg-x11-fonts-misc",
|
||||
"xorg-x11-server-utils",
|
||||
"xorg-x11-server-Xorg",
|
||||
"xorg-x11-xauth",
|
||||
"xz",
|
||||
},
|
||||
})
|
||||
|
||||
ps = ps.Append(anacondaBootPackageSet(t))
|
||||
|
||||
switch t.arch.Name() {
|
||||
case platform.ARCH_X86_64.String():
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"biosdevname",
|
||||
"dmidecode",
|
||||
"grub2-tools-efi",
|
||||
"memtest86+",
|
||||
},
|
||||
})
|
||||
|
||||
case platform.ARCH_AARCH64.String():
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"dmidecode",
|
||||
},
|
||||
})
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported arch: %s", t.arch.Name()))
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
|
@ -1,463 +0,0 @@
|
|||
package rhel9
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/oscap"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/runner"
|
||||
)
|
||||
|
||||
var (
|
||||
// rhel9 & cs9 share the same list
|
||||
// of allowed profiles so a single
|
||||
// allow list can be used
|
||||
oscapProfileAllowList = []oscap.Profile{
|
||||
oscap.AnssiBp28Enhanced,
|
||||
oscap.AnssiBp28High,
|
||||
oscap.AnssiBp28Intermediary,
|
||||
oscap.AnssiBp28Minimal,
|
||||
oscap.Cis,
|
||||
oscap.CisServerL1,
|
||||
oscap.CisWorkstationL1,
|
||||
oscap.CisWorkstationL2,
|
||||
oscap.Cui,
|
||||
oscap.E8,
|
||||
oscap.Hippa,
|
||||
oscap.IsmO,
|
||||
oscap.Ospp,
|
||||
oscap.PciDss,
|
||||
oscap.Stig,
|
||||
oscap.StigGui,
|
||||
}
|
||||
)
|
||||
|
||||
type distribution struct {
|
||||
name string
|
||||
product string
|
||||
osVersion string
|
||||
releaseVersion string
|
||||
modulePlatformID string
|
||||
vendor string
|
||||
ostreeRefTmpl string
|
||||
isolabelTmpl string
|
||||
runner runner.Runner
|
||||
arches map[string]distro.Arch
|
||||
defaultImageConfig *distro.ImageConfig
|
||||
}
|
||||
|
||||
// CentOS- and RHEL-based OS image configuration defaults
|
||||
var defaultDistroImageConfig = &distro.ImageConfig{
|
||||
Timezone: common.ToPtr("America/New_York"),
|
||||
Locale: common.ToPtr("C.UTF-8"),
|
||||
Sysconfig: []*osbuild.SysconfigStageOptions{
|
||||
{
|
||||
Kernel: &osbuild.SysconfigKernelOptions{
|
||||
UpdateDefault: true,
|
||||
DefaultKernel: "kernel",
|
||||
},
|
||||
Network: &osbuild.SysconfigNetworkOptions{
|
||||
Networking: true,
|
||||
NoZeroConf: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func (d *distribution) Name() string {
|
||||
return d.name
|
||||
}
|
||||
|
||||
func (d *distribution) Releasever() string {
|
||||
return d.releaseVersion
|
||||
}
|
||||
|
||||
func (d *distribution) ModulePlatformID() string {
|
||||
return d.modulePlatformID
|
||||
}
|
||||
|
||||
func (d *distribution) OSTreeRef() string {
|
||||
return d.ostreeRefTmpl
|
||||
}
|
||||
|
||||
func (d *distribution) ListArches() []string {
|
||||
archNames := make([]string, 0, len(d.arches))
|
||||
for name := range d.arches {
|
||||
archNames = append(archNames, name)
|
||||
}
|
||||
sort.Strings(archNames)
|
||||
return archNames
|
||||
}
|
||||
|
||||
func (d *distribution) GetArch(name string) (distro.Arch, error) {
|
||||
arch, exists := d.arches[name]
|
||||
if !exists {
|
||||
return nil, errors.New("invalid architecture: " + name)
|
||||
}
|
||||
return arch, nil
|
||||
}
|
||||
|
||||
func (d *distribution) addArches(arches ...architecture) {
|
||||
if d.arches == nil {
|
||||
d.arches = map[string]distro.Arch{}
|
||||
}
|
||||
|
||||
// Do not make copies of architectures, as opposed to image types,
|
||||
// because architecture definitions are not used by more than a single
|
||||
// distro definition.
|
||||
for idx := range arches {
|
||||
d.arches[arches[idx].name] = &arches[idx]
|
||||
}
|
||||
}
|
||||
|
||||
func (d *distribution) isRHEL() bool {
|
||||
return strings.HasPrefix(d.name, "rhel")
|
||||
}
|
||||
|
||||
func (d *distribution) getDefaultImageConfig() *distro.ImageConfig {
|
||||
return d.defaultImageConfig
|
||||
}
|
||||
|
||||
func New() distro.Distro {
|
||||
// default minor: create default minor version (current GA) and rename it
|
||||
d := newDistro("rhel", 1)
|
||||
d.name = "rhel-9"
|
||||
return d
|
||||
}
|
||||
|
||||
func NewCentOS9() distro.Distro {
|
||||
return newDistro("centos", 0)
|
||||
}
|
||||
|
||||
func NewRHEL90() distro.Distro {
|
||||
return newDistro("rhel", 0)
|
||||
}
|
||||
|
||||
func NewRHEL91() distro.Distro {
|
||||
return newDistro("rhel", 1)
|
||||
}
|
||||
|
||||
func NewRHEL92() distro.Distro {
|
||||
return newDistro("rhel", 2)
|
||||
}
|
||||
|
||||
func NewRHEL93() distro.Distro {
|
||||
return newDistro("rhel", 3)
|
||||
}
|
||||
|
||||
func newDistro(name string, minor int) *distribution {
|
||||
var rd distribution
|
||||
switch name {
|
||||
case "rhel":
|
||||
rd = distribution{
|
||||
name: fmt.Sprintf("rhel-9%d", minor),
|
||||
product: "Red Hat Enterprise Linux",
|
||||
osVersion: fmt.Sprintf("9.%d", minor),
|
||||
releaseVersion: "9",
|
||||
modulePlatformID: "platform:el9",
|
||||
vendor: "redhat",
|
||||
ostreeRefTmpl: "rhel/9/%s/edge",
|
||||
isolabelTmpl: fmt.Sprintf("RHEL-9-%d-0-BaseOS-%%s", minor),
|
||||
runner: &runner.RHEL{Major: uint64(9), Minor: uint64(minor)},
|
||||
defaultImageConfig: defaultDistroImageConfig,
|
||||
}
|
||||
case "centos":
|
||||
rd = distribution{
|
||||
name: "centos-9",
|
||||
product: "CentOS Stream",
|
||||
osVersion: "9-stream",
|
||||
releaseVersion: "9",
|
||||
modulePlatformID: "platform:el9",
|
||||
vendor: "centos",
|
||||
ostreeRefTmpl: "centos/9/%s/edge",
|
||||
isolabelTmpl: "CentOS-Stream-9-BaseOS-%s",
|
||||
runner: &runner.CentOS{Version: uint64(9)},
|
||||
defaultImageConfig: defaultDistroImageConfig,
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown distro name: %s", name))
|
||||
}
|
||||
|
||||
// Architecture definitions
|
||||
x86_64 := architecture{
|
||||
name: platform.ARCH_X86_64.String(),
|
||||
distro: &rd,
|
||||
}
|
||||
|
||||
aarch64 := architecture{
|
||||
name: platform.ARCH_AARCH64.String(),
|
||||
distro: &rd,
|
||||
}
|
||||
|
||||
ppc64le := architecture{
|
||||
distro: &rd,
|
||||
name: platform.ARCH_PPC64LE.String(),
|
||||
}
|
||||
|
||||
s390x := architecture{
|
||||
distro: &rd,
|
||||
name: platform.ARCH_S390X.String(),
|
||||
}
|
||||
|
||||
qcow2ImgType := mkQcow2ImgType(rd)
|
||||
ociImgType := qcow2ImgType
|
||||
ociImgType.name = "oci"
|
||||
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_QCOW2,
|
||||
QCOW2Compat: "1.1",
|
||||
},
|
||||
},
|
||||
qcow2ImgType,
|
||||
ociImgType,
|
||||
)
|
||||
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_QCOW2,
|
||||
},
|
||||
},
|
||||
openstackImgType,
|
||||
)
|
||||
|
||||
azureX64Platform := &platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_VHD,
|
||||
},
|
||||
}
|
||||
|
||||
azureAarch64Platform := &platform.Aarch64{
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_VHD,
|
||||
},
|
||||
}
|
||||
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_VMDK,
|
||||
},
|
||||
},
|
||||
vmdkImgType,
|
||||
)
|
||||
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_OVA,
|
||||
},
|
||||
},
|
||||
ovaImgType,
|
||||
)
|
||||
|
||||
ec2X86Platform := &platform.X86{
|
||||
BIOS: true,
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_RAW,
|
||||
},
|
||||
}
|
||||
x86_64.addImageTypes(
|
||||
ec2X86Platform,
|
||||
mkAMIImgTypeX86_64(rd.osVersion, rd.isRHEL()),
|
||||
)
|
||||
|
||||
gceX86Platform := &platform.X86{
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_GCE,
|
||||
},
|
||||
}
|
||||
x86_64.addImageTypes(
|
||||
gceX86Platform,
|
||||
mkGCEImageType(rd.isRHEL()),
|
||||
)
|
||||
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BasePlatform: platform.BasePlatform{
|
||||
FirmwarePackages: []string{
|
||||
"microcode_ctl", // ??
|
||||
"iwl1000-firmware",
|
||||
"iwl100-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6050-firmware",
|
||||
},
|
||||
},
|
||||
BIOS: true,
|
||||
UEFIVendor: rd.vendor,
|
||||
},
|
||||
edgeOCIImgType,
|
||||
edgeCommitImgType,
|
||||
edgeInstallerImgType,
|
||||
edgeRawImgType,
|
||||
imageInstaller,
|
||||
edgeAMIImgType,
|
||||
)
|
||||
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_RAW,
|
||||
},
|
||||
BIOS: false,
|
||||
UEFIVendor: rd.vendor,
|
||||
},
|
||||
edgeSimplifiedInstallerImgType,
|
||||
)
|
||||
|
||||
x86_64.addImageTypes(
|
||||
&platform.X86{},
|
||||
tarImgType,
|
||||
)
|
||||
|
||||
aarch64.addImageTypes(
|
||||
&platform.Aarch64{
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_QCOW2,
|
||||
},
|
||||
},
|
||||
openstackImgType,
|
||||
)
|
||||
|
||||
aarch64.addImageTypes(
|
||||
&platform.Aarch64{},
|
||||
tarImgType,
|
||||
)
|
||||
|
||||
aarch64.addImageTypes(
|
||||
&platform.Aarch64{
|
||||
BasePlatform: platform.BasePlatform{},
|
||||
UEFIVendor: rd.vendor,
|
||||
},
|
||||
edgeCommitImgType,
|
||||
edgeOCIImgType,
|
||||
edgeInstallerImgType,
|
||||
edgeSimplifiedInstallerImgType,
|
||||
imageInstaller,
|
||||
edgeAMIImgType,
|
||||
)
|
||||
aarch64.addImageTypes(
|
||||
&platform.Aarch64{
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_RAW,
|
||||
},
|
||||
UEFIVendor: rd.vendor,
|
||||
},
|
||||
edgeRawImgType,
|
||||
)
|
||||
|
||||
aarch64.addImageTypes(
|
||||
&platform.Aarch64{
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_QCOW2,
|
||||
QCOW2Compat: "1.1",
|
||||
},
|
||||
},
|
||||
qcow2ImgType,
|
||||
)
|
||||
aarch64.addImageTypes(
|
||||
&platform.Aarch64{
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_RAW,
|
||||
},
|
||||
},
|
||||
mkAMIImgTypeAarch64(rd.osVersion, rd.isRHEL()),
|
||||
)
|
||||
|
||||
ppc64le.addImageTypes(
|
||||
&platform.PPC64LE{
|
||||
BIOS: true,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_QCOW2,
|
||||
QCOW2Compat: "1.1",
|
||||
},
|
||||
},
|
||||
qcow2ImgType,
|
||||
)
|
||||
ppc64le.addImageTypes(
|
||||
&platform.PPC64LE{},
|
||||
tarImgType,
|
||||
)
|
||||
|
||||
s390x.addImageTypes(
|
||||
&platform.S390X{
|
||||
Zipl: true,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_QCOW2,
|
||||
QCOW2Compat: "1.1",
|
||||
},
|
||||
},
|
||||
qcow2ImgType,
|
||||
)
|
||||
s390x.addImageTypes(
|
||||
&platform.S390X{},
|
||||
tarImgType,
|
||||
)
|
||||
|
||||
if rd.isRHEL() {
|
||||
// add azure to RHEL distro only
|
||||
x86_64.addImageTypes(azureX64Platform, azureRhuiImgType, azureByosImgType)
|
||||
aarch64.addImageTypes(azureAarch64Platform, azureRhuiImgType, azureByosImgType)
|
||||
|
||||
// keep the RHEL EC2 x86_64 images before 9.3 BIOS-only for backward compatibility
|
||||
if common.VersionLessThan(rd.osVersion, "9.3") {
|
||||
ec2X86Platform = &platform.X86{
|
||||
BIOS: true,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_RAW,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// add ec2 image types to RHEL distro only
|
||||
x86_64.addImageTypes(ec2X86Platform, mkEc2ImgTypeX86_64(rd.osVersion, rd.isRHEL()), mkEc2HaImgTypeX86_64(rd.osVersion, rd.isRHEL()), mkEC2SapImgTypeX86_64(rd.osVersion, rd.isRHEL()))
|
||||
|
||||
aarch64.addImageTypes(
|
||||
&platform.Aarch64{
|
||||
UEFIVendor: rd.vendor,
|
||||
BasePlatform: platform.BasePlatform{
|
||||
ImageFormat: platform.FORMAT_RAW,
|
||||
},
|
||||
},
|
||||
mkEC2ImgTypeAarch64(rd.osVersion, rd.isRHEL()),
|
||||
)
|
||||
|
||||
// add GCE RHUI image to RHEL only
|
||||
x86_64.addImageTypes(gceX86Platform, mkGCERHUIImageType(rd.isRHEL()))
|
||||
} else {
|
||||
x86_64.addImageTypes(azureX64Platform, azureImgType)
|
||||
aarch64.addImageTypes(azureAarch64Platform, azureImgType)
|
||||
}
|
||||
rd.addArches(x86_64, aarch64, ppc64le, s390x)
|
||||
return &rd
|
||||
}
|
||||
|
|
@ -1,848 +0,0 @@
|
|||
package rhel9_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro/distro_test_common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro/rhel9"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
)
|
||||
|
||||
type rhelFamilyDistro struct {
|
||||
name string
|
||||
distro distro.Distro
|
||||
}
|
||||
|
||||
var rhelFamilyDistros = []rhelFamilyDistro{
|
||||
{
|
||||
name: "rhel",
|
||||
distro: rhel9.New(),
|
||||
},
|
||||
}
|
||||
|
||||
func TestFilenameFromType(t *testing.T) {
|
||||
type args struct {
|
||||
outputFormat string
|
||||
}
|
||||
type wantResult struct {
|
||||
filename string
|
||||
mimeType string
|
||||
wantErr bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want wantResult
|
||||
}{
|
||||
{
|
||||
name: "ami",
|
||||
args: args{"ami"},
|
||||
want: wantResult{
|
||||
filename: "image.raw",
|
||||
mimeType: "application/octet-stream",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ec2",
|
||||
args: args{"ec2"},
|
||||
want: wantResult{
|
||||
filename: "image.raw.xz",
|
||||
mimeType: "application/xz",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ec2-ha",
|
||||
args: args{"ec2-ha"},
|
||||
want: wantResult{
|
||||
filename: "image.raw.xz",
|
||||
mimeType: "application/xz",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ec2-sap",
|
||||
args: args{"ec2-sap"},
|
||||
want: wantResult{
|
||||
filename: "image.raw.xz",
|
||||
mimeType: "application/xz",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "qcow2",
|
||||
args: args{"qcow2"},
|
||||
want: wantResult{
|
||||
filename: "disk.qcow2",
|
||||
mimeType: "application/x-qemu-disk",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "openstack",
|
||||
args: args{"openstack"},
|
||||
want: wantResult{
|
||||
filename: "disk.qcow2",
|
||||
mimeType: "application/x-qemu-disk",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "vhd",
|
||||
args: args{"vhd"},
|
||||
want: wantResult{
|
||||
filename: "disk.vhd",
|
||||
mimeType: "application/x-vhd",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "azure-rhui",
|
||||
args: args{"azure-rhui"},
|
||||
want: wantResult{
|
||||
filename: "disk.vhd.xz",
|
||||
mimeType: "application/xz",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "vmdk",
|
||||
args: args{"vmdk"},
|
||||
want: wantResult{
|
||||
filename: "disk.vmdk",
|
||||
mimeType: "application/x-vmdk",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ova",
|
||||
args: args{"ova"},
|
||||
want: wantResult{
|
||||
filename: "image.ova",
|
||||
mimeType: "application/ovf",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "tar",
|
||||
args: args{"tar"},
|
||||
want: wantResult{
|
||||
filename: "root.tar.xz",
|
||||
mimeType: "application/x-tar",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "image-installer",
|
||||
args: args{"image-installer"},
|
||||
want: wantResult{
|
||||
filename: "installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "edge-commit",
|
||||
args: args{"edge-commit"},
|
||||
want: wantResult{
|
||||
filename: "commit.tar",
|
||||
mimeType: "application/x-tar",
|
||||
},
|
||||
},
|
||||
// Alias
|
||||
{
|
||||
name: "rhel-edge-commit",
|
||||
args: args{"rhel-edge-commit"},
|
||||
want: wantResult{
|
||||
filename: "commit.tar",
|
||||
mimeType: "application/x-tar",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "edge-container",
|
||||
args: args{"edge-container"},
|
||||
want: wantResult{
|
||||
filename: "container.tar",
|
||||
mimeType: "application/x-tar",
|
||||
},
|
||||
},
|
||||
// Alias
|
||||
{
|
||||
name: "rhel-edge-container",
|
||||
args: args{"rhel-edge-container"},
|
||||
want: wantResult{
|
||||
filename: "container.tar",
|
||||
mimeType: "application/x-tar",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "edge-installer",
|
||||
args: args{"edge-installer"},
|
||||
want: wantResult{
|
||||
filename: "installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
},
|
||||
},
|
||||
// Alias
|
||||
{
|
||||
name: "rhel-edge-installer",
|
||||
args: args{"rhel-edge-installer"},
|
||||
want: wantResult{
|
||||
filename: "installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "gce",
|
||||
args: args{"gce"},
|
||||
want: wantResult{
|
||||
filename: "image.tar.gz",
|
||||
mimeType: "application/gzip",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "edge-ami",
|
||||
args: args{"edge-ami"},
|
||||
want: wantResult{
|
||||
filename: "image.raw",
|
||||
mimeType: "application/octet-stream",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid-output-type",
|
||||
args: args{"foobar"},
|
||||
want: wantResult{wantErr: true},
|
||||
},
|
||||
}
|
||||
for _, dist := range rhelFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dist := dist.distro
|
||||
arch, _ := dist.GetArch("x86_64")
|
||||
imgType, err := arch.GetImageType(tt.args.outputFormat)
|
||||
if (err != nil) != tt.want.wantErr {
|
||||
t.Errorf("Arch.GetImageType() error = %v, wantErr %v", err, tt.want.wantErr)
|
||||
return
|
||||
}
|
||||
if !tt.want.wantErr {
|
||||
gotFilename := imgType.Filename()
|
||||
gotMIMEType := imgType.MIMEType()
|
||||
if gotFilename != tt.want.filename {
|
||||
t.Errorf("ImageType.Filename() got = %v, want %v", gotFilename, tt.want.filename)
|
||||
}
|
||||
if gotMIMEType != tt.want.mimeType {
|
||||
t.Errorf("ImageType.MIMEType() got1 = %v, want %v", gotMIMEType, tt.want.mimeType)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageType_BuildPackages(t *testing.T) {
|
||||
x8664BuildPackages := []string{
|
||||
"dnf",
|
||||
"dosfstools",
|
||||
"e2fsprogs",
|
||||
"grub2-efi-x64",
|
||||
"grub2-pc",
|
||||
"policycoreutils",
|
||||
"shim-x64",
|
||||
"systemd",
|
||||
"tar",
|
||||
"qemu-img",
|
||||
"xz",
|
||||
}
|
||||
aarch64BuildPackages := []string{
|
||||
"dnf",
|
||||
"dosfstools",
|
||||
"e2fsprogs",
|
||||
"policycoreutils",
|
||||
"qemu-img",
|
||||
"systemd",
|
||||
"tar",
|
||||
"xz",
|
||||
}
|
||||
buildPackages := map[string][]string{
|
||||
"x86_64": x8664BuildPackages,
|
||||
"aarch64": aarch64BuildPackages,
|
||||
}
|
||||
for _, dist := range rhelFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
d := dist.distro
|
||||
for _, archLabel := range d.ListArches() {
|
||||
archStruct, err := d.GetArch(archLabel)
|
||||
if assert.NoErrorf(t, err, "d.GetArch(%v) returned err = %v; expected nil", archLabel, err) {
|
||||
continue
|
||||
}
|
||||
for _, itLabel := range archStruct.ListImageTypes() {
|
||||
itStruct, err := archStruct.GetImageType(itLabel)
|
||||
if assert.NoErrorf(t, err, "d.GetArch(%v) returned err = %v; expected nil", archLabel, err) {
|
||||
continue
|
||||
}
|
||||
manifest, _, err := itStruct.Manifest(&blueprint.Blueprint{}, distro.ImageOptions{}, nil, 0)
|
||||
assert.NoError(t, err)
|
||||
buildPkgs := manifest.GetPackageSetChains()["build"]
|
||||
assert.NotNil(t, buildPkgs)
|
||||
assert.Len(t, buildPkgs, 1)
|
||||
assert.ElementsMatch(t, buildPackages[archLabel], buildPkgs[0].Include)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageType_Name(t *testing.T) {
|
||||
imgMap := []struct {
|
||||
arch string
|
||||
imgNames []string
|
||||
}{
|
||||
{
|
||||
arch: "x86_64",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"openstack",
|
||||
"vhd",
|
||||
"azure-rhui",
|
||||
"vmdk",
|
||||
"ova",
|
||||
"ami",
|
||||
"ec2",
|
||||
"ec2-ha",
|
||||
"ec2-sap",
|
||||
"edge-commit",
|
||||
"edge-container",
|
||||
"edge-installer",
|
||||
"gce",
|
||||
"tar",
|
||||
"image-installer",
|
||||
},
|
||||
},
|
||||
{
|
||||
arch: "aarch64",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"openstack",
|
||||
"ami",
|
||||
"ec2",
|
||||
"edge-commit",
|
||||
"edge-container",
|
||||
"tar",
|
||||
"image-installer",
|
||||
"vhd",
|
||||
"azure-rhui",
|
||||
},
|
||||
},
|
||||
{
|
||||
arch: "ppc64le",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"tar",
|
||||
},
|
||||
},
|
||||
{
|
||||
arch: "s390x",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"tar",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, dist := range rhelFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, mapping := range imgMap {
|
||||
if mapping.arch == platform.ARCH_S390X.String() && dist.name == "centos" {
|
||||
continue
|
||||
}
|
||||
arch, err := dist.distro.GetArch(mapping.arch)
|
||||
if assert.NoError(t, err) {
|
||||
for _, imgName := range mapping.imgNames {
|
||||
if imgName == "edge-commit" && dist.name == "centos" {
|
||||
continue
|
||||
}
|
||||
imgType, err := arch.GetImageType(imgName)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equalf(t, imgName, imgType.Name(), "arch: %s", mapping.arch)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageTypeAliases(t *testing.T) {
|
||||
type args struct {
|
||||
imageTypeAliases []string
|
||||
}
|
||||
type wantResult struct {
|
||||
imageTypeName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want wantResult
|
||||
}{
|
||||
{
|
||||
name: "edge-commit aliases",
|
||||
args: args{
|
||||
imageTypeAliases: []string{"rhel-edge-commit"},
|
||||
},
|
||||
want: wantResult{
|
||||
imageTypeName: "edge-commit",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "edge-container aliases",
|
||||
args: args{
|
||||
imageTypeAliases: []string{"rhel-edge-container"},
|
||||
},
|
||||
want: wantResult{
|
||||
imageTypeName: "edge-container",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "edge-installer aliases",
|
||||
args: args{
|
||||
imageTypeAliases: []string{"rhel-edge-installer"},
|
||||
},
|
||||
want: wantResult{
|
||||
imageTypeName: "edge-installer",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, dist := range rhelFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dist := dist.distro
|
||||
for _, archName := range dist.ListArches() {
|
||||
t.Run(archName, func(t *testing.T) {
|
||||
arch, err := dist.GetArch(archName)
|
||||
require.Nilf(t, err,
|
||||
"failed to get architecture '%s', previously listed as supported for the distro '%s'",
|
||||
archName, dist.Name())
|
||||
// Test image type aliases only if the aliased image type is supported for the arch
|
||||
if _, err = arch.GetImageType(tt.want.imageTypeName); err != nil {
|
||||
t.Skipf("aliased image type '%s' is not supported for architecture '%s'",
|
||||
tt.want.imageTypeName, archName)
|
||||
}
|
||||
for _, alias := range tt.args.imageTypeAliases {
|
||||
t.Run(fmt.Sprintf("'%s' alias for image type '%s'", alias, tt.want.imageTypeName),
|
||||
func(t *testing.T) {
|
||||
gotImage, err := arch.GetImageType(alias)
|
||||
require.Nilf(t, err, "arch.GetImageType() for image type alias '%s' failed: %v",
|
||||
alias, err)
|
||||
assert.Equalf(t, tt.want.imageTypeName, gotImage.Name(),
|
||||
"got unexpected image type name for alias '%s'. got = %s, want = %s",
|
||||
alias, tt.want.imageTypeName, gotImage.Name())
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Check that Manifest() function returns an error for unsupported
|
||||
// configurations.
|
||||
func TestDistro_ManifestError(t *testing.T) {
|
||||
// Currently, the only unsupported configuration is OSTree commit types
|
||||
// with Kernel boot options
|
||||
r9distro := rhel9.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Kernel: &blueprint.KernelCustomization{
|
||||
Append: "debug",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, archName := range r9distro.ListArches() {
|
||||
arch, _ := r9distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
imgOpts := distro.ImageOptions{
|
||||
Size: imgType.Size(0),
|
||||
}
|
||||
_, _, err := imgType.Manifest(&bp, imgOpts, nil, 0)
|
||||
if imgTypeName == "edge-commit" || imgTypeName == "edge-container" {
|
||||
assert.EqualError(t, err, "kernel boot parameter customizations are not supported for ostree types")
|
||||
} else if imgTypeName == "edge-raw-image" || imgTypeName == "edge-ami" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("\"%s\" images require specifying a URL from which to retrieve the OSTree commit", imgTypeName))
|
||||
} else if imgTypeName == "edge-installer" || imgTypeName == "edge-simplified-installer" {
|
||||
assert.EqualError(t, err, fmt.Sprintf("boot ISO image type \"%s\" requires specifying a URL from which to retrieve the OSTree commit", imgTypeName))
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestArchitecture_ListImageTypes(t *testing.T) {
|
||||
imgMap := []struct {
|
||||
arch string
|
||||
imgNames []string
|
||||
rhelAdditionalImageTypes []string
|
||||
}{
|
||||
{
|
||||
arch: "x86_64",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"openstack",
|
||||
"vhd",
|
||||
"azure-rhui",
|
||||
"vmdk",
|
||||
"ova",
|
||||
"ami",
|
||||
"ec2",
|
||||
"ec2-ha",
|
||||
"ec2-sap",
|
||||
"edge-commit",
|
||||
"edge-container",
|
||||
"edge-installer",
|
||||
"edge-raw-image",
|
||||
"edge-simplified-installer",
|
||||
"edge-ami",
|
||||
"gce",
|
||||
"gce-rhui",
|
||||
"tar",
|
||||
"image-installer",
|
||||
"oci",
|
||||
},
|
||||
},
|
||||
{
|
||||
arch: "aarch64",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"openstack",
|
||||
"ami",
|
||||
"ec2",
|
||||
"edge-commit",
|
||||
"edge-container",
|
||||
"edge-installer",
|
||||
"edge-simplified-installer",
|
||||
"edge-raw-image",
|
||||
"edge-ami",
|
||||
"tar",
|
||||
"image-installer",
|
||||
"vhd",
|
||||
"azure-rhui",
|
||||
},
|
||||
},
|
||||
{
|
||||
arch: "ppc64le",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"tar",
|
||||
},
|
||||
},
|
||||
{
|
||||
arch: "s390x",
|
||||
imgNames: []string{
|
||||
"qcow2",
|
||||
"tar",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, dist := range rhelFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, mapping := range imgMap {
|
||||
arch, err := dist.distro.GetArch(mapping.arch)
|
||||
require.NoError(t, err)
|
||||
imageTypes := arch.ListImageTypes()
|
||||
|
||||
var expectedImageTypes []string
|
||||
expectedImageTypes = append(expectedImageTypes, mapping.imgNames...)
|
||||
if dist.name == "rhel" {
|
||||
expectedImageTypes = append(expectedImageTypes, mapping.rhelAdditionalImageTypes...)
|
||||
}
|
||||
|
||||
require.ElementsMatch(t, expectedImageTypes, imageTypes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRhel9_ListArches(t *testing.T) {
|
||||
arches := rhel9.New().ListArches()
|
||||
assert.Equal(t, []string{"aarch64", "ppc64le", "s390x", "x86_64"}, arches)
|
||||
}
|
||||
|
||||
func TestRhel9_GetArch(t *testing.T) {
|
||||
arches := []struct {
|
||||
name string
|
||||
errorExpected bool
|
||||
errorExpectedInCentos bool
|
||||
}{
|
||||
{
|
||||
name: "x86_64",
|
||||
},
|
||||
{
|
||||
name: "aarch64",
|
||||
},
|
||||
{
|
||||
name: "ppc64le",
|
||||
},
|
||||
{
|
||||
name: "s390x",
|
||||
},
|
||||
{
|
||||
name: "foo-arch",
|
||||
errorExpected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, dist := range rhelFamilyDistros {
|
||||
t.Run(dist.name, func(t *testing.T) {
|
||||
for _, a := range arches {
|
||||
actualArch, err := dist.distro.GetArch(a.name)
|
||||
if a.errorExpected || (a.errorExpectedInCentos && dist.name == "centos") {
|
||||
assert.Nil(t, actualArch)
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.Equal(t, a.name, actualArch.Name())
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRhel9_Name(t *testing.T) {
|
||||
distro := rhel9.New()
|
||||
assert.Equal(t, "rhel-9", distro.Name())
|
||||
}
|
||||
|
||||
func TestRhel9_ModulePlatformID(t *testing.T) {
|
||||
distro := rhel9.New()
|
||||
assert.Equal(t, "platform:el9", distro.ModulePlatformID())
|
||||
}
|
||||
|
||||
func TestRhel9_KernelOption(t *testing.T) {
|
||||
distro_test_common.TestDistro_KernelOption(t, rhel9.New())
|
||||
}
|
||||
|
||||
func TestRhel9_OSTreeOptions(t *testing.T) {
|
||||
distro_test_common.TestDistro_OSTreeOptions(t, rhel9.New())
|
||||
}
|
||||
|
||||
func TestDistro_CustomFileSystemManifestError(t *testing.T) {
|
||||
r9distro := rhel9.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/etc",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range r9distro.ListArches() {
|
||||
arch, _ := r9distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if imgTypeName == "edge-commit" || imgTypeName == "edge-container" {
|
||||
assert.EqualError(t, err, "Custom mountpoints are not supported for ostree types")
|
||||
} else if imgTypeName == "edge-installer" || imgTypeName == "edge-simplified-installer" || imgTypeName == "edge-raw-image" || imgTypeName == "edge-ami" {
|
||||
continue
|
||||
} else {
|
||||
assert.EqualError(t, err, "The following custom mountpoints are not supported [\"/etc\"]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_TestRootMountPoint(t *testing.T) {
|
||||
r9distro := rhel9.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range r9distro.ListArches() {
|
||||
arch, _ := r9distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if imgTypeName == "edge-commit" || imgTypeName == "edge-container" {
|
||||
assert.EqualError(t, err, "Custom mountpoints are not supported for ostree types")
|
||||
} else if imgTypeName == "edge-installer" || imgTypeName == "edge-simplified-installer" || imgTypeName == "edge-raw-image" || imgTypeName == "edge-ami" {
|
||||
continue
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_CustomFileSystemSubDirectories(t *testing.T) {
|
||||
r9distro := rhel9.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/log",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/log/audit",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range r9distro.ListArches() {
|
||||
arch, _ := r9distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if strings.HasPrefix(imgTypeName, "edge-") {
|
||||
continue
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_MountpointsWithArbitraryDepthAllowed(t *testing.T) {
|
||||
r9distro := rhel9.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/a",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/a/b",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/a/b/c",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var/a/b/c/d",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range r9distro.ListArches() {
|
||||
arch, _ := r9distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if strings.HasPrefix(imgTypeName, "edge-") {
|
||||
continue
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_DirtyMountpointsNotAllowed(t *testing.T) {
|
||||
r9distro := rhel9.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "//",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var//",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/var//log/audit/",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range r9distro.ListArches() {
|
||||
arch, _ := r9distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if strings.HasPrefix(imgTypeName, "edge-") {
|
||||
continue
|
||||
} else {
|
||||
assert.EqualError(t, err, "The following custom mountpoints are not supported [\"//\" \"/var//\" \"/var//log/audit/\"]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_CustomFileSystemPatternMatching(t *testing.T) {
|
||||
r9distro := rhel9.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/variable",
|
||||
},
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/variable/log/audit",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range r9distro.ListArches() {
|
||||
arch, _ := r9distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if imgTypeName == "edge-commit" || imgTypeName == "edge-container" {
|
||||
assert.EqualError(t, err, "Custom mountpoints are not supported for ostree types")
|
||||
} else if imgTypeName == "edge-installer" || imgTypeName == "edge-simplified-installer" || imgTypeName == "edge-raw-image" || imgTypeName == "edge-ami" {
|
||||
continue
|
||||
} else {
|
||||
assert.EqualError(t, err, "The following custom mountpoints are not supported [\"/variable\" \"/variable/log/audit\"]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistro_CustomUsrPartitionNotLargeEnough(t *testing.T) {
|
||||
r9distro := rhel9.New()
|
||||
bp := blueprint.Blueprint{
|
||||
Customizations: &blueprint.Customizations{
|
||||
Filesystem: []blueprint.FilesystemCustomization{
|
||||
{
|
||||
MinSize: 1024,
|
||||
Mountpoint: "/usr",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, archName := range r9distro.ListArches() {
|
||||
arch, _ := r9distro.GetArch(archName)
|
||||
for _, imgTypeName := range arch.ListImageTypes() {
|
||||
imgType, _ := arch.GetImageType(imgTypeName)
|
||||
_, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0)
|
||||
if imgTypeName == "edge-commit" || imgTypeName == "edge-container" {
|
||||
assert.EqualError(t, err, "Custom mountpoints are not supported for ostree types")
|
||||
} else if imgTypeName == "edge-installer" || imgTypeName == "edge-simplified-installer" || imgTypeName == "edge-raw-image" || imgTypeName == "edge-ami" {
|
||||
continue
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,511 +0,0 @@
|
|||
package rhel9
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/disk"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/environment"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
var (
|
||||
// Image Definitions
|
||||
edgeCommitImgType = imageType{
|
||||
name: "edge-commit",
|
||||
nameAliases: []string{"rhel-edge-commit"},
|
||||
filename: "commit.tar",
|
||||
mimeType: "application/x-tar",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: edgeCommitPackageSet,
|
||||
},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
EnabledServices: edgeServices,
|
||||
},
|
||||
rpmOstree: true,
|
||||
image: edgeCommitImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "ostree-commit", "commit-archive"},
|
||||
exports: []string{"commit-archive"},
|
||||
}
|
||||
|
||||
edgeOCIImgType = imageType{
|
||||
name: "edge-container",
|
||||
nameAliases: []string{"rhel-edge-container"},
|
||||
filename: "container.tar",
|
||||
mimeType: "application/x-tar",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: edgeCommitPackageSet,
|
||||
containerPkgsKey: func(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{"nginx"}, // FIXME: this has no effect
|
||||
}
|
||||
},
|
||||
},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
EnabledServices: edgeServices,
|
||||
},
|
||||
rpmOstree: true,
|
||||
bootISO: false,
|
||||
image: edgeContainerImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "ostree-commit", "container-tree", "container"},
|
||||
exports: []string{"container"},
|
||||
}
|
||||
|
||||
edgeRawImgType = imageType{
|
||||
name: "edge-raw-image",
|
||||
nameAliases: []string{"rhel-edge-raw-image"},
|
||||
filename: "image.raw.xz",
|
||||
compression: "xz",
|
||||
mimeType: "application/xz",
|
||||
packageSets: nil,
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
},
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
rpmOstree: true,
|
||||
bootable: true,
|
||||
bootISO: false,
|
||||
image: edgeRawImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"ostree-deployment", "image", "xz"},
|
||||
exports: []string{"xz"},
|
||||
basePartitionTables: edgeBasePartitionTables,
|
||||
}
|
||||
|
||||
edgeInstallerImgType = imageType{
|
||||
name: "edge-installer",
|
||||
nameAliases: []string{"rhel-edge-installer"},
|
||||
filename: "installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
// TODO: non-arch-specific package set handling for installers
|
||||
// This image type requires build packages for installers and
|
||||
// ostree/edge. For now we only have x86-64 installer build
|
||||
// package sets defined. When we add installer build package sets
|
||||
// for other architectures, this will need to be moved to the
|
||||
// architecture and the merging will happen in the PackageSets()
|
||||
// method like the other sets.
|
||||
installerPkgsKey: edgeInstallerPackageSet,
|
||||
},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
EnabledServices: edgeServices,
|
||||
},
|
||||
rpmOstree: true,
|
||||
bootISO: true,
|
||||
image: edgeInstallerImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"anaconda-tree", "rootfs-image", "efiboot-tree", "bootiso-tree", "bootiso"},
|
||||
exports: []string{"bootiso"},
|
||||
}
|
||||
|
||||
edgeSimplifiedInstallerImgType = imageType{
|
||||
name: "edge-simplified-installer",
|
||||
nameAliases: []string{"rhel-edge-simplified-installer"},
|
||||
filename: "simplified-installer.iso",
|
||||
mimeType: "application/x-iso9660-image",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
// TODO: non-arch-specific package set handling for installers
|
||||
// This image type requires build packages for installers and
|
||||
// ostree/edge. For now we only have x86-64 installer build
|
||||
// package sets defined. When we add installer build package sets
|
||||
// for other architectures, this will need to be moved to the
|
||||
// architecture and the merging will happen in the PackageSets()
|
||||
// method like the other sets.
|
||||
installerPkgsKey: edgeSimplifiedInstallerPackageSet,
|
||||
},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
EnabledServices: edgeServices,
|
||||
},
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
rpmOstree: true,
|
||||
bootable: true,
|
||||
bootISO: true,
|
||||
image: edgeSimplifiedInstallerImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"ostree-deployment", "image", "xz", "coi-tree", "efiboot-tree", "bootiso-tree", "bootiso"},
|
||||
exports: []string{"bootiso"},
|
||||
basePartitionTables: edgeBasePartitionTables,
|
||||
}
|
||||
|
||||
edgeAMIImgType = imageType{
|
||||
name: "edge-ami",
|
||||
filename: "image.raw",
|
||||
mimeType: "application/octet-stream",
|
||||
packageSets: nil,
|
||||
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
},
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
rpmOstree: true,
|
||||
bootable: true,
|
||||
bootISO: false,
|
||||
image: edgeRawImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"ostree-deployment", "image"},
|
||||
exports: []string{"image"},
|
||||
basePartitionTables: edgeBasePartitionTables,
|
||||
environment: &environment.EC2{},
|
||||
}
|
||||
|
||||
// Shared Services
|
||||
edgeServices = []string{
|
||||
// TODO(runcom): move fdo-client-linuxapp.service to presets?
|
||||
"NetworkManager.service", "firewalld.service", "sshd.service", "fdo-client-linuxapp.service",
|
||||
}
|
||||
|
||||
// Partition tables
|
||||
edgeBasePartitionTables = distro.BasePartitionTableMap{
|
||||
platform.ARCH_X86_64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 1 * common.MebiByte, // 1MB
|
||||
Bootable: true,
|
||||
Type: disk.BIOSBootPartitionGUID,
|
||||
UUID: disk.BIOSBootPartitionUUID,
|
||||
},
|
||||
{
|
||||
Size: 127 * common.MebiByte, // 127 MB
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
Label: "EFI-SYSTEM",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 384 * common.MebiByte, // 384 MB
|
||||
Type: disk.XBootLDRPartitionGUID,
|
||||
UUID: disk.FilesystemDataUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/boot",
|
||||
Label: "boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 1,
|
||||
FSTabPassNo: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.LUKSContainer{
|
||||
Label: "crypt_root",
|
||||
Cipher: "cipher_null",
|
||||
Passphrase: "osbuild",
|
||||
PBKDF: disk.Argon2id{
|
||||
Memory: 32,
|
||||
Iterations: 4,
|
||||
Parallelism: 1,
|
||||
},
|
||||
Clevis: &disk.ClevisBind{
|
||||
Pin: "null",
|
||||
Policy: "{}",
|
||||
RemovePassphrase: true,
|
||||
},
|
||||
Payload: &disk.LVMVolumeGroup{
|
||||
Name: "rootvg",
|
||||
Description: "built with lvm2 and osbuild",
|
||||
LogicalVolumes: []disk.LVMLogicalVolume{
|
||||
{
|
||||
Size: 9 * 1024 * 1024 * 1024, // 9 GB
|
||||
Name: "rootlv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
platform.ARCH_AARCH64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 127 * common.MebiByte, // 127 MB
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
Label: "EFI-SYSTEM",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 384 * common.MebiByte, // 384 MB
|
||||
Type: disk.XBootLDRPartitionGUID,
|
||||
UUID: disk.FilesystemDataUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/boot",
|
||||
Label: "boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 1,
|
||||
FSTabPassNo: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.LUKSContainer{
|
||||
Label: "crypt_root",
|
||||
Cipher: "cipher_null",
|
||||
Passphrase: "osbuild",
|
||||
PBKDF: disk.Argon2id{
|
||||
Memory: 32,
|
||||
Iterations: 4,
|
||||
Parallelism: 1,
|
||||
},
|
||||
Clevis: &disk.ClevisBind{
|
||||
Pin: "null",
|
||||
Policy: "{}",
|
||||
RemovePassphrase: true,
|
||||
},
|
||||
Payload: &disk.LVMVolumeGroup{
|
||||
Name: "rootvg",
|
||||
Description: "built with lvm2 and osbuild",
|
||||
LogicalVolumes: []disk.LVMLogicalVolume{
|
||||
{
|
||||
Size: 9 * 1024 * 1024 * 1024, // 9 GB
|
||||
Name: "rootlv",
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// Package Sets
|
||||
|
||||
// edge commit OS package set
|
||||
func edgeCommitPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"redhat-release",
|
||||
"glibc",
|
||||
"glibc-minimal-langpack",
|
||||
"nss-altfiles",
|
||||
"dracut-config-generic",
|
||||
"dracut-network",
|
||||
"basesystem",
|
||||
"bash",
|
||||
"platform-python",
|
||||
"shadow-utils",
|
||||
"chrony",
|
||||
"setup",
|
||||
"shadow-utils",
|
||||
"sudo",
|
||||
"systemd",
|
||||
"coreutils",
|
||||
"util-linux",
|
||||
"curl",
|
||||
"vim-minimal",
|
||||
"rpm",
|
||||
"rpm-ostree",
|
||||
"polkit",
|
||||
"lvm2",
|
||||
"cryptsetup",
|
||||
"pinentry",
|
||||
"e2fsprogs",
|
||||
"dosfstools",
|
||||
"keyutils",
|
||||
"gnupg2",
|
||||
"attr",
|
||||
"xz",
|
||||
"gzip",
|
||||
"firewalld",
|
||||
"iptables",
|
||||
"NetworkManager",
|
||||
"NetworkManager-wifi",
|
||||
"NetworkManager-wwan",
|
||||
"wpa_supplicant",
|
||||
"dnsmasq",
|
||||
"traceroute",
|
||||
"hostname",
|
||||
"iproute",
|
||||
"iputils",
|
||||
"openssh-clients",
|
||||
"procps-ng",
|
||||
"rootfiles",
|
||||
"openssh-server",
|
||||
"passwd",
|
||||
"policycoreutils",
|
||||
"policycoreutils-python-utils",
|
||||
"selinux-policy-targeted",
|
||||
"setools-console",
|
||||
"less",
|
||||
"tar",
|
||||
"rsync",
|
||||
"usbguard",
|
||||
"bash-completion",
|
||||
"tmux",
|
||||
"ima-evm-utils",
|
||||
"audit",
|
||||
"podman",
|
||||
"containernetworking-plugins", // required for cni networks but not a hard dependency of podman >= 4.2.0 (rhbz#2123210)
|
||||
"container-selinux",
|
||||
"skopeo",
|
||||
"criu",
|
||||
"slirp4netns",
|
||||
"fuse-overlayfs",
|
||||
"clevis",
|
||||
"clevis-dracut",
|
||||
"clevis-luks",
|
||||
"greenboot",
|
||||
"greenboot-default-health-checks",
|
||||
"fdo-client",
|
||||
"fdo-owner-cli",
|
||||
"sos",
|
||||
},
|
||||
Exclude: []string{
|
||||
"rng-tools",
|
||||
},
|
||||
}
|
||||
|
||||
switch t.arch.Name() {
|
||||
case platform.ARCH_X86_64.String():
|
||||
ps = ps.Append(x8664EdgeCommitPackageSet(t))
|
||||
|
||||
case platform.ARCH_AARCH64.String():
|
||||
ps = ps.Append(aarch64EdgeCommitPackageSet(t))
|
||||
}
|
||||
|
||||
if !common.VersionLessThan(t.arch.distro.osVersion, "9.2") || !common.VersionLessThan(t.arch.distro.osVersion, "9-stream") {
|
||||
ps.Include = append(ps.Include, "ignition", "ignition-edge", "ssh-key-dir")
|
||||
}
|
||||
|
||||
return ps
|
||||
|
||||
}
|
||||
|
||||
func x8664EdgeCommitPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"grub2",
|
||||
"grub2-efi-x64",
|
||||
"efibootmgr",
|
||||
"shim-x64",
|
||||
"microcode_ctl",
|
||||
"iwl1000-firmware",
|
||||
"iwl100-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6050-firmware",
|
||||
"iwl7260-firmware",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func aarch64EdgeCommitPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"grub2-efi-aa64",
|
||||
"efibootmgr",
|
||||
"shim-aa64",
|
||||
"iwl7260-firmware",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func edgeInstallerPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return anacondaPackageSet(t)
|
||||
}
|
||||
|
||||
func edgeSimplifiedInstallerPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
// common installer packages
|
||||
ps := installerPackageSet(t)
|
||||
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"attr",
|
||||
"basesystem",
|
||||
"binutils",
|
||||
"bsdtar",
|
||||
"clevis-dracut",
|
||||
"clevis-luks",
|
||||
"cloud-utils-growpart",
|
||||
"coreos-installer",
|
||||
"coreos-installer-dracut",
|
||||
"coreutils",
|
||||
"device-mapper-multipath",
|
||||
"dnsmasq",
|
||||
"dosfstools",
|
||||
"dracut-live",
|
||||
"e2fsprogs",
|
||||
"fcoe-utils",
|
||||
"fdo-init",
|
||||
"gzip",
|
||||
"ima-evm-utils",
|
||||
"iproute",
|
||||
"iptables",
|
||||
"iputils",
|
||||
"iscsi-initiator-utils",
|
||||
"keyutils",
|
||||
"lldpad",
|
||||
"lvm2",
|
||||
"passwd",
|
||||
"policycoreutils",
|
||||
"policycoreutils-python-utils",
|
||||
"procps-ng",
|
||||
"redhat-logos",
|
||||
"rootfiles",
|
||||
"setools-console",
|
||||
"sudo",
|
||||
"traceroute",
|
||||
"util-linux",
|
||||
},
|
||||
})
|
||||
|
||||
switch t.arch.Name() {
|
||||
|
||||
case platform.ARCH_X86_64.String():
|
||||
ps = ps.Append(x8664EdgeCommitPackageSet(t))
|
||||
case platform.ARCH_AARCH64.String():
|
||||
ps = ps.Append(aarch64EdgeCommitPackageSet(t))
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported arch: %s", t.arch.Name()))
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
|
@ -1,300 +0,0 @@
|
|||
package rhel9
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/subscription"
|
||||
)
|
||||
|
||||
const gceKernelOptions = "net.ifnames=0 biosdevname=0 scsi_mod.use_blk_mq=Y console=ttyS0,38400n8d"
|
||||
|
||||
var (
|
||||
gceImgType = imageType{
|
||||
name: "gce",
|
||||
filename: "image.tar.gz",
|
||||
mimeType: "application/gzip",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: gcePackageSet,
|
||||
},
|
||||
kernelOptions: gceKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 20 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "archive"},
|
||||
exports: []string{"archive"},
|
||||
// TODO: the base partition table still contains the BIOS boot partition, but the image is UEFI-only
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
|
||||
gceRhuiImgType = imageType{
|
||||
name: "gce-rhui",
|
||||
filename: "image.tar.gz",
|
||||
mimeType: "application/gzip",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: gceRhuiPackageSet,
|
||||
},
|
||||
kernelOptions: gceKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 20 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "archive"},
|
||||
exports: []string{"archive"},
|
||||
// TODO: the base partition table still contains the BIOS boot partition, but the image is UEFI-only
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
)
|
||||
|
||||
func mkGCEImageType(rhsm bool) imageType {
|
||||
it := gceImgType
|
||||
it.defaultImageConfig = baseGCEImageConfig(rhsm)
|
||||
return it
|
||||
}
|
||||
|
||||
func mkGCERHUIImageType(rhsm bool) imageType {
|
||||
it := gceRhuiImgType
|
||||
it.defaultImageConfig = defaultGceRhuiImageConfig(rhsm)
|
||||
return it
|
||||
}
|
||||
|
||||
func baseGCEImageConfig(rhsm bool) *distro.ImageConfig {
|
||||
ic := &distro.ImageConfig{
|
||||
Timezone: common.ToPtr("UTC"),
|
||||
TimeSynchronization: &osbuild.ChronyStageOptions{
|
||||
Servers: []osbuild.ChronyConfigServer{{Hostname: "metadata.google.internal"}},
|
||||
},
|
||||
Firewall: &osbuild.FirewallStageOptions{
|
||||
DefaultZone: "trusted",
|
||||
},
|
||||
EnabledServices: []string{
|
||||
"sshd",
|
||||
"rngd",
|
||||
"dnf-automatic.timer",
|
||||
},
|
||||
DisabledServices: []string{
|
||||
"sshd-keygen@",
|
||||
"reboot.target",
|
||||
},
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
Keyboard: &osbuild.KeymapStageOptions{
|
||||
Keymap: "us",
|
||||
},
|
||||
DNFConfig: []*osbuild.DNFConfigStageOptions{
|
||||
{
|
||||
Config: &osbuild.DNFConfig{
|
||||
Main: &osbuild.DNFConfigMain{
|
||||
IPResolve: "4",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
DNFAutomaticConfig: &osbuild.DNFAutomaticConfigStageOptions{
|
||||
Config: &osbuild.DNFAutomaticConfig{
|
||||
Commands: &osbuild.DNFAutomaticConfigCommands{
|
||||
ApplyUpdates: common.ToPtr(true),
|
||||
UpgradeType: osbuild.DNFAutomaticUpgradeTypeSecurity,
|
||||
},
|
||||
},
|
||||
},
|
||||
YUMRepos: []*osbuild.YumReposStageOptions{
|
||||
{
|
||||
Filename: "google-cloud.repo",
|
||||
Repos: []osbuild.YumRepository{
|
||||
{
|
||||
Id: "google-compute-engine",
|
||||
Name: "Google Compute Engine",
|
||||
BaseURLs: []string{"https://packages.cloud.google.com/yum/repos/google-compute-engine-el9-x86_64-stable"},
|
||||
Enabled: common.ToPtr(true),
|
||||
// TODO: enable GPG check once Google stops using SHA-1 in their keys
|
||||
// https://issuetracker.google.com/issues/223626963
|
||||
GPGCheck: common.ToPtr(false),
|
||||
RepoGPGCheck: common.ToPtr(false),
|
||||
GPGKey: []string{
|
||||
"https://packages.cloud.google.com/yum/doc/yum-key.gpg",
|
||||
"https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
SshdConfig: &osbuild.SshdConfigStageOptions{
|
||||
Config: osbuild.SshdConfigConfig{
|
||||
PasswordAuthentication: common.ToPtr(false),
|
||||
ClientAliveInterval: common.ToPtr(420),
|
||||
PermitRootLogin: osbuild.PermitRootLoginValueNo,
|
||||
},
|
||||
},
|
||||
Sysconfig: []*osbuild.SysconfigStageOptions{
|
||||
{
|
||||
Kernel: &osbuild.SysconfigKernelOptions{
|
||||
DefaultKernel: "kernel-core",
|
||||
UpdateDefault: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Modprobe: []*osbuild.ModprobeStageOptions{
|
||||
{
|
||||
Filename: "blacklist-floppy.conf",
|
||||
Commands: osbuild.ModprobeConfigCmdList{
|
||||
osbuild.NewModprobeConfigCmdBlacklist("floppy"),
|
||||
},
|
||||
},
|
||||
},
|
||||
GCPGuestAgentConfig: &osbuild.GcpGuestAgentConfigOptions{
|
||||
ConfigScope: osbuild.GcpGuestAgentConfigScopeDistro,
|
||||
Config: &osbuild.GcpGuestAgentConfig{
|
||||
InstanceSetup: &osbuild.GcpGuestAgentConfigInstanceSetup{
|
||||
SetBotoConfig: common.ToPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if rhsm {
|
||||
ic.RHSMConfig = map[subscription.RHSMStatus]*osbuild.RHSMStageOptions{
|
||||
subscription.RHSMConfigNoSubscription: {
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
// Don't disable RHSM redhat.repo management on the GCE
|
||||
// image, which is BYOS and does not use RHUI for content.
|
||||
// Otherwise subscribing the system manually after booting
|
||||
// it would result in empty redhat.repo. Without RHUI, such
|
||||
// system would have no way to get Red Hat content, but
|
||||
// enable the repo management manually, which would be very
|
||||
// confusing.
|
||||
},
|
||||
},
|
||||
subscription.RHSMConfigWithSubscription: {
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
// do not disable the redhat.repo management if the user
|
||||
// explicitly request the system to be subscribed
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return ic
|
||||
}
|
||||
|
||||
func defaultGceRhuiImageConfig(rhsm bool) *distro.ImageConfig {
|
||||
ic := &distro.ImageConfig{
|
||||
RHSMConfig: map[subscription.RHSMStatus]*osbuild.RHSMStageOptions{
|
||||
subscription.RHSMConfigNoSubscription: {
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
Rhsm: &osbuild.SubManConfigRHSMSection{
|
||||
ManageRepos: common.ToPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
subscription.RHSMConfigWithSubscription: {
|
||||
SubMan: &osbuild.RHSMStageOptionsSubMan{
|
||||
Rhsmcertd: &osbuild.SubManConfigRHSMCERTDSection{
|
||||
AutoRegistration: common.ToPtr(true),
|
||||
},
|
||||
// do not disable the redhat.repo management if the user
|
||||
// explicitly request the system to be subscribed
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return ic.InheritFrom(baseGCEImageConfig(rhsm))
|
||||
}
|
||||
|
||||
func gceCommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"langpacks-en", // not in Google's KS
|
||||
"acpid",
|
||||
"dhcp-client",
|
||||
"dnf-automatic",
|
||||
"net-tools",
|
||||
//"openssh-server", included in core
|
||||
"python3",
|
||||
"rng-tools",
|
||||
"tar",
|
||||
"vim",
|
||||
|
||||
// GCE guest tools
|
||||
"google-compute-engine",
|
||||
"google-osconfig-agent",
|
||||
"gce-disk-expand",
|
||||
|
||||
// Not explicitly included in GCP kickstart, but present on the image
|
||||
// for time synchronization
|
||||
"chrony",
|
||||
"timedatex",
|
||||
// EFI
|
||||
"grub2-tools-efi",
|
||||
"firewalld", // not pulled in any more as on RHEL-8
|
||||
},
|
||||
Exclude: []string{
|
||||
"alsa-utils",
|
||||
"b43-fwcutter",
|
||||
"dmraid",
|
||||
"eject",
|
||||
"gpm",
|
||||
"irqbalance",
|
||||
"microcode_ctl",
|
||||
"smartmontools",
|
||||
"aic94xx-firmware",
|
||||
"atmel-firmware",
|
||||
"b43-openfwwf",
|
||||
"bfa-firmware",
|
||||
"ipw2100-firmware",
|
||||
"ipw2200-firmware",
|
||||
"ivtv-firmware",
|
||||
"iwl100-firmware",
|
||||
"iwl1000-firmware",
|
||||
"iwl3945-firmware",
|
||||
"iwl4965-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6000-firmware",
|
||||
"iwl6000g2a-firmware",
|
||||
"iwl6050-firmware",
|
||||
"kernel-firmware",
|
||||
"libertas-usb8388-firmware",
|
||||
"ql2100-firmware",
|
||||
"ql2200-firmware",
|
||||
"ql23xx-firmware",
|
||||
"ql2400-firmware",
|
||||
"ql2500-firmware",
|
||||
"rt61pci-firmware",
|
||||
"rt73usb-firmware",
|
||||
"xorg-x11-drv-ati-firmware",
|
||||
"zd1211-firmware",
|
||||
// RHBZ#2075815
|
||||
"qemu-guest-agent",
|
||||
},
|
||||
}.Append(coreOsCommonPackageSet(t)).Append(distroSpecificPackageSet(t))
|
||||
|
||||
// Some excluded packages are part of the @core group package set returned
|
||||
// by coreOsCommonPackageSet(). Ensure that the conflicting packages are
|
||||
// returned from the list of `Include` packages.
|
||||
return ps.ResolveConflictsExclude()
|
||||
}
|
||||
|
||||
// GCE BYOS image
|
||||
func gcePackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return gceCommonPackageSet(t)
|
||||
}
|
||||
|
||||
// GCE RHUI image
|
||||
func gceRhuiPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"google-rhui-client-rhel9",
|
||||
},
|
||||
}.Append(gceCommonPackageSet(t))
|
||||
}
|
||||
|
|
@ -1,627 +0,0 @@
|
|||
package rhel9
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/container"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/fdo"
|
||||
"github.com/osbuild/osbuild-composer/internal/ignition"
|
||||
"github.com/osbuild/osbuild-composer/internal/image"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/oscap"
|
||||
"github.com/osbuild/osbuild-composer/internal/ostree"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/users"
|
||||
"github.com/osbuild/osbuild-composer/internal/workload"
|
||||
)
|
||||
|
||||
func osCustomizations(
|
||||
t *imageType,
|
||||
osPackageSet rpmmd.PackageSet,
|
||||
options distro.ImageOptions,
|
||||
containers []container.SourceSpec,
|
||||
c *blueprint.Customizations,
|
||||
) manifest.OSCustomizations {
|
||||
|
||||
imageConfig := t.getDefaultImageConfig()
|
||||
|
||||
osc := manifest.OSCustomizations{}
|
||||
|
||||
if t.bootable || t.rpmOstree {
|
||||
osc.KernelName = c.GetKernel().Name
|
||||
|
||||
var kernelOptions []string
|
||||
if t.kernelOptions != "" {
|
||||
kernelOptions = append(kernelOptions, t.kernelOptions)
|
||||
}
|
||||
if bpKernel := c.GetKernel(); bpKernel.Append != "" {
|
||||
kernelOptions = append(kernelOptions, bpKernel.Append)
|
||||
}
|
||||
osc.KernelOptionsAppend = kernelOptions
|
||||
}
|
||||
|
||||
osc.ExtraBasePackages = osPackageSet.Include
|
||||
osc.ExcludeBasePackages = osPackageSet.Exclude
|
||||
osc.ExtraBaseRepos = osPackageSet.Repositories
|
||||
|
||||
osc.Containers = containers
|
||||
|
||||
osc.GPGKeyFiles = imageConfig.GPGKeyFiles
|
||||
if imageConfig.ExcludeDocs != nil {
|
||||
osc.ExcludeDocs = *imageConfig.ExcludeDocs
|
||||
}
|
||||
|
||||
if !t.bootISO {
|
||||
// don't put users and groups in the payload of an installer
|
||||
// add them via kickstart instead
|
||||
osc.Groups = users.GroupsFromBP(c.GetGroups())
|
||||
osc.Users = users.UsersFromBP(c.GetUsers())
|
||||
}
|
||||
|
||||
osc.EnabledServices = imageConfig.EnabledServices
|
||||
osc.DisabledServices = imageConfig.DisabledServices
|
||||
if imageConfig.DefaultTarget != nil {
|
||||
osc.DefaultTarget = *imageConfig.DefaultTarget
|
||||
}
|
||||
|
||||
osc.Firewall = imageConfig.Firewall
|
||||
if fw := c.GetFirewall(); fw != nil {
|
||||
options := osbuild.FirewallStageOptions{
|
||||
Ports: fw.Ports,
|
||||
}
|
||||
|
||||
if fw.Services != nil {
|
||||
options.EnabledServices = fw.Services.Enabled
|
||||
options.DisabledServices = fw.Services.Disabled
|
||||
}
|
||||
if fw.Zones != nil {
|
||||
for _, z := range fw.Zones {
|
||||
options.Zones = append(options.Zones, osbuild.FirewallZone{
|
||||
Name: *z.Name,
|
||||
Sources: z.Sources,
|
||||
})
|
||||
}
|
||||
}
|
||||
osc.Firewall = &options
|
||||
}
|
||||
|
||||
language, keyboard := c.GetPrimaryLocale()
|
||||
if language != nil {
|
||||
osc.Language = *language
|
||||
} else if imageConfig.Locale != nil {
|
||||
osc.Language = *imageConfig.Locale
|
||||
}
|
||||
if keyboard != nil {
|
||||
osc.Keyboard = keyboard
|
||||
} else if imageConfig.Keyboard != nil {
|
||||
osc.Keyboard = &imageConfig.Keyboard.Keymap
|
||||
if imageConfig.Keyboard.X11Keymap != nil {
|
||||
osc.X11KeymapLayouts = imageConfig.Keyboard.X11Keymap.Layouts
|
||||
}
|
||||
}
|
||||
|
||||
if hostname := c.GetHostname(); hostname != nil {
|
||||
osc.Hostname = *hostname
|
||||
}
|
||||
|
||||
timezone, ntpServers := c.GetTimezoneSettings()
|
||||
if timezone != nil {
|
||||
osc.Timezone = *timezone
|
||||
} else if imageConfig.Timezone != nil {
|
||||
osc.Timezone = *imageConfig.Timezone
|
||||
}
|
||||
|
||||
if len(ntpServers) > 0 {
|
||||
for _, server := range ntpServers {
|
||||
osc.NTPServers = append(osc.NTPServers, osbuild.ChronyConfigServer{Hostname: server})
|
||||
}
|
||||
} else if imageConfig.TimeSynchronization != nil {
|
||||
osc.NTPServers = imageConfig.TimeSynchronization.Servers
|
||||
osc.LeapSecTZ = imageConfig.TimeSynchronization.LeapsecTz
|
||||
}
|
||||
|
||||
// Relabel the tree, unless the `NoSElinux` flag is explicitly set to `true`
|
||||
if imageConfig.NoSElinux == nil || imageConfig.NoSElinux != nil && !*imageConfig.NoSElinux {
|
||||
osc.SElinux = "targeted"
|
||||
}
|
||||
|
||||
if oscapConfig := c.GetOpenSCAP(); oscapConfig != nil {
|
||||
if t.rpmOstree {
|
||||
panic("unexpected oscap options for ostree image type")
|
||||
}
|
||||
var datastream = oscapConfig.DataStream
|
||||
if datastream == "" {
|
||||
datastream = oscap.DefaultRHEL9Datastream(t.arch.distro.isRHEL())
|
||||
}
|
||||
osc.OpenSCAPConfig = osbuild.NewOscapRemediationStageOptions(
|
||||
osbuild.OscapConfig{
|
||||
Datastream: datastream,
|
||||
ProfileID: oscapConfig.ProfileID,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if t.arch.distro.isRHEL() && options.Facts != nil {
|
||||
osc.FactAPIType = &options.Facts.APIType
|
||||
}
|
||||
|
||||
var err error
|
||||
osc.Directories, err = blueprint.DirectoryCustomizationsToFsNodeDirectories(c.GetDirectories())
|
||||
if err != nil {
|
||||
// In theory this should never happen, because the blueprint directory customizations
|
||||
// should have been validated before this point.
|
||||
panic(fmt.Sprintf("failed to convert directory customizations to fs node directories: %v", err))
|
||||
}
|
||||
|
||||
osc.Files, err = blueprint.FileCustomizationsToFsNodeFiles(c.GetFiles())
|
||||
if err != nil {
|
||||
// In theory this should never happen, because the blueprint file customizations
|
||||
// should have been validated before this point.
|
||||
panic(fmt.Sprintf("failed to convert file customizations to fs node files: %v", err))
|
||||
}
|
||||
|
||||
// set yum repos first, so it doesn't get overridden by
|
||||
// imageConfig.YUMRepos
|
||||
osc.YUMRepos = imageConfig.YUMRepos
|
||||
|
||||
customRepos, err := c.GetRepositories()
|
||||
if err != nil {
|
||||
// This shouldn't happen and since the repos
|
||||
// should have already been validated
|
||||
panic(fmt.Sprintf("failed to get custom repos: %v", err))
|
||||
}
|
||||
|
||||
// This function returns a map of filename and corresponding yum repos
|
||||
// and a list of fs node files for the inline gpg keys so we can save
|
||||
// them to disk. This step also swaps the inline gpg key with the path
|
||||
// to the file in the os file tree
|
||||
yumRepos, gpgKeyFiles, err := blueprint.RepoCustomizationsToRepoConfigAndGPGKeyFiles(customRepos)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to convert inline gpgkeys to fs node files: %v", err))
|
||||
}
|
||||
|
||||
// add the gpg key files to the list of files to be added to the tree
|
||||
if len(gpgKeyFiles) > 0 {
|
||||
osc.Files = append(osc.Files, gpgKeyFiles...)
|
||||
}
|
||||
|
||||
for filename, repos := range yumRepos {
|
||||
osc.YUMRepos = append(osc.YUMRepos, osbuild.NewYumReposStageOptions(filename, repos))
|
||||
}
|
||||
|
||||
osc.ShellInit = imageConfig.ShellInit
|
||||
|
||||
osc.Grub2Config = imageConfig.Grub2Config
|
||||
osc.Sysconfig = imageConfig.Sysconfig
|
||||
osc.SystemdLogind = imageConfig.SystemdLogind
|
||||
osc.CloudInit = imageConfig.CloudInit
|
||||
osc.Modprobe = imageConfig.Modprobe
|
||||
osc.DracutConf = imageConfig.DracutConf
|
||||
osc.SystemdUnit = imageConfig.SystemdUnit
|
||||
osc.Authselect = imageConfig.Authselect
|
||||
osc.SELinuxConfig = imageConfig.SELinuxConfig
|
||||
osc.Tuned = imageConfig.Tuned
|
||||
osc.Tmpfilesd = imageConfig.Tmpfilesd
|
||||
osc.PamLimitsConf = imageConfig.PamLimitsConf
|
||||
osc.Sysctld = imageConfig.Sysctld
|
||||
osc.DNFConfig = imageConfig.DNFConfig
|
||||
osc.DNFAutomaticConfig = imageConfig.DNFAutomaticConfig
|
||||
osc.SshdConfig = imageConfig.SshdConfig
|
||||
osc.AuthConfig = imageConfig.Authconfig
|
||||
osc.PwQuality = imageConfig.PwQuality
|
||||
osc.RHSMConfig = imageConfig.RHSMConfig
|
||||
osc.Subscription = options.Subscription
|
||||
osc.WAAgentConfig = imageConfig.WAAgentConfig
|
||||
osc.UdevRules = imageConfig.UdevRules
|
||||
osc.GCPGuestAgentConfig = imageConfig.GCPGuestAgentConfig
|
||||
|
||||
return osc
|
||||
}
|
||||
|
||||
func liveImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
img := image.NewLiveImage()
|
||||
img.Platform = t.platform
|
||||
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], options, containers, customizations)
|
||||
img.Environment = t.environment
|
||||
img.Workload = workload
|
||||
img.Compression = t.compression
|
||||
// TODO: move generation into LiveImage
|
||||
pt, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img.PartitionTable = pt
|
||||
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func edgeCommitImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
parentCommit, commitRef := makeOSTreeParentCommit(options.OSTree, t.OSTreeRef())
|
||||
img := image.NewOSTreeArchive(commitRef)
|
||||
|
||||
img.Platform = t.platform
|
||||
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], options, containers, customizations)
|
||||
img.Environment = t.environment
|
||||
img.Workload = workload
|
||||
img.OSTreeParent = parentCommit
|
||||
img.OSVersion = t.arch.distro.osVersion
|
||||
img.Filename = t.Filename()
|
||||
|
||||
if !common.VersionLessThan(t.arch.distro.osVersion, "9.2") || t.arch.distro.osVersion == "9-stream" {
|
||||
img.OSCustomizations.EnabledServices = append(img.OSCustomizations.EnabledServices, "ignition-firstboot-complete.service", "coreos-ignition-write-issues.service")
|
||||
}
|
||||
img.Environment = t.environment
|
||||
img.Workload = workload
|
||||
|
||||
img.OSVersion = t.arch.distro.osVersion
|
||||
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func edgeContainerImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
parentCommit, commitRef := makeOSTreeParentCommit(options.OSTree, t.OSTreeRef())
|
||||
img := image.NewOSTreeContainer(commitRef)
|
||||
|
||||
img.Platform = t.platform
|
||||
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], options, containers, customizations)
|
||||
img.ContainerLanguage = img.OSCustomizations.Language
|
||||
img.Environment = t.environment
|
||||
img.Workload = workload
|
||||
img.OSTreeParent = parentCommit
|
||||
img.OSVersion = t.arch.distro.osVersion
|
||||
img.ExtraContainerPackages = packageSets[containerPkgsKey]
|
||||
img.Filename = t.Filename()
|
||||
|
||||
if !common.VersionLessThan(t.arch.distro.osVersion, "9.2") || t.arch.distro.osVersion == "9-stream" {
|
||||
img.OSCustomizations.EnabledServices = append(img.OSCustomizations.EnabledServices, "ignition-firstboot-complete.service", "coreos-ignition-write-issues.service")
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func edgeInstallerImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
d := t.arch.distro
|
||||
|
||||
commit, err := makeOSTreePayloadCommit(options.OSTree, t.OSTreeRef())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", t.Name(), err.Error())
|
||||
}
|
||||
|
||||
img := image.NewAnacondaOSTreeInstaller(commit)
|
||||
|
||||
img.Platform = t.platform
|
||||
img.ExtraBasePackages = packageSets[installerPkgsKey]
|
||||
img.Users = users.UsersFromBP(customizations.GetUsers())
|
||||
img.Groups = users.GroupsFromBP(customizations.GetGroups())
|
||||
|
||||
img.SquashfsCompression = "xz"
|
||||
img.AdditionalDracutModules = []string{
|
||||
"nvdimm", // non-volatile DIMM firmware (provides nfit, cuse, and nd_e820)
|
||||
"prefixdevname",
|
||||
"prefixdevname-tools",
|
||||
}
|
||||
img.AdditionalDrivers = []string{"cuse", "ipmi_devintf", "ipmi_msghandler"}
|
||||
|
||||
if len(img.Users)+len(img.Groups) > 0 {
|
||||
// only enable the users module if needed
|
||||
img.AdditionalAnacondaModules = []string{"org.fedoraproject.Anaconda.Modules.Users"}
|
||||
}
|
||||
|
||||
img.ISOLabelTempl = d.isolabelTmpl
|
||||
img.Product = d.product
|
||||
img.Variant = "edge"
|
||||
img.OSName = "rhel"
|
||||
img.OSVersion = d.osVersion
|
||||
img.Release = fmt.Sprintf("%s %s", d.product, d.osVersion)
|
||||
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func edgeRawImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
commit, err := makeOSTreePayloadCommit(options.OSTree, t.OSTreeRef())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", t.Name(), err.Error())
|
||||
}
|
||||
|
||||
img := image.NewOSTreeRawImage(commit)
|
||||
|
||||
if !common.VersionLessThan(t.arch.distro.osVersion, "9.2") || t.arch.distro.osVersion == "9-stream" {
|
||||
img.Ignition = true
|
||||
}
|
||||
|
||||
img.Users = users.UsersFromBP(customizations.GetUsers())
|
||||
img.Groups = users.GroupsFromBP(customizations.GetGroups())
|
||||
|
||||
// "rw" kernel option is required when /sysroot is mounted read-only to
|
||||
// keep stateful parts of the filesystem writeable (/var/ and /etc)
|
||||
img.KernelOptionsAppend = []string{"modprobe.blacklist=vc4"}
|
||||
img.Keyboard = "us"
|
||||
img.Locale = "C.UTF-8"
|
||||
if !common.VersionLessThan(t.arch.distro.osVersion, "9.2") || t.arch.distro.osVersion == "9-stream" {
|
||||
img.SysrootReadOnly = true
|
||||
img.KernelOptionsAppend = append(img.KernelOptionsAppend, "rw")
|
||||
}
|
||||
|
||||
img.Platform = t.platform
|
||||
img.Workload = workload
|
||||
img.Remote = ostree.Remote{
|
||||
Name: "rhel-edge",
|
||||
URL: options.OSTree.URL,
|
||||
ContentURL: options.OSTree.ContentURL,
|
||||
}
|
||||
img.OSName = "redhat"
|
||||
|
||||
if bpIgnition := customizations.GetIgnition(); bpIgnition != nil && bpIgnition.FirstBoot != nil && bpIgnition.FirstBoot.ProvisioningURL != "" {
|
||||
img.KernelOptionsAppend = append(img.KernelOptionsAppend, "ignition.config.url="+bpIgnition.FirstBoot.ProvisioningURL)
|
||||
}
|
||||
|
||||
// 92+ only
|
||||
if kopts := customizations.GetKernel(); kopts != nil && kopts.Append != "" {
|
||||
img.KernelOptionsAppend = append(img.KernelOptionsAppend, kopts.Append)
|
||||
}
|
||||
|
||||
// TODO: move generation into LiveImage
|
||||
pt, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img.PartitionTable = pt
|
||||
|
||||
img.Filename = t.Filename()
|
||||
img.Compression = t.compression
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func edgeSimplifiedInstallerImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
commit, err := makeOSTreePayloadCommit(options.OSTree, t.OSTreeRef())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", t.Name(), err.Error())
|
||||
}
|
||||
|
||||
rawImg := image.NewOSTreeRawImage(commit)
|
||||
if !common.VersionLessThan(t.arch.distro.osVersion, "9.2") || t.arch.distro.osVersion == "9-stream" {
|
||||
rawImg.Ignition = true
|
||||
}
|
||||
|
||||
rawImg.Users = users.UsersFromBP(customizations.GetUsers())
|
||||
rawImg.Groups = users.GroupsFromBP(customizations.GetGroups())
|
||||
|
||||
// "rw" kernel option is required when /sysroot is mounted read-only to
|
||||
// keep stateful parts of the filesystem writeable (/var/ and /etc)
|
||||
rawImg.KernelOptionsAppend = []string{"modprobe.blacklist=vc4"}
|
||||
rawImg.Keyboard = "us"
|
||||
rawImg.Locale = "C.UTF-8"
|
||||
if !common.VersionLessThan(t.arch.distro.osVersion, "9.2") || t.arch.distro.osVersion == "9-stream" {
|
||||
rawImg.SysrootReadOnly = true
|
||||
rawImg.KernelOptionsAppend = append(rawImg.KernelOptionsAppend, "rw")
|
||||
}
|
||||
|
||||
rawImg.Platform = t.platform
|
||||
rawImg.Workload = workload
|
||||
rawImg.Remote = ostree.Remote{
|
||||
Name: "rhel-edge",
|
||||
URL: options.OSTree.URL,
|
||||
ContentURL: options.OSTree.ContentURL,
|
||||
}
|
||||
rawImg.OSName = "redhat"
|
||||
|
||||
// TODO: move generation into LiveImage
|
||||
pt, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawImg.PartitionTable = pt
|
||||
|
||||
rawImg.Filename = t.Filename()
|
||||
|
||||
if bpIgnition := customizations.GetIgnition(); bpIgnition != nil && bpIgnition.FirstBoot != nil && bpIgnition.FirstBoot.ProvisioningURL != "" {
|
||||
rawImg.KernelOptionsAppend = append(rawImg.KernelOptionsAppend, "ignition.config.url="+bpIgnition.FirstBoot.ProvisioningURL)
|
||||
}
|
||||
|
||||
// 92+ only
|
||||
if kopts := customizations.GetKernel(); kopts != nil && kopts.Append != "" {
|
||||
rawImg.KernelOptionsAppend = append(rawImg.KernelOptionsAppend, kopts.Append)
|
||||
}
|
||||
|
||||
img := image.NewOSTreeSimplifiedInstaller(rawImg, customizations.InstallationDevice)
|
||||
img.ExtraBasePackages = packageSets[installerPkgsKey]
|
||||
// img.Workload = workload
|
||||
img.Platform = t.platform
|
||||
img.Filename = t.Filename()
|
||||
if bpFDO := customizations.GetFDO(); bpFDO != nil {
|
||||
img.FDO = fdo.FromBP(*bpFDO)
|
||||
}
|
||||
// ignition configs from blueprint
|
||||
if bpIgnition := customizations.GetIgnition(); bpIgnition != nil {
|
||||
if bpIgnition.Embedded != nil {
|
||||
var err error
|
||||
img.IgnitionEmbedded, err = ignition.EmbeddedOptionsFromBP(*bpIgnition.Embedded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d := t.arch.distro
|
||||
img.ISOLabelTempl = d.isolabelTmpl
|
||||
img.Product = d.product
|
||||
img.Variant = "edge"
|
||||
img.OSName = "redhat"
|
||||
img.OSVersion = d.osVersion
|
||||
img.AdditionalDracutModules = []string{"prefixdevname", "prefixdevname-tools"}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func imageInstallerImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
img := image.NewAnacondaTarInstaller()
|
||||
|
||||
img.Platform = t.platform
|
||||
img.Workload = workload
|
||||
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], options, containers, customizations)
|
||||
img.ExtraBasePackages = packageSets[installerPkgsKey]
|
||||
img.Users = users.UsersFromBP(customizations.GetUsers())
|
||||
img.Groups = users.GroupsFromBP(customizations.GetGroups())
|
||||
|
||||
img.AdditionalDracutModules = []string{
|
||||
"nvdimm", // non-volatile DIMM firmware (provides nfit, cuse, and nd_e820)
|
||||
"prefixdevname",
|
||||
"prefixdevname-tools",
|
||||
}
|
||||
img.AdditionalDrivers = []string{"cuse", "ipmi_devintf", "ipmi_msghandler"}
|
||||
img.AdditionalAnacondaModules = []string{"org.fedoraproject.Anaconda.Modules.Users"}
|
||||
|
||||
img.SquashfsCompression = "xz"
|
||||
|
||||
// put the kickstart file in the root of the iso
|
||||
img.ISORootKickstart = true
|
||||
|
||||
d := t.arch.distro
|
||||
|
||||
img.ISOLabelTempl = d.isolabelTmpl
|
||||
img.Product = d.product
|
||||
img.OSName = "redhat"
|
||||
img.OSVersion = d.osVersion
|
||||
img.Release = fmt.Sprintf("%s %s", d.product, d.osVersion)
|
||||
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func tarImage(workload workload.Workload,
|
||||
t *imageType,
|
||||
customizations *blueprint.Customizations,
|
||||
options distro.ImageOptions,
|
||||
packageSets map[string]rpmmd.PackageSet,
|
||||
containers []container.SourceSpec,
|
||||
rng *rand.Rand) (image.ImageKind, error) {
|
||||
|
||||
img := image.NewArchive()
|
||||
img.Platform = t.platform
|
||||
img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], options, containers, customizations)
|
||||
img.Environment = t.environment
|
||||
img.Workload = workload
|
||||
|
||||
img.Filename = t.Filename()
|
||||
|
||||
return img, nil
|
||||
|
||||
}
|
||||
|
||||
// Create an ostree SourceSpec to define an ostree parent commit using the user
|
||||
// options and the default ref for the image type. Additionally returns the
|
||||
// ref to be used for the new commit to be created.
|
||||
func makeOSTreeParentCommit(options *ostree.ImageOptions, defaultRef string) (*ostree.SourceSpec, string) {
|
||||
commitRef := defaultRef
|
||||
if options == nil {
|
||||
// nothing to do
|
||||
return nil, commitRef
|
||||
}
|
||||
if options.ImageRef != "" {
|
||||
// user option overrides default commit ref
|
||||
commitRef = options.ImageRef
|
||||
}
|
||||
|
||||
var parentCommit *ostree.SourceSpec
|
||||
if options.URL == "" {
|
||||
// no parent
|
||||
return nil, commitRef
|
||||
}
|
||||
|
||||
// ostree URL specified: set source spec for parent commit
|
||||
parentRef := options.ParentRef
|
||||
if parentRef == "" {
|
||||
// parent ref not set: use image ref
|
||||
parentRef = commitRef
|
||||
|
||||
}
|
||||
parentCommit = &ostree.SourceSpec{
|
||||
URL: options.URL,
|
||||
Ref: parentRef,
|
||||
RHSM: options.RHSM,
|
||||
}
|
||||
return parentCommit, commitRef
|
||||
}
|
||||
|
||||
// Create an ostree SourceSpec to define an ostree payload using the user options and the default ref for the image type.
|
||||
func makeOSTreePayloadCommit(options *ostree.ImageOptions, defaultRef string) (ostree.SourceSpec, error) {
|
||||
if options == nil || options.URL == "" {
|
||||
// this should be caught by checkOptions() in distro, but it's good
|
||||
// to guard against it here as well
|
||||
return ostree.SourceSpec{}, fmt.Errorf("ostree commit URL required")
|
||||
}
|
||||
|
||||
commitRef := defaultRef
|
||||
if options.ImageRef != "" {
|
||||
// user option overrides default commit ref
|
||||
commitRef = options.ImageRef
|
||||
}
|
||||
|
||||
return ostree.SourceSpec{
|
||||
URL: options.URL,
|
||||
Ref: commitRef,
|
||||
RHSM: options.RHSM,
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -1,429 +0,0 @@
|
|||
package rhel9
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/container"
|
||||
"github.com/osbuild/osbuild-composer/internal/disk"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/environment"
|
||||
"github.com/osbuild/osbuild-composer/internal/image"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/oscap"
|
||||
"github.com/osbuild/osbuild-composer/internal/ostree"
|
||||
"github.com/osbuild/osbuild-composer/internal/pathpolicy"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/workload"
|
||||
)
|
||||
|
||||
const (
|
||||
// package set names
|
||||
|
||||
// build package set name
|
||||
buildPkgsKey = "build"
|
||||
|
||||
// main/common os image package set name
|
||||
osPkgsKey = "os"
|
||||
|
||||
// container package set name
|
||||
containerPkgsKey = "container"
|
||||
|
||||
// installer package set name
|
||||
installerPkgsKey = "installer"
|
||||
|
||||
// blueprint package set name
|
||||
blueprintPkgsKey = "blueprint"
|
||||
)
|
||||
|
||||
type imageFunc func(workload workload.Workload, t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, packageSets map[string]rpmmd.PackageSet, containers []container.SourceSpec, rng *rand.Rand) (image.ImageKind, error)
|
||||
|
||||
type packageSetFunc func(t *imageType) rpmmd.PackageSet
|
||||
|
||||
type imageType struct {
|
||||
arch *architecture
|
||||
platform platform.Platform
|
||||
environment environment.Environment
|
||||
workload workload.Workload
|
||||
name string
|
||||
nameAliases []string
|
||||
filename string
|
||||
compression string // TODO: remove from image definition and make it a transport option
|
||||
mimeType string
|
||||
packageSets map[string]packageSetFunc
|
||||
defaultImageConfig *distro.ImageConfig
|
||||
kernelOptions string
|
||||
defaultSize uint64
|
||||
buildPipelines []string
|
||||
payloadPipelines []string
|
||||
exports []string
|
||||
image imageFunc
|
||||
|
||||
// bootISO: installable ISO
|
||||
bootISO bool
|
||||
// rpmOstree: edge/ostree
|
||||
rpmOstree bool
|
||||
// bootable image
|
||||
bootable bool
|
||||
// List of valid arches for the image type
|
||||
basePartitionTables distro.BasePartitionTableMap
|
||||
}
|
||||
|
||||
func (t *imageType) Name() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *imageType) Arch() distro.Arch {
|
||||
return t.arch
|
||||
}
|
||||
|
||||
func (t *imageType) Filename() string {
|
||||
return t.filename
|
||||
}
|
||||
|
||||
func (t *imageType) MIMEType() string {
|
||||
return t.mimeType
|
||||
}
|
||||
|
||||
func (t *imageType) OSTreeRef() string {
|
||||
d := t.arch.distro
|
||||
if t.rpmOstree {
|
||||
return fmt.Sprintf(d.ostreeRefTmpl, t.Arch().Name())
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t *imageType) Size(size uint64) uint64 {
|
||||
// Microsoft Azure requires vhd images to be rounded up to the nearest MB
|
||||
if t.name == "vhd" && size%common.MebiByte != 0 {
|
||||
size = (size/common.MebiByte + 1) * common.MebiByte
|
||||
}
|
||||
if size == 0 {
|
||||
size = t.defaultSize
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func (t *imageType) BuildPipelines() []string {
|
||||
return t.buildPipelines
|
||||
}
|
||||
|
||||
func (t *imageType) PayloadPipelines() []string {
|
||||
return t.payloadPipelines
|
||||
}
|
||||
|
||||
func (t *imageType) PayloadPackageSets() []string {
|
||||
return []string{blueprintPkgsKey}
|
||||
}
|
||||
|
||||
func (t *imageType) PackageSetsChains() map[string][]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *imageType) Exports() []string {
|
||||
if len(t.exports) > 0 {
|
||||
return t.exports
|
||||
}
|
||||
return []string{"assembler"}
|
||||
}
|
||||
|
||||
func (t *imageType) BootMode() distro.BootMode {
|
||||
if t.platform.GetUEFIVendor() != "" && t.platform.GetBIOSPlatform() != "" {
|
||||
return distro.BOOT_HYBRID
|
||||
} else if t.platform.GetUEFIVendor() != "" {
|
||||
return distro.BOOT_UEFI
|
||||
} else if t.platform.GetBIOSPlatform() != "" || t.platform.GetZiplSupport() {
|
||||
return distro.BOOT_LEGACY
|
||||
}
|
||||
return distro.BOOT_NONE
|
||||
}
|
||||
|
||||
func (t *imageType) getPartitionTable(
|
||||
mountpoints []blueprint.FilesystemCustomization,
|
||||
options distro.ImageOptions,
|
||||
rng *rand.Rand,
|
||||
) (*disk.PartitionTable, error) {
|
||||
archName := t.arch.Name()
|
||||
|
||||
basePartitionTable, exists := t.basePartitionTables[archName]
|
||||
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("no partition table defined for architecture %q for image type %q", archName, t.Name())
|
||||
}
|
||||
|
||||
imageSize := t.Size(options.Size)
|
||||
|
||||
lvmify := !t.rpmOstree
|
||||
|
||||
return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, lvmify, nil, rng)
|
||||
}
|
||||
|
||||
func (t *imageType) getDefaultImageConfig() *distro.ImageConfig {
|
||||
// ensure that image always returns non-nil default config
|
||||
imageConfig := t.defaultImageConfig
|
||||
if imageConfig == nil {
|
||||
imageConfig = &distro.ImageConfig{}
|
||||
}
|
||||
return imageConfig.InheritFrom(t.arch.distro.getDefaultImageConfig())
|
||||
|
||||
}
|
||||
|
||||
func (t *imageType) PartitionType() string {
|
||||
archName := t.arch.Name()
|
||||
basePartitionTable, exists := t.basePartitionTables[archName]
|
||||
if !exists {
|
||||
return ""
|
||||
}
|
||||
|
||||
return basePartitionTable.Type
|
||||
}
|
||||
|
||||
func (t *imageType) Manifest(bp *blueprint.Blueprint,
|
||||
options distro.ImageOptions,
|
||||
repos []rpmmd.RepoConfig,
|
||||
seed int64) (*manifest.Manifest, []string, error) {
|
||||
|
||||
warnings, err := t.checkOptions(bp, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// merge package sets that appear in the image type with the package sets
|
||||
// of the same name from the distro and arch
|
||||
staticPackageSets := make(map[string]rpmmd.PackageSet)
|
||||
|
||||
for name, getter := range t.packageSets {
|
||||
staticPackageSets[name] = getter(t)
|
||||
}
|
||||
|
||||
// amend with repository information and collect payload repos
|
||||
payloadRepos := make([]rpmmd.RepoConfig, 0)
|
||||
for _, repo := range repos {
|
||||
if len(repo.PackageSets) > 0 {
|
||||
// only apply the repo to the listed package sets
|
||||
for _, psName := range repo.PackageSets {
|
||||
if slices.Contains(t.PayloadPackageSets(), psName) {
|
||||
payloadRepos = append(payloadRepos, repo)
|
||||
}
|
||||
ps := staticPackageSets[psName]
|
||||
ps.Repositories = append(ps.Repositories, repo)
|
||||
staticPackageSets[psName] = ps
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w := t.workload
|
||||
if w == nil {
|
||||
cw := &workload.Custom{
|
||||
BaseWorkload: workload.BaseWorkload{
|
||||
Repos: payloadRepos,
|
||||
},
|
||||
Packages: bp.GetPackagesEx(false),
|
||||
}
|
||||
if services := bp.Customizations.GetServices(); services != nil {
|
||||
cw.Services = services.Enabled
|
||||
cw.DisabledServices = services.Disabled
|
||||
}
|
||||
w = cw
|
||||
}
|
||||
|
||||
containerSources := make([]container.SourceSpec, len(bp.Containers))
|
||||
for idx := range bp.Containers {
|
||||
containerSources[idx] = container.SourceSpec(bp.Containers[idx])
|
||||
}
|
||||
|
||||
source := rand.NewSource(seed)
|
||||
// math/rand is good enough in this case
|
||||
/* #nosec G404 */
|
||||
rng := rand.New(source)
|
||||
|
||||
img, err := t.image(w, t, bp.Customizations, options, staticPackageSets, containerSources, rng)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
mf := manifest.New()
|
||||
mf.Distro = manifest.DISTRO_EL9
|
||||
_, err = img.InstantiateManifest(&mf, repos, t.arch.distro.runner, rng)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &mf, warnings, err
|
||||
}
|
||||
|
||||
// checkOptions checks the validity and compatibility of options and customizations for the image type.
|
||||
// Returns ([]string, error) where []string, if non-nil, will hold any generated warnings (e.g. deprecation notices).
|
||||
func (t *imageType) checkOptions(bp *blueprint.Blueprint, options distro.ImageOptions) ([]string, error) {
|
||||
|
||||
customizations := bp.Customizations
|
||||
|
||||
// holds warnings (e.g. deprecation notices)
|
||||
var warnings []string
|
||||
if t.workload != nil {
|
||||
// For now, if an image type defines its own workload, don't allow any
|
||||
// user customizations.
|
||||
// Soon we will have more workflows and each will define its allowed
|
||||
// set of customizations. The current set of customizations defined in
|
||||
// the blueprint spec corresponds to the Custom workflow.
|
||||
if customizations != nil {
|
||||
return warnings, fmt.Errorf("image type %q does not support customizations", t.name)
|
||||
}
|
||||
}
|
||||
|
||||
// we do not support embedding containers on ostree-derived images, only on commits themselves
|
||||
if len(bp.Containers) > 0 && t.rpmOstree && (t.name != "edge-commit" && t.name != "edge-container") {
|
||||
return warnings, fmt.Errorf("embedding containers is not supported for %s on %s", t.name, t.arch.distro.name)
|
||||
}
|
||||
|
||||
ostreeURL := ""
|
||||
if options.OSTree != nil {
|
||||
if options.OSTree.ParentRef != "" && options.OSTree.URL == "" {
|
||||
// specifying parent ref also requires URL
|
||||
return nil, ostree.NewParameterComboError("ostree parent ref specified, but no URL to retrieve it")
|
||||
}
|
||||
ostreeURL = options.OSTree.URL
|
||||
}
|
||||
|
||||
if t.bootISO && t.rpmOstree {
|
||||
// ostree-based ISOs require a URL from which to pull a payload commit
|
||||
if ostreeURL == "" {
|
||||
return warnings, fmt.Errorf("boot ISO image type %q requires specifying a URL from which to retrieve the OSTree commit", t.name)
|
||||
}
|
||||
|
||||
if t.name == "edge-simplified-installer" {
|
||||
allowed := []string{"InstallationDevice", "FDO", "Ignition", "Kernel", "User", "Group"}
|
||||
if err := customizations.CheckAllowed(allowed...); err != nil {
|
||||
return warnings, fmt.Errorf("unsupported blueprint customizations found for boot ISO image type %q: (allowed: %s)", t.name, strings.Join(allowed, ", "))
|
||||
}
|
||||
if customizations.GetInstallationDevice() == "" {
|
||||
return warnings, fmt.Errorf("boot ISO image type %q requires specifying an installation device to install to", t.name)
|
||||
}
|
||||
|
||||
// FDO is optional, but when specified has some restrictions
|
||||
if customizations.GetFDO() != nil {
|
||||
if customizations.GetFDO().ManufacturingServerURL == "" {
|
||||
return warnings, fmt.Errorf("boot ISO image type %q requires specifying FDO.ManufacturingServerURL configuration to install to when using FDO", t.name)
|
||||
}
|
||||
var diunSet int
|
||||
if customizations.GetFDO().DiunPubKeyHash != "" {
|
||||
diunSet++
|
||||
}
|
||||
if customizations.GetFDO().DiunPubKeyInsecure != "" {
|
||||
diunSet++
|
||||
}
|
||||
if customizations.GetFDO().DiunPubKeyRootCerts != "" {
|
||||
diunSet++
|
||||
}
|
||||
if diunSet != 1 {
|
||||
return warnings, fmt.Errorf("boot ISO image type %q requires specifying one of [FDO.DiunPubKeyHash,FDO.DiunPubKeyInsecure,FDO.DiunPubKeyRootCerts] configuration to install to when using FDO", t.name)
|
||||
}
|
||||
}
|
||||
|
||||
// ignition is optional, we might be using FDO
|
||||
if customizations.GetIgnition() != nil {
|
||||
if customizations.GetIgnition().Embedded != nil && customizations.GetIgnition().FirstBoot != nil {
|
||||
return warnings, fmt.Errorf("both ignition embedded and firstboot configurations found")
|
||||
}
|
||||
if customizations.GetIgnition().FirstBoot != nil && customizations.GetIgnition().FirstBoot.ProvisioningURL == "" {
|
||||
return warnings, fmt.Errorf("ignition.firstboot requires a provisioning url")
|
||||
}
|
||||
}
|
||||
} else if t.name == "edge-installer" {
|
||||
allowed := []string{"User", "Group"}
|
||||
if err := customizations.CheckAllowed(allowed...); err != nil {
|
||||
return warnings, fmt.Errorf("unsupported blueprint customizations found for boot ISO image type %q: (allowed: %s)", t.name, strings.Join(allowed, ", "))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if t.name == "edge-raw-image" || t.name == "edge-ami" {
|
||||
// ostree-based bootable images require a URL from which to pull a payload commit
|
||||
if ostreeURL == "" {
|
||||
return warnings, fmt.Errorf("%q images require specifying a URL from which to retrieve the OSTree commit", t.name)
|
||||
}
|
||||
|
||||
allowed := []string{"Ignition", "Kernel", "User", "Group"}
|
||||
if err := customizations.CheckAllowed(allowed...); err != nil {
|
||||
return warnings, fmt.Errorf("unsupported blueprint customizations found for image type %q: (allowed: %s)", t.name, strings.Join(allowed, ", "))
|
||||
}
|
||||
// TODO: consider additional checks, such as those in "edge-simplified-installer"
|
||||
}
|
||||
|
||||
// warn that user & group customizations on edge-commit, edge-container are deprecated
|
||||
// TODO(edge): directly error if these options are provided when rhel-9.5's time arrives
|
||||
if t.name == "edge-commit" || t.name == "edge-container" {
|
||||
if customizations.GetUsers() != nil {
|
||||
w := fmt.Sprintf("Please note that user customizations on %q image type are deprecated and will be removed in the near future\n", t.name)
|
||||
log.Print(w)
|
||||
warnings = append(warnings, w)
|
||||
}
|
||||
if customizations.GetGroups() != nil {
|
||||
w := fmt.Sprintf("Please note that group customizations on %q image type are deprecated and will be removed in the near future\n", t.name)
|
||||
log.Print(w)
|
||||
warnings = append(warnings, w)
|
||||
}
|
||||
}
|
||||
|
||||
if kernelOpts := customizations.GetKernel(); kernelOpts.Append != "" && t.rpmOstree && t.name != "edge-raw-image" && t.name != "edge-simplified-installer" {
|
||||
return warnings, fmt.Errorf("kernel boot parameter customizations are not supported for ostree types")
|
||||
}
|
||||
|
||||
mountpoints := customizations.GetFilesystems()
|
||||
|
||||
if mountpoints != nil && t.rpmOstree {
|
||||
return warnings, fmt.Errorf("Custom mountpoints are not supported for ostree types")
|
||||
}
|
||||
|
||||
err := blueprint.CheckMountpointsPolicy(mountpoints, pathpolicy.MountpointPolicies)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
if osc := customizations.GetOpenSCAP(); osc != nil {
|
||||
if t.arch.distro.osVersion == "9.0" {
|
||||
return warnings, fmt.Errorf(fmt.Sprintf("OpenSCAP unsupported os version: %s", t.arch.distro.osVersion))
|
||||
}
|
||||
if !oscap.IsProfileAllowed(osc.ProfileID, oscapProfileAllowList) {
|
||||
return warnings, fmt.Errorf(fmt.Sprintf("OpenSCAP unsupported profile: %s", osc.ProfileID))
|
||||
}
|
||||
if t.rpmOstree {
|
||||
return warnings, fmt.Errorf("OpenSCAP customizations are not supported for ostree types")
|
||||
}
|
||||
if osc.ProfileID == "" {
|
||||
return warnings, fmt.Errorf("OpenSCAP profile cannot be empty")
|
||||
}
|
||||
}
|
||||
|
||||
// Check Directory/File Customizations are valid
|
||||
dc := customizations.GetDirectories()
|
||||
fc := customizations.GetFiles()
|
||||
|
||||
err = blueprint.ValidateDirFileCustomizations(dc, fc)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
err = blueprint.CheckDirectoryCustomizationsPolicy(dc, pathpolicy.CustomDirectoriesPolicies)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
err = blueprint.CheckFileCustomizationsPolicy(fc, pathpolicy.CustomFilesPolicies)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
// check if repository customizations are valid
|
||||
_, err = customizations.GetRepositories()
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
return warnings, nil
|
||||
}
|
||||
|
|
@ -1,247 +0,0 @@
|
|||
package rhel9
|
||||
|
||||
// This file defines package sets that are used by more than one image type.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
// BUILD PACKAGE SETS
|
||||
|
||||
// distro-wide build package set
|
||||
func distroBuildPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"dnf",
|
||||
"dosfstools",
|
||||
"e2fsprogs",
|
||||
"glibc",
|
||||
"lorax-templates-generic",
|
||||
"lorax-templates-rhel",
|
||||
"lvm2",
|
||||
"policycoreutils",
|
||||
"python3-iniparse",
|
||||
"qemu-img",
|
||||
"selinux-policy-targeted",
|
||||
"systemd",
|
||||
"tar",
|
||||
"xfsprogs",
|
||||
"xz",
|
||||
},
|
||||
}
|
||||
|
||||
switch t.arch.Name() {
|
||||
|
||||
case platform.ARCH_X86_64.String():
|
||||
ps = ps.Append(x8664BuildPackageSet(t))
|
||||
|
||||
case platform.ARCH_PPC64LE.String():
|
||||
ps = ps.Append(ppc64leBuildPackageSet(t))
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
// x86_64 build package set
|
||||
func x8664BuildPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"grub2-pc",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ppc64le build package set
|
||||
func ppc64leBuildPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"grub2-ppc64le",
|
||||
"grub2-ppc64le-modules",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// installer boot package sets, needed for booting and
|
||||
// also in the build host
|
||||
func anacondaBootPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{}
|
||||
|
||||
grubCommon := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"grub2-tools",
|
||||
"grub2-tools-extra",
|
||||
"grub2-tools-minimal",
|
||||
},
|
||||
}
|
||||
|
||||
efiCommon := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"efibootmgr",
|
||||
},
|
||||
}
|
||||
|
||||
switch t.arch.Name() {
|
||||
case platform.ARCH_X86_64.String():
|
||||
ps = ps.Append(grubCommon)
|
||||
ps = ps.Append(efiCommon)
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"grub2-efi-x64",
|
||||
"grub2-efi-x64-cdboot",
|
||||
"grub2-pc",
|
||||
"grub2-pc-modules",
|
||||
"shim-x64",
|
||||
"syslinux",
|
||||
"syslinux-nonlinux",
|
||||
},
|
||||
})
|
||||
case platform.ARCH_AARCH64.String():
|
||||
ps = ps.Append(grubCommon)
|
||||
ps = ps.Append(efiCommon)
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"grub2-efi-aa64-cdboot",
|
||||
"grub2-efi-aa64",
|
||||
"shim-aa64",
|
||||
},
|
||||
})
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported arch: %s", t.arch.Name()))
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
// OS package sets
|
||||
|
||||
// Replacement of the previously used @core package group
|
||||
func coreOsCommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"audit",
|
||||
"basesystem",
|
||||
"bash",
|
||||
"coreutils",
|
||||
"cronie",
|
||||
"crypto-policies",
|
||||
"crypto-policies-scripts",
|
||||
"curl",
|
||||
"dnf",
|
||||
"yum",
|
||||
"e2fsprogs",
|
||||
"filesystem",
|
||||
"glibc",
|
||||
"grubby",
|
||||
"hostname",
|
||||
"iproute",
|
||||
"iproute-tc",
|
||||
"iputils",
|
||||
"kbd",
|
||||
"kexec-tools",
|
||||
"less",
|
||||
"logrotate",
|
||||
"man-db",
|
||||
"ncurses",
|
||||
"openssh-clients",
|
||||
"openssh-server",
|
||||
"p11-kit",
|
||||
"parted",
|
||||
"passwd",
|
||||
"policycoreutils",
|
||||
"procps-ng",
|
||||
"rootfiles",
|
||||
"rpm",
|
||||
"rpm-plugin-audit",
|
||||
"rsyslog",
|
||||
"selinux-policy-targeted",
|
||||
"setup",
|
||||
"shadow-utils",
|
||||
"sssd-common",
|
||||
"sssd-kcm",
|
||||
"sudo",
|
||||
"systemd",
|
||||
"tuned",
|
||||
"util-linux",
|
||||
"vim-minimal",
|
||||
"xfsprogs",
|
||||
"authselect",
|
||||
"prefixdevname",
|
||||
"dnf-plugins-core",
|
||||
"NetworkManager",
|
||||
"NetworkManager-team",
|
||||
"NetworkManager-tui",
|
||||
"libsysfs",
|
||||
"linux-firmware",
|
||||
"lshw",
|
||||
"lsscsi",
|
||||
"kernel-tools",
|
||||
"sg3_utils",
|
||||
"sg3_utils-libs",
|
||||
"python3-libselinux",
|
||||
},
|
||||
}
|
||||
|
||||
// Do not include this in the distroSpecificPackageSet for now,
|
||||
// because it includes 'insights-client' which is not installed
|
||||
// by default on all RHEL images (although it would probably make sense).
|
||||
if t.arch.distro.isRHEL() {
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"subscription-manager",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
switch t.arch.Name() {
|
||||
case platform.ARCH_X86_64.String():
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"irqbalance",
|
||||
"microcode_ctl",
|
||||
},
|
||||
})
|
||||
|
||||
case platform.ARCH_AARCH64.String():
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"irqbalance",
|
||||
},
|
||||
})
|
||||
|
||||
case platform.ARCH_PPC64LE.String():
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"irqbalance",
|
||||
"opal-prd",
|
||||
"ppc64-diag-rtas",
|
||||
"powerpc-utils-core",
|
||||
"lsvpd",
|
||||
},
|
||||
})
|
||||
|
||||
case platform.ARCH_S390X.String():
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"s390utils-core",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
// packages that are only in some (sub)-distributions
|
||||
func distroSpecificPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
if t.arch.distro.isRHEL() {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"insights-client",
|
||||
},
|
||||
}
|
||||
}
|
||||
return rpmmd.PackageSet{}
|
||||
}
|
||||
|
|
@ -1,169 +0,0 @@
|
|||
package rhel9
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/disk"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
)
|
||||
|
||||
var defaultBasePartitionTables = distro.BasePartitionTableMap{
|
||||
platform.ARCH_X86_64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 1 * common.MebiByte, // 1MB
|
||||
Bootable: true,
|
||||
Type: disk.BIOSBootPartitionGUID,
|
||||
UUID: disk.BIOSBootPartitionUUID,
|
||||
},
|
||||
{
|
||||
Size: 200 * common.MebiByte, // 200 MB
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
Label: "EFI-SYSTEM",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 500 * common.MebiByte, // 500 MB
|
||||
Type: disk.XBootLDRPartitionGUID,
|
||||
UUID: disk.FilesystemDataUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/boot",
|
||||
Label: "boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte, // 2GiB
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
platform.ARCH_AARCH64.String(): disk.PartitionTable{
|
||||
UUID: "D209C89E-EA5E-4FBD-B161-B461CCE297E0",
|
||||
Type: "gpt",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 200 * common.MebiByte, // 200 MB
|
||||
Type: disk.EFISystemPartitionGUID,
|
||||
UUID: disk.EFISystemPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
UUID: disk.EFIFilesystemUUID,
|
||||
Mountpoint: "/boot/efi",
|
||||
Label: "EFI-SYSTEM",
|
||||
FSTabOptions: "defaults,uid=0,gid=0,umask=077,shortname=winnt",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 500 * common.MebiByte, // 500 MB
|
||||
Type: disk.XBootLDRPartitionGUID,
|
||||
UUID: disk.FilesystemDataUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/boot",
|
||||
Label: "boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte, // 2GiB
|
||||
Type: disk.FilesystemDataGUID,
|
||||
UUID: disk.RootPartitionUUID,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Label: "root",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
platform.ARCH_PPC64LE.String(): disk.PartitionTable{
|
||||
UUID: "0x14fc63d2",
|
||||
Type: "dos",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 4 * common.MebiByte,
|
||||
Type: "41",
|
||||
Bootable: true,
|
||||
},
|
||||
{
|
||||
Size: 500 * common.MebiByte, // 500 MB
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/boot",
|
||||
Label: "boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte, // 2GiB
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
platform.ARCH_S390X.String(): disk.PartitionTable{
|
||||
UUID: "0x14fc63d2",
|
||||
Type: "dos",
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Size: 500 * common.MebiByte, // 500 MB
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/boot",
|
||||
Label: "boot",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Size: 2 * common.GibiByte, // 2GiB
|
||||
Bootable: true,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "xfs",
|
||||
Mountpoint: "/",
|
||||
FSTabOptions: "defaults",
|
||||
FSTabFreq: 0,
|
||||
FSTabPassNo: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
package rhel9
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/subscription"
|
||||
)
|
||||
|
||||
var (
|
||||
openstackImgType = imageType{
|
||||
name: "openstack",
|
||||
filename: "disk.qcow2",
|
||||
mimeType: "application/x-qemu-disk",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: openstackCommonPackageSet,
|
||||
},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
},
|
||||
kernelOptions: "ro net.ifnames=0",
|
||||
bootable: true,
|
||||
defaultSize: 4 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "qcow2"},
|
||||
exports: []string{"qcow2"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
)
|
||||
|
||||
func qcow2CommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"authselect-compat",
|
||||
"chrony",
|
||||
"cloud-init",
|
||||
"cloud-utils-growpart",
|
||||
"cockpit-system",
|
||||
"cockpit-ws",
|
||||
"dnf-utils",
|
||||
"dosfstools",
|
||||
"nfs-utils",
|
||||
"oddjob",
|
||||
"oddjob-mkhomedir",
|
||||
"psmisc",
|
||||
"python3-jsonschema",
|
||||
"qemu-guest-agent",
|
||||
"redhat-release",
|
||||
"redhat-release-eula",
|
||||
"rsync",
|
||||
"tar",
|
||||
"tcpdump",
|
||||
},
|
||||
Exclude: []string{
|
||||
"aic94xx-firmware",
|
||||
"alsa-firmware",
|
||||
"alsa-lib",
|
||||
"alsa-tools-firmware",
|
||||
"biosdevname",
|
||||
"dnf-plugin-spacewalk",
|
||||
"fedora-release",
|
||||
"fedora-repos",
|
||||
"iprutils",
|
||||
"ivtv-firmware",
|
||||
"langpacks-*",
|
||||
"langpacks-en",
|
||||
"libertas-sd8787-firmware",
|
||||
"nss",
|
||||
"plymouth",
|
||||
"rng-tools",
|
||||
"udisks2",
|
||||
},
|
||||
}.Append(coreOsCommonPackageSet(t)).Append(distroSpecificPackageSet(t))
|
||||
|
||||
// Ensure to not pull in subscription-manager on non-RHEL distro
|
||||
if t.arch.distro.isRHEL() {
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"subscription-manager-cockpit",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
func openstackCommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
// Defaults
|
||||
"langpacks-en",
|
||||
"firewalld",
|
||||
|
||||
// From the lorax kickstart
|
||||
"cloud-init",
|
||||
"qemu-guest-agent",
|
||||
"spice-vdagent",
|
||||
},
|
||||
Exclude: []string{
|
||||
"rng-tools",
|
||||
},
|
||||
}.Append(coreOsCommonPackageSet(t))
|
||||
|
||||
if t.arch.Name() == platform.ARCH_X86_64.String() {
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
// packages below used to come from @core group and were not excluded
|
||||
// they may not be needed at all, but kept them here to not need
|
||||
// to exclude them instead in all other images
|
||||
"iwl100-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl1000-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6000g2a-firmware",
|
||||
"iwl6050-firmware",
|
||||
"iwl7260-firmware",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
func qcowImageConfig(d distribution) *distro.ImageConfig {
|
||||
ic := &distro.ImageConfig{
|
||||
DefaultTarget: common.ToPtr("multi-user.target"),
|
||||
}
|
||||
if d.isRHEL() {
|
||||
ic.RHSMConfig = map[subscription.RHSMStatus]*osbuild.RHSMStageOptions{
|
||||
subscription.RHSMConfigNoSubscription: {
|
||||
DnfPlugins: &osbuild.RHSMStageOptionsDnfPlugins{
|
||||
ProductID: &osbuild.RHSMStageOptionsDnfPlugin{
|
||||
Enabled: false,
|
||||
},
|
||||
SubscriptionManager: &osbuild.RHSMStageOptionsDnfPlugin{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
return ic
|
||||
}
|
||||
|
||||
func mkQcow2ImgType(d distribution) imageType {
|
||||
it := imageType{
|
||||
name: "qcow2",
|
||||
filename: "disk.qcow2",
|
||||
mimeType: "application/x-qemu-disk",
|
||||
kernelOptions: "console=tty0 console=ttyS0,115200n8 no_timer_check net.ifnames=0",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: qcow2CommonPackageSet,
|
||||
},
|
||||
bootable: true,
|
||||
defaultSize: 10 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "qcow2"},
|
||||
exports: []string{"qcow2"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
it.defaultImageConfig = qcowImageConfig(d)
|
||||
return it
|
||||
}
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
package rhel9
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
// sapImageConfig returns the SAP specific ImageConfig data
|
||||
func sapImageConfig(osVersion string) *distro.ImageConfig {
|
||||
return &distro.ImageConfig{
|
||||
SELinuxConfig: &osbuild.SELinuxConfigStageOptions{
|
||||
State: osbuild.SELinuxStatePermissive,
|
||||
},
|
||||
// RHBZ#1960617
|
||||
Tuned: osbuild.NewTunedStageOptions("sap-hana"),
|
||||
// RHBZ#1959979
|
||||
Tmpfilesd: []*osbuild.TmpfilesdStageOptions{
|
||||
osbuild.NewTmpfilesdStageOptions("sap.conf",
|
||||
[]osbuild.TmpfilesdConfigLine{
|
||||
{
|
||||
Type: "x",
|
||||
Path: "/tmp/.sap*",
|
||||
},
|
||||
{
|
||||
Type: "x",
|
||||
Path: "/tmp/.hdb*lock",
|
||||
},
|
||||
{
|
||||
Type: "x",
|
||||
Path: "/tmp/.trex*lock",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
// RHBZ#1959963
|
||||
PamLimitsConf: []*osbuild.PamLimitsConfStageOptions{
|
||||
osbuild.NewPamLimitsConfStageOptions("99-sap.conf",
|
||||
[]osbuild.PamLimitsConfigLine{
|
||||
{
|
||||
Domain: "@sapsys",
|
||||
Type: osbuild.PamLimitsTypeHard,
|
||||
Item: osbuild.PamLimitsItemNofile,
|
||||
Value: osbuild.PamLimitsValueInt(1048576),
|
||||
},
|
||||
{
|
||||
Domain: "@sapsys",
|
||||
Type: osbuild.PamLimitsTypeSoft,
|
||||
Item: osbuild.PamLimitsItemNofile,
|
||||
Value: osbuild.PamLimitsValueInt(1048576),
|
||||
},
|
||||
{
|
||||
Domain: "@dba",
|
||||
Type: osbuild.PamLimitsTypeHard,
|
||||
Item: osbuild.PamLimitsItemNofile,
|
||||
Value: osbuild.PamLimitsValueInt(1048576),
|
||||
},
|
||||
{
|
||||
Domain: "@dba",
|
||||
Type: osbuild.PamLimitsTypeSoft,
|
||||
Item: osbuild.PamLimitsItemNofile,
|
||||
Value: osbuild.PamLimitsValueInt(1048576),
|
||||
},
|
||||
{
|
||||
Domain: "@sapsys",
|
||||
Type: osbuild.PamLimitsTypeHard,
|
||||
Item: osbuild.PamLimitsItemNproc,
|
||||
Value: osbuild.PamLimitsValueUnlimited,
|
||||
},
|
||||
{
|
||||
Domain: "@sapsys",
|
||||
Type: osbuild.PamLimitsTypeSoft,
|
||||
Item: osbuild.PamLimitsItemNproc,
|
||||
Value: osbuild.PamLimitsValueUnlimited,
|
||||
},
|
||||
{
|
||||
Domain: "@dba",
|
||||
Type: osbuild.PamLimitsTypeHard,
|
||||
Item: osbuild.PamLimitsItemNproc,
|
||||
Value: osbuild.PamLimitsValueUnlimited,
|
||||
},
|
||||
{
|
||||
Domain: "@dba",
|
||||
Type: osbuild.PamLimitsTypeSoft,
|
||||
Item: osbuild.PamLimitsItemNproc,
|
||||
Value: osbuild.PamLimitsValueUnlimited,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
// RHBZ#1959962
|
||||
Sysctld: []*osbuild.SysctldStageOptions{
|
||||
osbuild.NewSysctldStageOptions("sap.conf",
|
||||
[]osbuild.SysctldConfigLine{
|
||||
{
|
||||
Key: "kernel.pid_max",
|
||||
Value: "4194304",
|
||||
},
|
||||
{
|
||||
Key: "vm.max_map_count",
|
||||
Value: "2147483647",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
// E4S/EUS
|
||||
DNFConfig: []*osbuild.DNFConfigStageOptions{
|
||||
osbuild.NewDNFConfigStageOptions(
|
||||
[]osbuild.DNFVariable{
|
||||
{
|
||||
Name: "releasever",
|
||||
Value: osVersion,
|
||||
},
|
||||
},
|
||||
nil,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func SapPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
return rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
// RHBZ#2076763
|
||||
"@Server",
|
||||
// SAP System Roles
|
||||
// https://access.redhat.com/sites/default/files/attachments/rhel_system_roles_for_sap_1.pdf
|
||||
"ansible-core",
|
||||
"rhel-system-roles-sap",
|
||||
// RHBZ#1959813
|
||||
"bind-utils",
|
||||
"nfs-utils",
|
||||
"tcsh",
|
||||
// RHBZ#1959955
|
||||
"uuidd",
|
||||
// RHBZ#1959923
|
||||
"cairo",
|
||||
"expect",
|
||||
"graphviz",
|
||||
"gtk2",
|
||||
"iptraf-ng",
|
||||
"krb5-workstation",
|
||||
"libaio",
|
||||
"libatomic",
|
||||
"libcanberra-gtk2",
|
||||
"libicu",
|
||||
"libtool-ltdl",
|
||||
"lm_sensors",
|
||||
"net-tools",
|
||||
"numactl",
|
||||
"PackageKit-gtk3-module",
|
||||
"xorg-x11-xauth",
|
||||
// RHBZ#1960617
|
||||
"tuned-profiles-sap-hana",
|
||||
// RHBZ#1961168
|
||||
"libnsl",
|
||||
},
|
||||
Exclude: []string{
|
||||
// COMPOSER-1829
|
||||
"firewalld",
|
||||
"iwl1000-firmware",
|
||||
"iwl100-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6000g2a-firmware",
|
||||
"iwl6000g2b-firmware",
|
||||
"iwl6050-firmware",
|
||||
"iwl7260-firmware",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
package rhel9
|
||||
|
||||
import (
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
const vmdkKernelOptions = "ro net.ifnames=0"
|
||||
|
||||
var vmdkImgType = imageType{
|
||||
name: "vmdk",
|
||||
filename: "disk.vmdk",
|
||||
mimeType: "application/x-vmdk",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: vmdkCommonPackageSet,
|
||||
},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
},
|
||||
kernelOptions: vmdkKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 4 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "vmdk"},
|
||||
exports: []string{"vmdk"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
|
||||
var ovaImgType = imageType{
|
||||
name: "ova",
|
||||
filename: "image.ova",
|
||||
mimeType: "application/ovf",
|
||||
packageSets: map[string]packageSetFunc{
|
||||
osPkgsKey: vmdkCommonPackageSet,
|
||||
},
|
||||
defaultImageConfig: &distro.ImageConfig{
|
||||
Locale: common.ToPtr("en_US.UTF-8"),
|
||||
},
|
||||
kernelOptions: vmdkKernelOptions,
|
||||
bootable: true,
|
||||
defaultSize: 4 * common.GibiByte,
|
||||
image: liveImage,
|
||||
buildPipelines: []string{"build"},
|
||||
payloadPipelines: []string{"os", "image", "vmdk", "ovf", "archive"},
|
||||
exports: []string{"archive"},
|
||||
basePartitionTables: defaultBasePartitionTables,
|
||||
}
|
||||
|
||||
func vmdkCommonPackageSet(t *imageType) rpmmd.PackageSet {
|
||||
ps := rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
"chrony",
|
||||
"cloud-init",
|
||||
"firewalld",
|
||||
"langpacks-en",
|
||||
"open-vm-tools",
|
||||
},
|
||||
Exclude: []string{
|
||||
"rng-tools",
|
||||
},
|
||||
}.Append(coreOsCommonPackageSet(t))
|
||||
|
||||
if t.arch.Name() == platform.ARCH_X86_64.String() {
|
||||
ps = ps.Append(rpmmd.PackageSet{
|
||||
Include: []string{
|
||||
// packages below used to come from @core group and were not excluded
|
||||
// they may not be needed at all, but kept them here to not need
|
||||
// to exclude them instead in all other images
|
||||
"iwl100-firmware",
|
||||
"iwl105-firmware",
|
||||
"iwl135-firmware",
|
||||
"iwl1000-firmware",
|
||||
"iwl2000-firmware",
|
||||
"iwl2030-firmware",
|
||||
"iwl3160-firmware",
|
||||
"iwl5000-firmware",
|
||||
"iwl5150-firmware",
|
||||
"iwl6000g2a-firmware",
|
||||
"iwl6050-firmware",
|
||||
"iwl7260-firmware",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
|
@ -1,435 +0,0 @@
|
|||
package test_distro
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/container"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/distroregistry"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
dnfjson_mock "github.com/osbuild/osbuild-composer/internal/mocks/dnfjson"
|
||||
"github.com/osbuild/osbuild-composer/internal/ostree"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
)
|
||||
|
||||
const (
|
||||
// package set names
|
||||
|
||||
// build package set name
|
||||
buildPkgsKey = "build"
|
||||
|
||||
// main/common os image package set name
|
||||
osPkgsKey = "os"
|
||||
|
||||
// blueprint package set name
|
||||
blueprintPkgsKey = "blueprint"
|
||||
)
|
||||
|
||||
type TestDistro struct {
|
||||
name string
|
||||
releasever string
|
||||
modulePlatformID string
|
||||
ostreeRef string
|
||||
arches map[string]distro.Arch
|
||||
}
|
||||
|
||||
type TestArch struct {
|
||||
distribution *TestDistro
|
||||
name string
|
||||
imageTypes map[string]distro.ImageType
|
||||
}
|
||||
|
||||
type TestImageType struct {
|
||||
architecture *TestArch
|
||||
name string
|
||||
}
|
||||
|
||||
const (
|
||||
TestDistroName = "test-distro"
|
||||
TestDistro2Name = "test-distro-2"
|
||||
TestDistroReleasever = "1"
|
||||
TestDistro2Releasever = "2"
|
||||
TestDistroModulePlatformID = "platform:test"
|
||||
TestDistro2ModulePlatformID = "platform:test-2"
|
||||
|
||||
TestArchName = "test_arch"
|
||||
TestArch2Name = "test_arch2"
|
||||
TestArch3Name = "test_arch3"
|
||||
|
||||
TestImageTypeName = "test_type"
|
||||
TestImageType2Name = "test_type2"
|
||||
TestImageTypeOSTree = "test_ostree_type"
|
||||
|
||||
// added for cloudapi tests
|
||||
TestImageTypeAmi = "ami"
|
||||
TestImageTypeGce = "gce"
|
||||
TestImageTypeVhd = "vhd"
|
||||
TestImageTypeEdgeCommit = "rhel-edge-commit"
|
||||
TestImageTypeEdgeInstaller = "rhel-edge-installer"
|
||||
TestImageTypeImageInstaller = "image-installer"
|
||||
TestImageTypeQcow2 = "qcow2"
|
||||
TestImageTypeVmdk = "vmdk"
|
||||
)
|
||||
|
||||
// TestDistro
|
||||
|
||||
func (d *TestDistro) Name() string {
|
||||
return d.name
|
||||
}
|
||||
|
||||
func (d *TestDistro) Releasever() string {
|
||||
return d.releasever
|
||||
}
|
||||
|
||||
func (d *TestDistro) ModulePlatformID() string {
|
||||
return d.modulePlatformID
|
||||
}
|
||||
|
||||
func (d *TestDistro) OSTreeRef() string {
|
||||
return d.ostreeRef
|
||||
}
|
||||
|
||||
func (d *TestDistro) ListArches() []string {
|
||||
archs := make([]string, 0, len(d.arches))
|
||||
for name := range d.arches {
|
||||
archs = append(archs, name)
|
||||
}
|
||||
sort.Strings(archs)
|
||||
return archs
|
||||
}
|
||||
|
||||
func (d *TestDistro) GetArch(arch string) (distro.Arch, error) {
|
||||
a, exists := d.arches[arch]
|
||||
if !exists {
|
||||
return nil, errors.New("invalid arch: " + arch)
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (d *TestDistro) addArches(arches ...*TestArch) {
|
||||
if d.arches == nil {
|
||||
d.arches = map[string]distro.Arch{}
|
||||
}
|
||||
|
||||
for _, a := range arches {
|
||||
a.distribution = d
|
||||
d.arches[a.Name()] = a
|
||||
}
|
||||
}
|
||||
|
||||
// TestArch
|
||||
|
||||
func (a *TestArch) Name() string {
|
||||
return a.name
|
||||
}
|
||||
|
||||
func (a *TestArch) Distro() distro.Distro {
|
||||
return a.distribution
|
||||
}
|
||||
|
||||
func (a *TestArch) ListImageTypes() []string {
|
||||
formats := make([]string, 0, len(a.imageTypes))
|
||||
for name := range a.imageTypes {
|
||||
formats = append(formats, name)
|
||||
}
|
||||
sort.Strings(formats)
|
||||
return formats
|
||||
}
|
||||
|
||||
func (a *TestArch) GetImageType(imageType string) (distro.ImageType, error) {
|
||||
t, exists := a.imageTypes[imageType]
|
||||
if !exists {
|
||||
return nil, errors.New("invalid image type: " + imageType)
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (a *TestArch) addImageTypes(imageTypes ...TestImageType) {
|
||||
if a.imageTypes == nil {
|
||||
a.imageTypes = map[string]distro.ImageType{}
|
||||
}
|
||||
for idx := range imageTypes {
|
||||
it := imageTypes[idx]
|
||||
it.architecture = a
|
||||
a.imageTypes[it.Name()] = &it
|
||||
}
|
||||
}
|
||||
|
||||
// TestImageType
|
||||
|
||||
func (t *TestImageType) Name() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *TestImageType) Arch() distro.Arch {
|
||||
return t.architecture
|
||||
}
|
||||
|
||||
func (t *TestImageType) Filename() string {
|
||||
return "test.img"
|
||||
}
|
||||
|
||||
func (t *TestImageType) MIMEType() string {
|
||||
return "application/x-test"
|
||||
}
|
||||
|
||||
func (t *TestImageType) OSTreeRef() string {
|
||||
if t.name == TestImageTypeEdgeCommit || t.name == TestImageTypeEdgeInstaller || t.name == TestImageTypeOSTree {
|
||||
return t.architecture.distribution.OSTreeRef()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t *TestImageType) Size(size uint64) uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (t *TestImageType) PartitionType() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t *TestImageType) BootMode() distro.BootMode {
|
||||
return distro.BOOT_HYBRID
|
||||
}
|
||||
|
||||
func (t *TestImageType) BuildPipelines() []string {
|
||||
return distro.BuildPipelinesFallback()
|
||||
}
|
||||
|
||||
func (t *TestImageType) PayloadPipelines() []string {
|
||||
return distro.PayloadPipelinesFallback()
|
||||
}
|
||||
|
||||
func (t *TestImageType) PayloadPackageSets() []string {
|
||||
return []string{blueprintPkgsKey}
|
||||
}
|
||||
|
||||
func (t *TestImageType) PackageSetsChains() map[string][]string {
|
||||
return map[string][]string{
|
||||
osPkgsKey: {osPkgsKey, blueprintPkgsKey},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TestImageType) Exports() []string {
|
||||
return distro.ExportsFallback()
|
||||
}
|
||||
|
||||
func (t *TestImageType) Manifest(b *blueprint.Blueprint, options distro.ImageOptions, repos []rpmmd.RepoConfig, seed int64) (*manifest.Manifest, []string, error) {
|
||||
var bpPkgs []string
|
||||
if b != nil {
|
||||
mountpoints := b.Customizations.GetFilesystems()
|
||||
|
||||
invalidMountpoints := []string{}
|
||||
for _, m := range mountpoints {
|
||||
if m.Mountpoint != "/" {
|
||||
invalidMountpoints = append(invalidMountpoints, m.Mountpoint)
|
||||
}
|
||||
}
|
||||
|
||||
if len(invalidMountpoints) > 0 {
|
||||
return nil, nil, fmt.Errorf("The following custom mountpoints are not supported %+q", invalidMountpoints)
|
||||
}
|
||||
|
||||
bpPkgs = b.GetPackages()
|
||||
}
|
||||
|
||||
var ostreeSources []ostree.SourceSpec
|
||||
if defaultRef := t.OSTreeRef(); defaultRef != "" {
|
||||
// ostree image type
|
||||
ostreeSource := ostree.SourceSpec{ // init with default
|
||||
Ref: defaultRef,
|
||||
}
|
||||
if ostreeOptions := options.OSTree; ostreeOptions != nil {
|
||||
// handle the parameter combo error like we do in distros
|
||||
if ostreeOptions.ParentRef != "" && ostreeOptions.URL == "" {
|
||||
// specifying parent ref also requires URL
|
||||
return nil, nil, ostree.NewParameterComboError("ostree parent ref specified, but no URL to retrieve it")
|
||||
}
|
||||
if ostreeOptions.ImageRef != "" { // override with ref from image options
|
||||
ostreeSource.Ref = ostreeOptions.ImageRef
|
||||
}
|
||||
if ostreeOptions.ParentRef != "" { // override with parent ref
|
||||
ostreeSource.Ref = ostreeOptions.ParentRef
|
||||
}
|
||||
// copy any other options that might be specified
|
||||
ostreeSource.URL = options.OSTree.URL
|
||||
ostreeSource.RHSM = options.OSTree.RHSM
|
||||
}
|
||||
ostreeSources = []ostree.SourceSpec{ostreeSource}
|
||||
}
|
||||
|
||||
buildPackages := []rpmmd.PackageSet{{
|
||||
Include: []string{
|
||||
"dep-package1",
|
||||
"dep-package2",
|
||||
"dep-package3",
|
||||
},
|
||||
Repositories: repos,
|
||||
}}
|
||||
osPackages := []rpmmd.PackageSet{
|
||||
{
|
||||
Include: bpPkgs,
|
||||
Repositories: repos,
|
||||
},
|
||||
{
|
||||
Include: []string{
|
||||
"dep-package1",
|
||||
"dep-package2",
|
||||
"dep-package3",
|
||||
},
|
||||
Repositories: repos,
|
||||
},
|
||||
}
|
||||
|
||||
m := &manifest.Manifest{}
|
||||
|
||||
manifest.NewContentTest(m, buildPkgsKey, buildPackages, nil, nil)
|
||||
manifest.NewContentTest(m, osPkgsKey, osPackages, nil, ostreeSources)
|
||||
|
||||
return m, nil, nil
|
||||
}
|
||||
|
||||
// newTestDistro returns a new instance of TestDistro with the
|
||||
// given name and modulePlatformID.
|
||||
//
|
||||
// It contains two architectures "test_arch" and "test_arch2".
|
||||
// "test_arch" contains one image type "test_type".
|
||||
// "test_arch2" contains two image types "test_type" and "test_type2".
|
||||
func newTestDistro(name, modulePlatformID, releasever string) *TestDistro {
|
||||
td := TestDistro{
|
||||
name: name,
|
||||
releasever: releasever,
|
||||
modulePlatformID: modulePlatformID,
|
||||
ostreeRef: "test/13/x86_64/edge",
|
||||
}
|
||||
|
||||
ta1 := TestArch{
|
||||
name: TestArchName,
|
||||
}
|
||||
|
||||
ta2 := TestArch{
|
||||
name: TestArch2Name,
|
||||
}
|
||||
|
||||
ta3 := TestArch{
|
||||
name: TestArch3Name,
|
||||
}
|
||||
|
||||
it1 := TestImageType{
|
||||
name: TestImageTypeName,
|
||||
}
|
||||
|
||||
it2 := TestImageType{
|
||||
name: TestImageType2Name,
|
||||
}
|
||||
|
||||
it3 := TestImageType{
|
||||
name: TestImageTypeAmi,
|
||||
}
|
||||
|
||||
it4 := TestImageType{
|
||||
name: TestImageTypeVhd,
|
||||
}
|
||||
|
||||
it5 := TestImageType{
|
||||
name: TestImageTypeEdgeCommit,
|
||||
}
|
||||
|
||||
it6 := TestImageType{
|
||||
name: TestImageTypeEdgeInstaller,
|
||||
}
|
||||
|
||||
it7 := TestImageType{
|
||||
name: TestImageTypeImageInstaller,
|
||||
}
|
||||
|
||||
it8 := TestImageType{
|
||||
name: TestImageTypeQcow2,
|
||||
}
|
||||
|
||||
it9 := TestImageType{
|
||||
name: TestImageTypeVmdk,
|
||||
}
|
||||
|
||||
it10 := TestImageType{
|
||||
name: TestImageTypeGce,
|
||||
}
|
||||
|
||||
it11 := TestImageType{
|
||||
name: TestImageTypeOSTree,
|
||||
}
|
||||
|
||||
ta1.addImageTypes(it1, it11)
|
||||
ta2.addImageTypes(it1, it2)
|
||||
ta3.addImageTypes(it3, it4, it5, it6, it7, it8, it9, it10)
|
||||
|
||||
td.addArches(&ta1, &ta2, &ta3)
|
||||
|
||||
return &td
|
||||
}
|
||||
|
||||
// New returns new instance of TestDistro named "test-distro".
|
||||
func New() *TestDistro {
|
||||
return newTestDistro(TestDistroName, TestDistroModulePlatformID, TestDistroReleasever)
|
||||
}
|
||||
|
||||
func NewRegistry() *distroregistry.Registry {
|
||||
td := New()
|
||||
registry, err := distroregistry.New(td, td)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Override the host's architecture name with the test's name
|
||||
registry.SetHostArchName(TestArchName)
|
||||
return registry
|
||||
}
|
||||
|
||||
// New2 returns new instance of TestDistro named "test-distro-2".
|
||||
func New2() *TestDistro {
|
||||
return newTestDistro(TestDistro2Name, TestDistro2ModulePlatformID, TestDistro2Releasever)
|
||||
}
|
||||
|
||||
// ResolveContent transforms content source specs into resolved specs for serialization.
|
||||
// For packages, it uses the dnfjson_mock.BaseDeps() every time, but retains
|
||||
// the map keys from the input.
|
||||
// For ostree commits it hashes the URL+Ref to create a checksum.
|
||||
func ResolveContent(pkgs map[string][]rpmmd.PackageSet, containers map[string][]container.SourceSpec, commits map[string][]ostree.SourceSpec) (map[string][]rpmmd.PackageSpec, map[string][]container.Spec, map[string][]ostree.CommitSpec) {
|
||||
|
||||
pkgSpecs := make(map[string][]rpmmd.PackageSpec, len(pkgs))
|
||||
for name := range pkgs {
|
||||
pkgSpecs[name] = dnfjson_mock.BaseDeps()
|
||||
}
|
||||
|
||||
containerSpecs := make(map[string][]container.Spec, len(containers))
|
||||
for name := range containers {
|
||||
containerSpecs[name] = make([]container.Spec, len(containers[name]))
|
||||
for idx := range containers[name] {
|
||||
containerSpecs[name][idx] = container.Spec{
|
||||
Source: containers[name][idx].Source,
|
||||
TLSVerify: containers[name][idx].TLSVerify,
|
||||
LocalName: containers[name][idx].Name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commitSpecs := make(map[string][]ostree.CommitSpec, len(commits))
|
||||
for name := range commits {
|
||||
commitSpecs[name] = make([]ostree.CommitSpec, len(commits[name]))
|
||||
for idx := range commits[name] {
|
||||
commitSpecs[name][idx] = ostree.CommitSpec{
|
||||
Ref: commits[name][idx].Ref,
|
||||
URL: commits[name][idx].URL,
|
||||
Checksum: fmt.Sprintf("%x", sha256.Sum256([]byte(commits[name][idx].URL+commits[name][idx].Ref))),
|
||||
}
|
||||
fmt.Printf("Test distro spec: %+v\n", commitSpecs[name][idx])
|
||||
}
|
||||
}
|
||||
|
||||
return pkgSpecs, containerSpecs, commitSpecs
|
||||
}
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
package distroregistry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro/fedora"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro/rhel7"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro/rhel8"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro/rhel9"
|
||||
)
|
||||
|
||||
// When adding support for a new distribution, add it here.
|
||||
// Note that this is a constant, do not write to this array.
|
||||
var supportedDistros = []func() distro.Distro{
|
||||
fedora.NewF37,
|
||||
fedora.NewF38,
|
||||
fedora.NewF39,
|
||||
|
||||
rhel7.New,
|
||||
|
||||
rhel8.New,
|
||||
rhel8.NewRHEL84,
|
||||
rhel8.NewRHEL85,
|
||||
rhel8.NewRHEL86,
|
||||
rhel8.NewRHEL87,
|
||||
rhel8.NewRHEL88,
|
||||
rhel8.NewRHEL89,
|
||||
rhel8.NewCentos,
|
||||
|
||||
rhel9.New,
|
||||
rhel9.NewRHEL90,
|
||||
rhel9.NewRHEL91,
|
||||
rhel9.NewRHEL92,
|
||||
rhel9.NewRHEL93,
|
||||
rhel9.NewCentOS9,
|
||||
}
|
||||
|
||||
type Registry struct {
|
||||
distros map[string]distro.Distro
|
||||
hostDistro distro.Distro
|
||||
hostArchName string
|
||||
}
|
||||
|
||||
func New(hostDistro distro.Distro, distros ...distro.Distro) (*Registry, error) {
|
||||
reg := &Registry{
|
||||
distros: make(map[string]distro.Distro),
|
||||
hostDistro: hostDistro,
|
||||
hostArchName: common.CurrentArch(),
|
||||
}
|
||||
for _, d := range distros {
|
||||
name := d.Name()
|
||||
if _, exists := reg.distros[name]; exists {
|
||||
return nil, fmt.Errorf("New: passed two distros with the same name: %s", d.Name())
|
||||
}
|
||||
reg.distros[name] = d
|
||||
}
|
||||
return reg, nil
|
||||
}
|
||||
|
||||
// NewDefault creates a Registry with all distributions supported by
|
||||
// osbuild-composer. If you need to add a distribution here, see the
|
||||
// supportedDistros variable.
|
||||
func NewDefault() *Registry {
|
||||
var distros []distro.Distro
|
||||
var hostDistro distro.Distro
|
||||
|
||||
// First determine the name of the Host Distro
|
||||
// If there was an error, then the hostDistroName will be an empty string
|
||||
// and as a result, the hostDistro will have a nil value when calling New().
|
||||
// Getting the host distro later using FromHost() will return nil as well.
|
||||
hostDistroName, _, _, _ := common.GetHostDistroName()
|
||||
for _, supportedDistro := range supportedDistros {
|
||||
distro := supportedDistro()
|
||||
if distro.Name() == hostDistroName {
|
||||
hostDistro = supportedDistro()
|
||||
}
|
||||
distros = append(distros, distro)
|
||||
}
|
||||
|
||||
registry, err := New(hostDistro, distros...)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("two supported distros have the same name, this is a programming error: %v", err))
|
||||
}
|
||||
|
||||
return registry
|
||||
}
|
||||
|
||||
func (r *Registry) GetDistro(name string) distro.Distro {
|
||||
d, ok := r.distros[name]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// List returns the names of all distros in a Registry, sorted alphabetically.
|
||||
func (r *Registry) List() []string {
|
||||
list := []string{}
|
||||
for _, d := range r.distros {
|
||||
list = append(list, d.Name())
|
||||
}
|
||||
sort.Strings(list)
|
||||
return list
|
||||
}
|
||||
|
||||
func mangleHostDistroName(name string, isBeta, isStream bool) string {
|
||||
hostDistroName := name
|
||||
if strings.HasPrefix(hostDistroName, "rhel-8") {
|
||||
hostDistroName = "rhel-8"
|
||||
}
|
||||
if isBeta {
|
||||
hostDistroName += "-beta"
|
||||
}
|
||||
|
||||
// override repository for centos stream, remove when CentOS 8 is EOL
|
||||
if isStream && hostDistroName == "centos-8" {
|
||||
hostDistroName = "centos-stream-8"
|
||||
}
|
||||
|
||||
return hostDistroName
|
||||
}
|
||||
|
||||
// FromHost returns a distro instance, that is specific to the host.
|
||||
// Its name may differ from other supported distros, if the host version
|
||||
// is e.g. a Beta or a Stream.
|
||||
func (r *Registry) FromHost() distro.Distro {
|
||||
return r.hostDistro
|
||||
}
|
||||
|
||||
// HostArchName returns the host's arch name
|
||||
func (r *Registry) HostArchName() string {
|
||||
return r.hostArchName
|
||||
}
|
||||
|
||||
// SetHostArchName can be used to override the host's arch name for testing
|
||||
func (r *Registry) SetHostArchName(name string) {
|
||||
r.hostArchName = name
|
||||
}
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
package distroregistry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro/rhel8"
|
||||
)
|
||||
|
||||
// Test that all distros are registered properly and that Registry.List() works.
|
||||
func TestRegistry_List(t *testing.T) {
|
||||
// build expected distros
|
||||
var expected []string
|
||||
for _, supportedDistro := range supportedDistros {
|
||||
d := supportedDistro()
|
||||
expected = append(expected, d.Name())
|
||||
}
|
||||
|
||||
distros := NewDefault()
|
||||
|
||||
require.ElementsMatch(t, expected, distros.List(), "unexpected list of distros")
|
||||
}
|
||||
|
||||
func TestRegistry_GetDistro(t *testing.T) {
|
||||
distros := NewDefault()
|
||||
|
||||
t.Run("distro exists", func(t *testing.T) {
|
||||
expectedDistro := rhel8.New()
|
||||
require.Equal(t, expectedDistro.Name(), distros.GetDistro(expectedDistro.Name()).Name())
|
||||
})
|
||||
|
||||
t.Run("distro doesn't exist", func(t *testing.T) {
|
||||
require.Nil(t, distros.GetDistro("toucan-os"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestRegistry_mangleHostDistroName(t *testing.T) {
|
||||
|
||||
type args struct {
|
||||
name string
|
||||
isBeta bool
|
||||
isStream bool
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{"fedora-33", args{"fedora-33", false, false}, "fedora-33"},
|
||||
{"fedora-33 beta", args{"fedora-33", true, false}, "fedora-33-beta"},
|
||||
{"fedora-33 stream", args{"fedora-33", false, true}, "fedora-33"},
|
||||
{"fedora-33 beta stream", args{"fedora-33", true, true}, "fedora-33-beta"},
|
||||
|
||||
{"rhel-8", args{"rhel-8", false, false}, "rhel-8"},
|
||||
{"rhel-8 beta", args{"rhel-8", true, false}, "rhel-8-beta"},
|
||||
{"rhel-8 stream", args{"rhel-8", false, true}, "rhel-8"},
|
||||
{"rhel-8 beta stream", args{"rhel-8", true, true}, "rhel-8-beta"},
|
||||
|
||||
{"rhel-84", args{"rhel-84", false, false}, "rhel-8"},
|
||||
{"rhel-84 beta", args{"rhel-84", true, false}, "rhel-8-beta"},
|
||||
{"rhel-84 stream", args{"rhel-84", false, true}, "rhel-8"},
|
||||
{"rhel-84 beta stream", args{"rhel-84", true, true}, "rhel-8-beta"},
|
||||
|
||||
{"centos-8", args{"centos-8", false, false}, "centos-8"},
|
||||
{"centos-8 beta", args{"centos-8", true, false}, "centos-8-beta"},
|
||||
{"centos-8 stream", args{"centos-8", false, true}, "centos-stream-8"},
|
||||
{"centos-8 beta stream", args{"centos-8", true, true}, "centos-8-beta"},
|
||||
|
||||
{"rhel-90", args{"rhel-90", false, false}, "rhel-90"},
|
||||
{"rhel-90 beta", args{"rhel-90", true, false}, "rhel-90-beta"},
|
||||
{"rhel-90 stream", args{"rhel-90", false, true}, "rhel-90"},
|
||||
{"rhel-90 beta stream", args{"rhel-90", true, true}, "rhel-90-beta"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mangledName := mangleHostDistroName(tt.args.name, tt.args.isBeta, tt.args.isStream)
|
||||
require.Equalf(
|
||||
t,
|
||||
tt.want,
|
||||
mangledName,
|
||||
"mangleHostDistroName() name:%s, isBeta:%s, isStream:%s =\nExpected: %s\nGot: %s\n",
|
||||
tt.args.name,
|
||||
tt.args.isBeta,
|
||||
tt.args.isStream,
|
||||
tt.want,
|
||||
mangledName,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistry_FromHost(t *testing.T) {
|
||||
// expected distros
|
||||
var distros []distro.Distro
|
||||
for _, supportedDistro := range supportedDistros {
|
||||
distros = append(distros, supportedDistro())
|
||||
}
|
||||
|
||||
t.Run("host distro is nil", func(t *testing.T) {
|
||||
registry, err := New(nil, distros...)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, registry)
|
||||
require.Nil(t, registry.FromHost())
|
||||
})
|
||||
|
||||
t.Run("host distro not nil", func(t *testing.T) {
|
||||
hostDistro := rhel8.New()
|
||||
fmt.Println(hostDistro.Name())
|
||||
registry, err := New(hostDistro, distros...)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, registry)
|
||||
|
||||
gotDistro := registry.FromHost()
|
||||
require.NotNil(t, gotDistro)
|
||||
require.Equal(t, gotDistro.Name(), hostDistro.Name())
|
||||
})
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/images/pkg/rpmmd"
|
||||
|
||||
"github.com/gobwas/glob"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/images/pkg/rpmmd"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/rhsm"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/images/pkg/rhsm"
|
||||
"github.com/osbuild/images/pkg/rpmmd"
|
||||
)
|
||||
|
||||
// BaseSolver defines the basic solver configuration without platform
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/osbuild/images/pkg/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/mocks/rpmrepo"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package environment
|
||||
|
||||
import "github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
import "github.com/osbuild/images/pkg/rpmmd"
|
||||
|
||||
type Environment interface {
|
||||
GetPackages() []string
|
||||
|
|
|
|||
|
|
@ -1,127 +0,0 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/artifact"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/disk"
|
||||
"github.com/osbuild/osbuild-composer/internal/environment"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/runner"
|
||||
"github.com/osbuild/osbuild-composer/internal/workload"
|
||||
)
|
||||
|
||||
type AnacondaLiveInstaller struct {
|
||||
Base
|
||||
Platform platform.Platform
|
||||
Environment environment.Environment
|
||||
Workload workload.Workload
|
||||
|
||||
ExtraBasePackages rpmmd.PackageSet
|
||||
|
||||
ISOLabelTempl string
|
||||
Product string
|
||||
Variant string
|
||||
OSName string
|
||||
OSVersion string
|
||||
Release string
|
||||
|
||||
Filename string
|
||||
|
||||
AdditionalKernelOpts []string
|
||||
}
|
||||
|
||||
func NewAnacondaLiveInstaller() *AnacondaLiveInstaller {
|
||||
return &AnacondaLiveInstaller{
|
||||
Base: NewBase("live-installer"),
|
||||
}
|
||||
}
|
||||
|
||||
func (img *AnacondaLiveInstaller) InstantiateManifest(m *manifest.Manifest,
|
||||
repos []rpmmd.RepoConfig,
|
||||
runner runner.Runner,
|
||||
rng *rand.Rand) (*artifact.Artifact, error) {
|
||||
buildPipeline := manifest.NewBuild(m, runner, repos)
|
||||
buildPipeline.Checkpoint()
|
||||
|
||||
livePipeline := manifest.NewAnacondaInstaller(m,
|
||||
manifest.AnacondaInstallerTypeLive,
|
||||
buildPipeline,
|
||||
img.Platform,
|
||||
repos,
|
||||
"kernel",
|
||||
img.Product,
|
||||
img.OSVersion)
|
||||
|
||||
livePipeline.ExtraPackages = img.ExtraBasePackages.Include
|
||||
|
||||
livePipeline.Variant = img.Variant
|
||||
livePipeline.Biosdevname = (img.Platform.GetArch() == platform.ARCH_X86_64)
|
||||
|
||||
livePipeline.Checkpoint()
|
||||
|
||||
rootfsPartitionTable := &disk.PartitionTable{
|
||||
Size: 20 * common.MebiByte,
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Start: 0,
|
||||
Size: 20 * common.MebiByte,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
Mountpoint: "/",
|
||||
UUID: disk.NewVolIDFromRand(rng),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: replace isoLabelTmpl with more high-level properties
|
||||
isoLabel := fmt.Sprintf(img.ISOLabelTempl, img.Platform.GetArch())
|
||||
|
||||
rootfsImagePipeline := manifest.NewISORootfsImg(m, buildPipeline, livePipeline)
|
||||
rootfsImagePipeline.Size = 8 * common.GibiByte
|
||||
|
||||
bootTreePipeline := manifest.NewEFIBootTree(m, buildPipeline, img.Product, img.OSVersion)
|
||||
bootTreePipeline.Platform = img.Platform
|
||||
bootTreePipeline.UEFIVendor = img.Platform.GetUEFIVendor()
|
||||
bootTreePipeline.ISOLabel = isoLabel
|
||||
|
||||
kernelOpts := []string{
|
||||
fmt.Sprintf("root=live:CDLABEL=%s", isoLabel),
|
||||
"rd.live.image",
|
||||
"quiet",
|
||||
"rhgb",
|
||||
}
|
||||
|
||||
kernelOpts = append(kernelOpts, img.AdditionalKernelOpts...)
|
||||
|
||||
bootTreePipeline.KernelOpts = kernelOpts
|
||||
|
||||
// enable ISOLinux on x86_64 only
|
||||
isoLinuxEnabled := img.Platform.GetArch() == platform.ARCH_X86_64
|
||||
|
||||
isoTreePipeline := manifest.NewAnacondaInstallerISOTree(m,
|
||||
buildPipeline,
|
||||
livePipeline,
|
||||
rootfsImagePipeline,
|
||||
bootTreePipeline,
|
||||
isoLabel)
|
||||
isoTreePipeline.PartitionTable = rootfsPartitionTable
|
||||
isoTreePipeline.Release = img.Release
|
||||
isoTreePipeline.OSName = img.OSName
|
||||
|
||||
isoTreePipeline.KernelOpts = kernelOpts
|
||||
isoTreePipeline.ISOLinux = isoLinuxEnabled
|
||||
|
||||
isoPipeline := manifest.NewISO(m, buildPipeline, isoTreePipeline, isoLabel)
|
||||
isoPipeline.Filename = img.Filename
|
||||
isoPipeline.ISOLinux = isoLinuxEnabled
|
||||
|
||||
artifact := isoPipeline.Export()
|
||||
|
||||
return artifact, nil
|
||||
}
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/artifact"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/disk"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/ostree"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/runner"
|
||||
"github.com/osbuild/osbuild-composer/internal/users"
|
||||
)
|
||||
|
||||
type AnacondaOSTreeInstaller struct {
|
||||
Base
|
||||
Platform platform.Platform
|
||||
ExtraBasePackages rpmmd.PackageSet
|
||||
Users []users.User
|
||||
Groups []users.Group
|
||||
|
||||
SquashfsCompression string
|
||||
|
||||
ISOLabelTempl string
|
||||
Product string
|
||||
Variant string
|
||||
OSName string
|
||||
OSVersion string
|
||||
Release string
|
||||
|
||||
Commit ostree.SourceSpec
|
||||
|
||||
Filename string
|
||||
|
||||
AdditionalDracutModules []string
|
||||
AdditionalAnacondaModules []string
|
||||
AdditionalDrivers []string
|
||||
}
|
||||
|
||||
func NewAnacondaOSTreeInstaller(commit ostree.SourceSpec) *AnacondaOSTreeInstaller {
|
||||
return &AnacondaOSTreeInstaller{
|
||||
Base: NewBase("ostree-installer"),
|
||||
Commit: commit,
|
||||
}
|
||||
}
|
||||
|
||||
func (img *AnacondaOSTreeInstaller) InstantiateManifest(m *manifest.Manifest,
|
||||
repos []rpmmd.RepoConfig,
|
||||
runner runner.Runner,
|
||||
rng *rand.Rand) (*artifact.Artifact, error) {
|
||||
buildPipeline := manifest.NewBuild(m, runner, repos)
|
||||
buildPipeline.Checkpoint()
|
||||
|
||||
anacondaPipeline := manifest.NewAnacondaInstaller(m,
|
||||
manifest.AnacondaInstallerTypePayload,
|
||||
buildPipeline,
|
||||
img.Platform,
|
||||
repos,
|
||||
"kernel",
|
||||
img.Product,
|
||||
img.OSVersion)
|
||||
anacondaPipeline.ExtraPackages = img.ExtraBasePackages.Include
|
||||
anacondaPipeline.ExtraRepos = img.ExtraBasePackages.Repositories
|
||||
anacondaPipeline.Users = img.Users
|
||||
anacondaPipeline.Groups = img.Groups
|
||||
anacondaPipeline.Variant = img.Variant
|
||||
anacondaPipeline.Biosdevname = (img.Platform.GetArch() == platform.ARCH_X86_64)
|
||||
anacondaPipeline.Checkpoint()
|
||||
anacondaPipeline.AdditionalDracutModules = img.AdditionalDracutModules
|
||||
anacondaPipeline.AdditionalAnacondaModules = img.AdditionalAnacondaModules
|
||||
anacondaPipeline.AdditionalDrivers = img.AdditionalDrivers
|
||||
|
||||
rootfsPartitionTable := &disk.PartitionTable{
|
||||
Size: 20 * common.MebiByte,
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Start: 0,
|
||||
Size: 20 * common.MebiByte,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
Mountpoint: "/",
|
||||
UUID: disk.NewVolIDFromRand(rng),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: replace isoLabelTmpl with more high-level properties
|
||||
isoLabel := fmt.Sprintf(img.ISOLabelTempl, img.Platform.GetArch())
|
||||
|
||||
rootfsImagePipeline := manifest.NewISORootfsImg(m, buildPipeline, anacondaPipeline)
|
||||
rootfsImagePipeline.Size = 4 * common.GibiByte
|
||||
|
||||
bootTreePipeline := manifest.NewEFIBootTree(m, buildPipeline, img.Product, img.OSVersion)
|
||||
bootTreePipeline.Platform = img.Platform
|
||||
bootTreePipeline.UEFIVendor = img.Platform.GetUEFIVendor()
|
||||
bootTreePipeline.ISOLabel = isoLabel
|
||||
bootTreePipeline.KernelOpts = []string{fmt.Sprintf("inst.stage2=hd:LABEL=%s", isoLabel), fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", isoLabel, kspath)}
|
||||
|
||||
// enable ISOLinux on x86_64 only
|
||||
isoLinuxEnabled := img.Platform.GetArch() == platform.ARCH_X86_64
|
||||
|
||||
isoTreePipeline := manifest.NewAnacondaInstallerISOTree(m,
|
||||
buildPipeline,
|
||||
anacondaPipeline,
|
||||
rootfsImagePipeline,
|
||||
bootTreePipeline,
|
||||
isoLabel)
|
||||
isoTreePipeline.PartitionTable = rootfsPartitionTable
|
||||
isoTreePipeline.Release = img.Release
|
||||
isoTreePipeline.OSName = img.OSName
|
||||
isoTreePipeline.Users = img.Users
|
||||
isoTreePipeline.Groups = img.Groups
|
||||
|
||||
isoTreePipeline.SquashfsCompression = img.SquashfsCompression
|
||||
|
||||
// For ostree installers, always put the kickstart file in the root of the ISO
|
||||
isoTreePipeline.KSPath = kspath
|
||||
isoTreePipeline.PayloadPath = "/ostree/repo"
|
||||
|
||||
isoTreePipeline.OSTreeCommitSource = &img.Commit
|
||||
isoTreePipeline.ISOLinux = isoLinuxEnabled
|
||||
|
||||
isoPipeline := manifest.NewISO(m, buildPipeline, isoTreePipeline, isoLabel)
|
||||
isoPipeline.Filename = img.Filename
|
||||
isoPipeline.ISOLinux = isoLinuxEnabled
|
||||
artifact := isoPipeline.Export()
|
||||
|
||||
return artifact, nil
|
||||
}
|
||||
|
|
@ -1,166 +0,0 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/artifact"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/disk"
|
||||
"github.com/osbuild/osbuild-composer/internal/environment"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/runner"
|
||||
"github.com/osbuild/osbuild-composer/internal/users"
|
||||
"github.com/osbuild/osbuild-composer/internal/workload"
|
||||
)
|
||||
|
||||
const kspath = "/osbuild.ks"
|
||||
|
||||
type AnacondaTarInstaller struct {
|
||||
Base
|
||||
Platform platform.Platform
|
||||
OSCustomizations manifest.OSCustomizations
|
||||
Environment environment.Environment
|
||||
Workload workload.Workload
|
||||
|
||||
ExtraBasePackages rpmmd.PackageSet
|
||||
Users []users.User
|
||||
Groups []users.Group
|
||||
|
||||
// If set, the kickstart file will be added to the bootiso-tree as
|
||||
// /osbuild.ks, otherwise any kickstart options will be configured in the
|
||||
// default /usr/share/anaconda/interactive-defaults.ks in the rootfs.
|
||||
ISORootKickstart bool
|
||||
|
||||
SquashfsCompression string
|
||||
|
||||
ISOLabelTempl string
|
||||
Product string
|
||||
Variant string
|
||||
OSName string
|
||||
OSVersion string
|
||||
Release string
|
||||
|
||||
Filename string
|
||||
|
||||
AdditionalKernelOpts []string
|
||||
AdditionalAnacondaModules []string
|
||||
AdditionalDracutModules []string
|
||||
AdditionalDrivers []string
|
||||
}
|
||||
|
||||
func NewAnacondaTarInstaller() *AnacondaTarInstaller {
|
||||
return &AnacondaTarInstaller{
|
||||
Base: NewBase("image-installer"),
|
||||
}
|
||||
}
|
||||
|
||||
func (img *AnacondaTarInstaller) InstantiateManifest(m *manifest.Manifest,
|
||||
repos []rpmmd.RepoConfig,
|
||||
runner runner.Runner,
|
||||
rng *rand.Rand) (*artifact.Artifact, error) {
|
||||
buildPipeline := manifest.NewBuild(m, runner, repos)
|
||||
buildPipeline.Checkpoint()
|
||||
|
||||
anacondaPipeline := manifest.NewAnacondaInstaller(m,
|
||||
manifest.AnacondaInstallerTypePayload,
|
||||
buildPipeline,
|
||||
img.Platform,
|
||||
repos,
|
||||
"kernel",
|
||||
img.Product,
|
||||
img.OSVersion)
|
||||
|
||||
anacondaPipeline.ExtraPackages = img.ExtraBasePackages.Include
|
||||
anacondaPipeline.ExtraRepos = img.ExtraBasePackages.Repositories
|
||||
anacondaPipeline.Users = img.Users
|
||||
anacondaPipeline.Groups = img.Groups
|
||||
anacondaPipeline.Variant = img.Variant
|
||||
anacondaPipeline.Biosdevname = (img.Platform.GetArch() == platform.ARCH_X86_64)
|
||||
anacondaPipeline.AdditionalAnacondaModules = img.AdditionalAnacondaModules
|
||||
anacondaPipeline.AdditionalDracutModules = img.AdditionalDracutModules
|
||||
anacondaPipeline.AdditionalDrivers = img.AdditionalDrivers
|
||||
|
||||
tarPath := "/liveimg.tar.gz"
|
||||
|
||||
if !img.ISORootKickstart {
|
||||
payloadPath := filepath.Join("/run/install/repo/", tarPath)
|
||||
anacondaPipeline.InteractiveDefaults = manifest.NewAnacondaInteractiveDefaults(fmt.Sprintf("file://%s", payloadPath))
|
||||
}
|
||||
|
||||
anacondaPipeline.Checkpoint()
|
||||
|
||||
rootfsPartitionTable := &disk.PartitionTable{
|
||||
Size: 20 * common.MebiByte,
|
||||
Partitions: []disk.Partition{
|
||||
{
|
||||
Start: 0,
|
||||
Size: 20 * common.MebiByte,
|
||||
Payload: &disk.Filesystem{
|
||||
Type: "vfat",
|
||||
Mountpoint: "/",
|
||||
UUID: disk.NewVolIDFromRand(rng),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: replace isoLabelTmpl with more high-level properties
|
||||
isoLabel := fmt.Sprintf(img.ISOLabelTempl, img.Platform.GetArch())
|
||||
|
||||
rootfsImagePipeline := manifest.NewISORootfsImg(m, buildPipeline, anacondaPipeline)
|
||||
rootfsImagePipeline.Size = 4 * common.GibiByte
|
||||
|
||||
bootTreePipeline := manifest.NewEFIBootTree(m, buildPipeline, img.Product, img.OSVersion)
|
||||
bootTreePipeline.Platform = img.Platform
|
||||
bootTreePipeline.UEFIVendor = img.Platform.GetUEFIVendor()
|
||||
bootTreePipeline.ISOLabel = isoLabel
|
||||
|
||||
kernelOpts := []string{fmt.Sprintf("inst.stage2=hd:LABEL=%s", isoLabel)}
|
||||
if img.ISORootKickstart {
|
||||
kernelOpts = append(kernelOpts, fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", isoLabel, kspath))
|
||||
}
|
||||
kernelOpts = append(kernelOpts, img.AdditionalKernelOpts...)
|
||||
bootTreePipeline.KernelOpts = kernelOpts
|
||||
|
||||
osPipeline := manifest.NewOS(m, buildPipeline, img.Platform, repos)
|
||||
osPipeline.OSCustomizations = img.OSCustomizations
|
||||
osPipeline.Environment = img.Environment
|
||||
osPipeline.Workload = img.Workload
|
||||
|
||||
// enable ISOLinux on x86_64 only
|
||||
isoLinuxEnabled := img.Platform.GetArch() == platform.ARCH_X86_64
|
||||
|
||||
isoTreePipeline := manifest.NewAnacondaInstallerISOTree(m,
|
||||
buildPipeline,
|
||||
anacondaPipeline,
|
||||
rootfsImagePipeline,
|
||||
bootTreePipeline,
|
||||
isoLabel)
|
||||
isoTreePipeline.PartitionTable = rootfsPartitionTable
|
||||
isoTreePipeline.Release = img.Release
|
||||
isoTreePipeline.OSName = img.OSName
|
||||
isoTreePipeline.Users = img.Users
|
||||
isoTreePipeline.Groups = img.Groups
|
||||
isoTreePipeline.PayloadPath = tarPath
|
||||
if img.ISORootKickstart {
|
||||
isoTreePipeline.KSPath = kspath
|
||||
}
|
||||
|
||||
isoTreePipeline.SquashfsCompression = img.SquashfsCompression
|
||||
|
||||
isoTreePipeline.OSPipeline = osPipeline
|
||||
isoTreePipeline.KernelOpts = img.AdditionalKernelOpts
|
||||
isoTreePipeline.ISOLinux = isoLinuxEnabled
|
||||
|
||||
isoPipeline := manifest.NewISO(m, buildPipeline, isoTreePipeline, isoLabel)
|
||||
isoPipeline.Filename = img.Filename
|
||||
isoPipeline.ISOLinux = isoLinuxEnabled
|
||||
|
||||
artifact := isoPipeline.Export()
|
||||
|
||||
return artifact, nil
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/artifact"
|
||||
"github.com/osbuild/osbuild-composer/internal/environment"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/runner"
|
||||
"github.com/osbuild/osbuild-composer/internal/workload"
|
||||
)
|
||||
|
||||
type Archive struct {
|
||||
Base
|
||||
Platform platform.Platform
|
||||
OSCustomizations manifest.OSCustomizations
|
||||
Environment environment.Environment
|
||||
Workload workload.Workload
|
||||
Filename string
|
||||
}
|
||||
|
||||
func NewArchive() *Archive {
|
||||
return &Archive{
|
||||
Base: NewBase("archive"),
|
||||
}
|
||||
}
|
||||
|
||||
func (img *Archive) InstantiateManifest(m *manifest.Manifest,
|
||||
repos []rpmmd.RepoConfig,
|
||||
runner runner.Runner,
|
||||
rng *rand.Rand) (*artifact.Artifact, error) {
|
||||
buildPipeline := manifest.NewBuild(m, runner, repos)
|
||||
buildPipeline.Checkpoint()
|
||||
|
||||
osPipeline := manifest.NewOS(m, buildPipeline, img.Platform, repos)
|
||||
osPipeline.OSCustomizations = img.OSCustomizations
|
||||
osPipeline.Environment = img.Environment
|
||||
osPipeline.Workload = img.Workload
|
||||
|
||||
tarPipeline := manifest.NewTar(m, buildPipeline, &osPipeline.Base, "archive")
|
||||
tarPipeline.Filename = img.Filename
|
||||
artifact := tarPipeline.Export()
|
||||
|
||||
return artifact, nil
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/artifact"
|
||||
"github.com/osbuild/osbuild-composer/internal/environment"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/runner"
|
||||
"github.com/osbuild/osbuild-composer/internal/workload"
|
||||
)
|
||||
|
||||
type BaseContainer struct {
|
||||
Base
|
||||
Platform platform.Platform
|
||||
OSCustomizations manifest.OSCustomizations
|
||||
Environment environment.Environment
|
||||
Workload workload.Workload
|
||||
Filename string
|
||||
}
|
||||
|
||||
func NewBaseContainer() *BaseContainer {
|
||||
return &BaseContainer{
|
||||
Base: NewBase("base-container"),
|
||||
}
|
||||
}
|
||||
|
||||
func (img *BaseContainer) InstantiateManifest(m *manifest.Manifest,
|
||||
repos []rpmmd.RepoConfig,
|
||||
runner runner.Runner,
|
||||
rng *rand.Rand) (*artifact.Artifact, error) {
|
||||
buildPipeline := manifest.NewBuild(m, runner, repos)
|
||||
buildPipeline.Checkpoint()
|
||||
|
||||
osPipeline := manifest.NewOS(m, buildPipeline, img.Platform, repos)
|
||||
osPipeline.OSCustomizations = img.OSCustomizations
|
||||
osPipeline.Environment = img.Environment
|
||||
osPipeline.Workload = img.Workload
|
||||
|
||||
ociPipeline := manifest.NewOCIContainer(m, buildPipeline, osPipeline)
|
||||
ociPipeline.Filename = img.Filename
|
||||
artifact := ociPipeline.Export()
|
||||
|
||||
return artifact, nil
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/artifact"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/runner"
|
||||
)
|
||||
|
||||
type ImageKind interface {
|
||||
Name() string
|
||||
InstantiateManifest(m *manifest.Manifest, repos []rpmmd.RepoConfig, runner runner.Runner, rng *rand.Rand) (*artifact.Artifact, error)
|
||||
}
|
||||
|
||||
type Base struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (img Base) Name() string {
|
||||
return img.name
|
||||
}
|
||||
|
||||
func NewBase(name string) Base {
|
||||
return Base{
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/artifact"
|
||||
"github.com/osbuild/osbuild-composer/internal/common"
|
||||
"github.com/osbuild/osbuild-composer/internal/disk"
|
||||
"github.com/osbuild/osbuild-composer/internal/environment"
|
||||
"github.com/osbuild/osbuild-composer/internal/manifest"
|
||||
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
||||
"github.com/osbuild/osbuild-composer/internal/platform"
|
||||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/runner"
|
||||
"github.com/osbuild/osbuild-composer/internal/workload"
|
||||
)
|
||||
|
||||
type LiveImage struct {
|
||||
Base
|
||||
Platform platform.Platform
|
||||
PartitionTable *disk.PartitionTable
|
||||
OSCustomizations manifest.OSCustomizations
|
||||
Environment environment.Environment
|
||||
Workload workload.Workload
|
||||
Filename string
|
||||
Compression string
|
||||
ForceSize *bool
|
||||
PartTool osbuild.PartTool
|
||||
|
||||
NoBLS bool
|
||||
OSProduct string
|
||||
OSVersion string
|
||||
OSNick string
|
||||
}
|
||||
|
||||
func NewLiveImage() *LiveImage {
|
||||
return &LiveImage{
|
||||
Base: NewBase("live-image"),
|
||||
PartTool: osbuild.PTSfdisk,
|
||||
}
|
||||
}
|
||||
|
||||
func (img *LiveImage) InstantiateManifest(m *manifest.Manifest,
|
||||
repos []rpmmd.RepoConfig,
|
||||
runner runner.Runner,
|
||||
rng *rand.Rand) (*artifact.Artifact, error) {
|
||||
buildPipeline := manifest.NewBuild(m, runner, repos)
|
||||
buildPipeline.Checkpoint()
|
||||
|
||||
osPipeline := manifest.NewOS(m, buildPipeline, img.Platform, repos)
|
||||
osPipeline.PartitionTable = img.PartitionTable
|
||||
osPipeline.OSCustomizations = img.OSCustomizations
|
||||
osPipeline.Environment = img.Environment
|
||||
osPipeline.Workload = img.Workload
|
||||
osPipeline.NoBLS = img.NoBLS
|
||||
osPipeline.OSProduct = img.OSProduct
|
||||
osPipeline.OSVersion = img.OSVersion
|
||||
osPipeline.OSNick = img.OSNick
|
||||
|
||||
imagePipeline := manifest.NewRawImage(m, buildPipeline, osPipeline)
|
||||
imagePipeline.PartTool = img.PartTool
|
||||
|
||||
var artifact *artifact.Artifact
|
||||
var artifactPipeline manifest.Pipeline
|
||||
switch img.Platform.GetImageFormat() {
|
||||
case platform.FORMAT_RAW:
|
||||
if img.Compression == "" {
|
||||
imagePipeline.Filename = img.Filename
|
||||
}
|
||||
artifactPipeline = imagePipeline
|
||||
artifact = imagePipeline.Export()
|
||||
case platform.FORMAT_QCOW2:
|
||||
qcow2Pipeline := manifest.NewQCOW2(m, buildPipeline, imagePipeline)
|
||||
if img.Compression == "" {
|
||||
qcow2Pipeline.Filename = img.Filename
|
||||
}
|
||||
qcow2Pipeline.Compat = img.Platform.GetQCOW2Compat()
|
||||
artifactPipeline = qcow2Pipeline
|
||||
artifact = qcow2Pipeline.Export()
|
||||
case platform.FORMAT_VHD:
|
||||
vpcPipeline := manifest.NewVPC(m, buildPipeline, imagePipeline)
|
||||
if img.Compression == "" {
|
||||
vpcPipeline.Filename = img.Filename
|
||||
}
|
||||
vpcPipeline.ForceSize = img.ForceSize
|
||||
artifactPipeline = vpcPipeline
|
||||
artifact = vpcPipeline.Export()
|
||||
case platform.FORMAT_VMDK:
|
||||
vmdkPipeline := manifest.NewVMDK(m, buildPipeline, imagePipeline)
|
||||
if img.Compression == "" {
|
||||
vmdkPipeline.Filename = img.Filename
|
||||
}
|
||||
artifactPipeline = vmdkPipeline
|
||||
artifact = vmdkPipeline.Export()
|
||||
case platform.FORMAT_OVA:
|
||||
vmdkPipeline := manifest.NewVMDK(m, buildPipeline, imagePipeline)
|
||||
ovfPipeline := manifest.NewOVF(m, buildPipeline, vmdkPipeline)
|
||||
artifactPipeline := manifest.NewTar(m, buildPipeline, ovfPipeline, "archive")
|
||||
artifactPipeline.Format = osbuild.TarArchiveFormatOldgnu
|
||||
artifactPipeline.RootNode = osbuild.TarRootNodeOmit
|
||||
artifactPipeline.Filename = img.Filename
|
||||
artifact = artifactPipeline.Export()
|
||||
case platform.FORMAT_GCE:
|
||||
// NOTE(akoutsou): temporary workaround; filename required for GCP
|
||||
// TODO: define internal raw filename on image type
|
||||
imagePipeline.Filename = "disk.raw"
|
||||
archivePipeline := manifest.NewTar(m, buildPipeline, imagePipeline, "archive")
|
||||
archivePipeline.Format = osbuild.TarArchiveFormatOldgnu
|
||||
archivePipeline.RootNode = osbuild.TarRootNodeOmit
|
||||
// these are required to successfully import the image to GCP
|
||||
archivePipeline.ACLs = common.ToPtr(false)
|
||||
archivePipeline.SELinux = common.ToPtr(false)
|
||||
archivePipeline.Xattrs = common.ToPtr(false)
|
||||
archivePipeline.Filename = img.Filename // filename extension will determine compression
|
||||
default:
|
||||
panic("invalid image format for image kind")
|
||||
}
|
||||
|
||||
switch img.Compression {
|
||||
case "xz":
|
||||
xzPipeline := manifest.NewXZ(m, buildPipeline, artifactPipeline)
|
||||
xzPipeline.Filename = img.Filename
|
||||
artifact = xzPipeline.Export()
|
||||
case "":
|
||||
// do nothing
|
||||
default:
|
||||
// panic on unknown strings
|
||||
panic(fmt.Sprintf("unsupported compression type %q", img.Compression))
|
||||
}
|
||||
|
||||
return artifact, nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue