diff --git a/internal/distro/distro.go b/internal/distro/distro.go index f5102f415..da0efd0e6 100644 --- a/internal/distro/distro.go +++ b/internal/distro/distro.go @@ -16,9 +16,9 @@ import ( "github.com/osbuild/osbuild-composer/internal/rpmmd" ) +// A Distro represents composer's notion of what a given distribution is. type Distro interface { - // Returns the name of the distro. This is the same name that was - // passed to New(). + // Returns the name of the distro. Name() string // Return strong-typed distribution @@ -28,6 +28,10 @@ type Distro interface { // for modularity support. ModulePlatformID() string + // Returns an object representing the given architecture as support + // by this distro. + GetArch(arch string) (Arch, error) + // Returns a sorted list of the output formats this distro supports. ListOutputFormats() []string @@ -54,6 +58,49 @@ type Distro interface { Runner() string } +// 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) +} + +// 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 canonical filename for the image type. + Filename() string + + // Retrns the MIME-type for the image type. + MIMEType() 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 default packages to include and exclude when making the image + // type. + BasePackages() ([]string, []string) + + // Returns the build packages for the output type. + BuildPackages() []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. + Manifest(b *blueprint.Customizations, repos []rpmmd.RepoConfig, packageSpecs, buildPackageSpecs []rpmmd.PackageSpec, size uint64) (*osbuild.Manifest, error) +} + type Registry struct { distros map[common.Distribution]Distro } diff --git a/internal/distro/fedora30/distro.go b/internal/distro/fedora30/distro.go index d21134020..79c18953c 100644 --- a/internal/distro/fedora30/distro.go +++ b/internal/distro/fedora30/distro.go @@ -6,6 +6,7 @@ import ( "strconv" "github.com/osbuild/osbuild-composer/internal/common" + "github.com/osbuild/osbuild-composer/internal/distro" "github.com/osbuild/osbuild-composer/internal/osbuild" "github.com/google/uuid" @@ -15,12 +16,6 @@ import ( "github.com/osbuild/osbuild-composer/internal/rpmmd" ) -type Fedora30 struct { - arches map[string]arch - outputs map[string]output - buildPackages []string -} - type arch struct { Name string BootloaderPackages []string @@ -44,6 +39,103 @@ type output struct { const Distro = common.Fedora30 const ModulePlatformID = "platform:f30" +type Fedora30 struct { + arches map[string]arch + outputs map[string]output + buildPackages []string +} + +type Fedora30Arch struct { + name string + distro *Fedora30 + arch *arch +} + +type Fedora30ImageType struct { + name string + arch *Fedora30Arch + output *output +} + +func (d *Fedora30) GetArch(arch string) (distro.Arch, error) { + a, exists := d.arches[arch] + if !exists { + return nil, errors.New("invalid architecture: " + arch) + } + + return &Fedora30Arch{ + name: arch, + distro: d, + arch: &a, + }, nil +} + +func (a *Fedora30Arch) Name() string { + return a.name +} + +func (a *Fedora30Arch) ListImageTypes() []string { + return a.distro.ListOutputFormats() +} + +func (a *Fedora30Arch) GetImageType(imageType string) (distro.ImageType, error) { + t, exists := a.distro.outputs[imageType] + if !exists { + return nil, errors.New("invalid image type: " + imageType) + } + + return &Fedora30ImageType{ + name: imageType, + arch: a, + output: &t, + }, nil +} + +func (t *Fedora30ImageType) Name() string { + return t.name +} + +func (t *Fedora30ImageType) Filename() string { + return t.output.Name +} + +func (t *Fedora30ImageType) MIMEType() string { + return t.output.MimeType +} + +func (t *Fedora30ImageType) Size(size uint64) uint64 { + return t.arch.distro.GetSizeForOutputType(t.name, size) +} + +func (t *Fedora30ImageType) BasePackages() ([]string, []string) { + packages := t.output.Packages + if t.output.Bootable { + packages = append(packages, t.arch.arch.BootloaderPackages...) + } + + return packages, t.output.ExcludedPackages +} + +func (t *Fedora30ImageType) BuildPackages() []string { + return append(t.arch.distro.buildPackages, t.arch.arch.BuildPackages...) +} + +func (t *Fedora30ImageType) Manifest(c *blueprint.Customizations, + repos []rpmmd.RepoConfig, + packageSpecs, + buildPackageSpecs []rpmmd.PackageSpec, + size uint64) (*osbuild.Manifest, error) { + pipeline, err := t.arch.distro.pipeline(c, repos, packageSpecs, buildPackageSpecs, t.arch.name, t.name, size) + if err != nil { + return nil, err + } + + return &osbuild.Manifest{ + Sources: *t.arch.distro.sources(append(packageSpecs, buildPackageSpecs...)), + Pipeline: *pipeline, + }, nil +} + func New() *Fedora30 { const GigaByte = 1024 * 1024 * 1024 diff --git a/internal/distro/fedora31/distro.go b/internal/distro/fedora31/distro.go index 1e810e9b9..449bd03fd 100644 --- a/internal/distro/fedora31/distro.go +++ b/internal/distro/fedora31/distro.go @@ -6,6 +6,7 @@ import ( "strconv" "github.com/osbuild/osbuild-composer/internal/common" + "github.com/osbuild/osbuild-composer/internal/distro" "github.com/osbuild/osbuild-composer/internal/osbuild" "github.com/google/uuid" @@ -15,12 +16,6 @@ import ( "github.com/osbuild/osbuild-composer/internal/rpmmd" ) -type Fedora31 struct { - arches map[string]arch - outputs map[string]output - buildPackages []string -} - type arch struct { Name string BootloaderPackages []string @@ -44,6 +39,103 @@ type output struct { const Distro = common.Fedora31 const ModulePlatformID = "platform:f31" +type Fedora31 struct { + arches map[string]arch + outputs map[string]output + buildPackages []string +} + +type Fedora31Arch struct { + name string + distro *Fedora31 + arch *arch +} + +type Fedora31ImageType struct { + name string + arch *Fedora31Arch + output *output +} + +func (d *Fedora31) GetArch(arch string) (distro.Arch, error) { + a, exists := d.arches[arch] + if !exists { + return nil, errors.New("invalid architecture: " + arch) + } + + return &Fedora31Arch{ + name: arch, + distro: d, + arch: &a, + }, nil +} + +func (a *Fedora31Arch) Name() string { + return a.name +} + +func (a *Fedora31Arch) ListImageTypes() []string { + return a.distro.ListOutputFormats() +} + +func (a *Fedora31Arch) GetImageType(imageType string) (distro.ImageType, error) { + t, exists := a.distro.outputs[imageType] + if !exists { + return nil, errors.New("invalid image type: " + imageType) + } + + return &Fedora31ImageType{ + name: imageType, + arch: a, + output: &t, + }, nil +} + +func (t *Fedora31ImageType) Name() string { + return t.name +} + +func (t *Fedora31ImageType) Filename() string { + return t.output.Name +} + +func (t *Fedora31ImageType) MIMEType() string { + return t.output.MimeType +} + +func (t *Fedora31ImageType) Size(size uint64) uint64 { + return t.arch.distro.GetSizeForOutputType(t.name, size) +} + +func (t *Fedora31ImageType) BasePackages() ([]string, []string) { + packages := t.output.Packages + if t.output.Bootable { + packages = append(packages, t.arch.arch.BootloaderPackages...) + } + + return packages, t.output.ExcludedPackages +} + +func (t *Fedora31ImageType) BuildPackages() []string { + return append(t.arch.distro.buildPackages, t.arch.arch.BuildPackages...) +} + +func (t *Fedora31ImageType) Manifest(c *blueprint.Customizations, + repos []rpmmd.RepoConfig, + packageSpecs, + buildPackageSpecs []rpmmd.PackageSpec, + size uint64) (*osbuild.Manifest, error) { + pipeline, err := t.arch.distro.pipeline(c, repos, packageSpecs, buildPackageSpecs, t.arch.name, t.name, size) + if err != nil { + return nil, err + } + + return &osbuild.Manifest{ + Sources: *t.arch.distro.sources(append(packageSpecs, buildPackageSpecs...)), + Pipeline: *pipeline, + }, nil +} + func New() *Fedora31 { const GigaByte = 1024 * 1024 * 1024 diff --git a/internal/distro/fedora32/distro.go b/internal/distro/fedora32/distro.go index dcbcfb591..9bfe50429 100644 --- a/internal/distro/fedora32/distro.go +++ b/internal/distro/fedora32/distro.go @@ -6,6 +6,7 @@ import ( "strconv" "github.com/osbuild/osbuild-composer/internal/common" + "github.com/osbuild/osbuild-composer/internal/distro" "github.com/osbuild/osbuild-composer/internal/osbuild" "github.com/google/uuid" @@ -15,12 +16,6 @@ import ( "github.com/osbuild/osbuild-composer/internal/rpmmd" ) -type Fedora32 struct { - arches map[string]arch - outputs map[string]output - buildPackages []string -} - type arch struct { Name string BootloaderPackages []string @@ -44,6 +39,103 @@ type output struct { const Distro = common.Fedora32 const ModulePlatformID = "platform:f32" +type Fedora32 struct { + arches map[string]arch + outputs map[string]output + buildPackages []string +} + +type Fedora32Arch struct { + name string + distro *Fedora32 + arch *arch +} + +type Fedora32ImageType struct { + name string + arch *Fedora32Arch + output *output +} + +func (d *Fedora32) GetArch(arch string) (distro.Arch, error) { + a, exists := d.arches[arch] + if !exists { + return nil, errors.New("invalid architecture: " + arch) + } + + return &Fedora32Arch{ + name: arch, + distro: d, + arch: &a, + }, nil +} + +func (a *Fedora32Arch) Name() string { + return a.name +} + +func (a *Fedora32Arch) ListImageTypes() []string { + return a.distro.ListOutputFormats() +} + +func (a *Fedora32Arch) GetImageType(imageType string) (distro.ImageType, error) { + t, exists := a.distro.outputs[imageType] + if !exists { + return nil, errors.New("invalid image type: " + imageType) + } + + return &Fedora32ImageType{ + name: imageType, + arch: a, + output: &t, + }, nil +} + +func (t *Fedora32ImageType) Name() string { + return t.name +} + +func (t *Fedora32ImageType) Filename() string { + return t.output.Name +} + +func (t *Fedora32ImageType) MIMEType() string { + return t.output.MimeType +} + +func (t *Fedora32ImageType) Size(size uint64) uint64 { + return t.arch.distro.GetSizeForOutputType(t.name, size) +} + +func (t *Fedora32ImageType) BasePackages() ([]string, []string) { + packages := t.output.Packages + if t.output.Bootable { + packages = append(packages, t.arch.arch.BootloaderPackages...) + } + + return packages, t.output.ExcludedPackages +} + +func (t *Fedora32ImageType) BuildPackages() []string { + return append(t.arch.distro.buildPackages, t.arch.arch.BuildPackages...) +} + +func (t *Fedora32ImageType) Manifest(c *blueprint.Customizations, + repos []rpmmd.RepoConfig, + packageSpecs, + buildPackageSpecs []rpmmd.PackageSpec, + size uint64) (*osbuild.Manifest, error) { + pipeline, err := t.arch.distro.pipeline(c, repos, packageSpecs, buildPackageSpecs, t.arch.name, t.name, size) + if err != nil { + return nil, err + } + + return &osbuild.Manifest{ + Sources: *t.arch.distro.sources(append(packageSpecs, buildPackageSpecs...)), + Pipeline: *pipeline, + }, nil +} + func New() *Fedora32 { const GigaByte = 1024 * 1024 * 1024 diff --git a/internal/distro/fedoratest/distro.go b/internal/distro/fedoratest/distro.go index 5ac971036..158c33ae4 100644 --- a/internal/distro/fedoratest/distro.go +++ b/internal/distro/fedoratest/distro.go @@ -5,13 +5,89 @@ import ( "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/osbuild" "github.com/osbuild/osbuild-composer/internal/rpmmd" ) +const ModulePlatformID = "platform:f30" + type FedoraTestDistro struct{} -const ModulePlatformID = "platform:f30" +type FedoraTestDistroArch struct { + name string + distro *FedoraTestDistro +} + +type FedoraTestDistroImageType struct { + name string + arch *FedoraTestDistroArch +} + +func (d *FedoraTestDistro) GetArch(arch string) (distro.Arch, error) { + if arch != "x86_64" { + return nil, errors.New("invalid architecture: " + arch) + } + + return &FedoraTestDistroArch{ + name: arch, + distro: d, + }, nil +} + +func (a *FedoraTestDistroArch) Name() string { + return a.name +} + +func (a *FedoraTestDistroArch) ListImageTypes() []string { + return a.distro.ListOutputFormats() +} + +func (a *FedoraTestDistroArch) GetImageType(imageType string) (distro.ImageType, error) { + if imageType != "qcow2" { + return nil, errors.New("invalid image type: " + imageType) + } + + return &FedoraTestDistroImageType{ + name: imageType, + arch: a, + }, nil +} + +func (t *FedoraTestDistroImageType) Name() string { + return t.name +} + +func (t *FedoraTestDistroImageType) Filename() string { + return "test.img" +} + +func (t *FedoraTestDistroImageType) MIMEType() string { + return "application/x-test" +} + +func (t *FedoraTestDistroImageType) Size(size uint64) uint64 { + return t.arch.distro.GetSizeForOutputType(t.name, size) +} + +func (t *FedoraTestDistroImageType) BasePackages() ([]string, []string) { + return nil, nil +} + +func (t *FedoraTestDistroImageType) BuildPackages() []string { + return nil +} + +func (t *FedoraTestDistroImageType) Manifest(c *blueprint.Customizations, + repos []rpmmd.RepoConfig, + packageSpecs, + buildPackageSpecs []rpmmd.PackageSpec, + size uint64) (*osbuild.Manifest, error) { + return &osbuild.Manifest{ + Pipeline: osbuild.Pipeline{}, + Sources: osbuild.Sources{}, + }, nil +} func New() *FedoraTestDistro { return &FedoraTestDistro{} diff --git a/internal/distro/rhel81/distro.go b/internal/distro/rhel81/distro.go index 42a475bee..ed4287b13 100644 --- a/internal/distro/rhel81/distro.go +++ b/internal/distro/rhel81/distro.go @@ -6,6 +6,7 @@ import ( "strconv" "github.com/osbuild/osbuild-composer/internal/common" + "github.com/osbuild/osbuild-composer/internal/distro" "github.com/osbuild/osbuild-composer/internal/osbuild" "github.com/google/uuid" @@ -15,12 +16,6 @@ import ( "github.com/osbuild/osbuild-composer/internal/rpmmd" ) -type RHEL81 struct { - arches map[string]arch - outputs map[string]output - buildPackages []string -} - type arch struct { Name string BootloaderPackages []string @@ -45,6 +40,103 @@ type output struct { const Distro = common.RHEL81 const ModulePlatformID = "platform:el8" +type RHEL81 struct { + arches map[string]arch + outputs map[string]output + buildPackages []string +} + +type RHEL81Arch struct { + name string + distro *RHEL81 + arch *arch +} + +type RHEL81ImageType struct { + name string + arch *RHEL81Arch + output *output +} + +func (d *RHEL81) GetArch(arch string) (distro.Arch, error) { + a, exists := d.arches[arch] + if !exists { + return nil, errors.New("invalid architecture: " + arch) + } + + return &RHEL81Arch{ + name: arch, + distro: d, + arch: &a, + }, nil +} + +func (a *RHEL81Arch) Name() string { + return a.name +} + +func (a *RHEL81Arch) ListImageTypes() []string { + return a.distro.ListOutputFormats() +} + +func (a *RHEL81Arch) GetImageType(imageType string) (distro.ImageType, error) { + t, exists := a.distro.outputs[imageType] + if !exists { + return nil, errors.New("invalid image type: " + imageType) + } + + return &RHEL81ImageType{ + name: imageType, + arch: a, + output: &t, + }, nil +} + +func (t *RHEL81ImageType) Name() string { + return t.name +} + +func (t *RHEL81ImageType) Filename() string { + return t.output.Name +} + +func (t *RHEL81ImageType) MIMEType() string { + return t.output.MimeType +} + +func (t *RHEL81ImageType) Size(size uint64) uint64 { + return t.arch.distro.GetSizeForOutputType(t.name, size) +} + +func (t *RHEL81ImageType) BasePackages() ([]string, []string) { + packages := t.output.Packages + if t.output.Bootable { + packages = append(packages, t.arch.arch.BootloaderPackages...) + } + + return packages, t.output.ExcludedPackages +} + +func (t *RHEL81ImageType) BuildPackages() []string { + return append(t.arch.distro.buildPackages, t.arch.arch.BuildPackages...) +} + +func (t *RHEL81ImageType) Manifest(c *blueprint.Customizations, + repos []rpmmd.RepoConfig, + packageSpecs, + buildPackageSpecs []rpmmd.PackageSpec, + size uint64) (*osbuild.Manifest, error) { + pipeline, err := t.arch.distro.pipeline(c, repos, packageSpecs, buildPackageSpecs, t.arch.name, t.name, size) + if err != nil { + return nil, err + } + + return &osbuild.Manifest{ + Sources: *t.arch.distro.sources(append(packageSpecs, buildPackageSpecs...)), + Pipeline: *pipeline, + }, nil +} + func New() *RHEL81 { const GigaByte = 1024 * 1024 * 1024 diff --git a/internal/distro/rhel82/distro.go b/internal/distro/rhel82/distro.go index f86588292..e97e7fefe 100644 --- a/internal/distro/rhel82/distro.go +++ b/internal/distro/rhel82/distro.go @@ -6,6 +6,7 @@ import ( "strconv" "github.com/osbuild/osbuild-composer/internal/common" + "github.com/osbuild/osbuild-composer/internal/distro" "github.com/osbuild/osbuild-composer/internal/osbuild" "github.com/google/uuid" @@ -15,12 +16,6 @@ import ( "github.com/osbuild/osbuild-composer/internal/rpmmd" ) -type RHEL82 struct { - arches map[string]arch - outputs map[string]output - buildPackages []string -} - type arch struct { Name string BootloaderPackages []string @@ -45,6 +40,103 @@ type output struct { const Distro = common.RHEL82 const ModulePlatformID = "platform:el8" +type RHEL82 struct { + arches map[string]arch + outputs map[string]output + buildPackages []string +} + +type RHEL82Arch struct { + name string + distro *RHEL82 + arch *arch +} + +type RHEL82ImageType struct { + name string + arch *RHEL82Arch + output *output +} + +func (d *RHEL82) GetArch(arch string) (distro.Arch, error) { + a, exists := d.arches[arch] + if !exists { + return nil, errors.New("invalid architecture: " + arch) + } + + return &RHEL82Arch{ + name: arch, + distro: d, + arch: &a, + }, nil +} + +func (a *RHEL82Arch) Name() string { + return a.name +} + +func (a *RHEL82Arch) ListImageTypes() []string { + return a.distro.ListOutputFormats() +} + +func (a *RHEL82Arch) GetImageType(imageType string) (distro.ImageType, error) { + t, exists := a.distro.outputs[imageType] + if !exists { + return nil, errors.New("invalid image type: " + imageType) + } + + return &RHEL82ImageType{ + name: imageType, + arch: a, + output: &t, + }, nil +} + +func (t *RHEL82ImageType) Name() string { + return t.name +} + +func (t *RHEL82ImageType) Filename() string { + return t.output.Name +} + +func (t *RHEL82ImageType) MIMEType() string { + return t.output.MimeType +} + +func (t *RHEL82ImageType) Size(size uint64) uint64 { + return t.arch.distro.GetSizeForOutputType(t.name, size) +} + +func (t *RHEL82ImageType) BasePackages() ([]string, []string) { + packages := t.output.Packages + if t.output.Bootable { + packages = append(packages, t.arch.arch.BootloaderPackages...) + } + + return packages, t.output.ExcludedPackages +} + +func (t *RHEL82ImageType) BuildPackages() []string { + return append(t.arch.distro.buildPackages, t.arch.arch.BuildPackages...) +} + +func (t *RHEL82ImageType) Manifest(c *blueprint.Customizations, + repos []rpmmd.RepoConfig, + packageSpecs, + buildPackageSpecs []rpmmd.PackageSpec, + size uint64) (*osbuild.Manifest, error) { + pipeline, err := t.arch.distro.pipeline(c, repos, packageSpecs, buildPackageSpecs, t.arch.name, t.name, size) + if err != nil { + return nil, err + } + + return &osbuild.Manifest{ + Sources: *t.arch.distro.sources(append(packageSpecs, buildPackageSpecs...)), + Pipeline: *pipeline, + }, nil +} + func New() *RHEL82 { const GigaByte = 1024 * 1024 * 1024 diff --git a/internal/distro/test/distro.go b/internal/distro/test/distro.go index 5893570c0..6cf56a3a2 100644 --- a/internal/distro/test/distro.go +++ b/internal/distro/test/distro.go @@ -4,15 +4,68 @@ import ( "errors" "github.com/osbuild/osbuild-composer/internal/blueprint" + "github.com/osbuild/osbuild-composer/internal/distro" "github.com/osbuild/osbuild-composer/internal/osbuild" "github.com/osbuild/osbuild-composer/internal/rpmmd" ) type TestDistro struct{} +type TestArch struct{} +type TestImageType struct{} const Name = "test-distro" const ModulePlatformID = "platform:test" +func (d *TestDistro) GetArch(arch string) (distro.Arch, error) { + if arch != "test_arch" { + return nil, errors.New("invalid arch: " + arch) + } + return &TestArch{}, nil +} + +func (a *TestArch) Name() string { + return Name +} + +func (a *TestArch) ListImageTypes() []string { + return []string{"test-format"} +} + +func (a *TestArch) GetImageType(imageType string) (distro.ImageType, error) { + if imageType != "test_output" { + return nil, errors.New("invalid image type: " + imageType) + } + return &TestImageType{}, nil +} + +func (t *TestImageType) Name() string { + return "test-format" +} + +func (t *TestImageType) Filename() string { + return "test.img" +} + +func (t *TestImageType) MIMEType() string { + return "application/x-test" +} + +func (t *TestImageType) Size(size uint64) uint64 { + return 0 +} + +func (t *TestImageType) BasePackages() ([]string, []string) { + return nil, nil +} + +func (t *TestImageType) BuildPackages() []string { + return nil +} + +func (t *TestImageType) Manifest(b *blueprint.Customizations, repos []rpmmd.RepoConfig, packageSpecs, buildPackageSpecs []rpmmd.PackageSpec, size uint64) (*osbuild.Manifest, error) { + return &osbuild.Manifest{}, nil +} + func New() *TestDistro { return &TestDistro{} }