From bbd8dc338daefe2bd18698a0449cb6d2d302e637 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Sun, 22 Mar 2020 13:08:09 +0100 Subject: [PATCH] distro: introduce Arch and ImageType interfaces Objects implementing these interfaces will represent the architecture support for a given distro and the image type support for a given architecture distro combination, respectively. The idea is to always resolve to these objects early, and drop the equilavent methods from the distro interface. This means that we convert our input strings to real objects once, and then never have to verify their correctness again. Signed-off-by: Tom Gundersen --- internal/distro/distro.go | 51 ++++++++++++- internal/distro/fedora30/distro.go | 104 +++++++++++++++++++++++++-- internal/distro/fedora31/distro.go | 104 +++++++++++++++++++++++++-- internal/distro/fedora32/distro.go | 104 +++++++++++++++++++++++++-- internal/distro/fedoratest/distro.go | 78 +++++++++++++++++++- internal/distro/rhel81/distro.go | 104 +++++++++++++++++++++++++-- internal/distro/rhel82/distro.go | 104 +++++++++++++++++++++++++-- internal/distro/test/distro.go | 53 ++++++++++++++ 8 files changed, 669 insertions(+), 33 deletions(-) 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{} }