|
Packit Service |
4d2de5 |
package distro
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
import (
|
|
Packit Service |
4d2de5 |
"bufio"
|
|
Packit Service |
4d2de5 |
"encoding/json"
|
|
Packit Service |
4d2de5 |
"errors"
|
|
Packit Service |
4d2de5 |
"fmt"
|
|
Packit Service |
4d2de5 |
"io"
|
|
Packit Service |
4d2de5 |
"os"
|
|
Packit Service |
4d2de5 |
"sort"
|
|
Packit Service |
4d2de5 |
"strings"
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
|
Packit Service |
4d2de5 |
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
|
Packit Service |
4d2de5 |
)
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// A Distro represents composer's notion of what a given distribution is.
|
|
Packit Service |
4d2de5 |
type Distro interface {
|
|
Packit Service |
4d2de5 |
// Returns the name of the distro.
|
|
Packit Service |
4d2de5 |
Name() string
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// Returns the module platform id of the distro. This is used by DNF
|
|
Packit Service |
4d2de5 |
// for modularity support.
|
|
Packit Service |
4d2de5 |
ModulePlatformID() string
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// Returns a sorted list of the names of the architectures this distro
|
|
Packit Service |
4d2de5 |
// supports.
|
|
Packit Service |
4d2de5 |
ListArches() []string
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// Returns an object representing the given architecture as support
|
|
Packit Service |
4d2de5 |
// by this distro.
|
|
Packit Service |
4d2de5 |
GetArch(arch string) (Arch, error)
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// An Arch represents a given distribution's support for a given architecture.
|
|
Packit Service |
4d2de5 |
type Arch interface {
|
|
Packit Service |
4d2de5 |
// Returns the name of the architecture.
|
|
Packit Service |
4d2de5 |
Name() string
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// Returns a sorted list of the names of the image types this architecture
|
|
Packit Service |
4d2de5 |
// supports.
|
|
Packit Service |
4d2de5 |
ListImageTypes() []string
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// Returns an object representing a given image format for this architecture,
|
|
Packit Service |
4d2de5 |
// on this distro.
|
|
Packit Service |
4d2de5 |
GetImageType(imageType string) (ImageType, error)
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// Returns the parent distro
|
|
Packit Service |
4d2de5 |
Distro() Distro
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// An ImageType represents a given distribution's support for a given Image Type
|
|
Packit Service |
4d2de5 |
// for a given architecture.
|
|
Packit Service |
4d2de5 |
type ImageType interface {
|
|
Packit Service |
4d2de5 |
// Returns the name of the image type.
|
|
Packit Service |
4d2de5 |
Name() string
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// Returns the parent architecture
|
|
Packit Service |
4d2de5 |
Arch() Arch
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// Returns the canonical filename for the image type.
|
|
Packit Service |
4d2de5 |
Filename() string
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// Retrns the MIME-type for the image type.
|
|
Packit Service |
4d2de5 |
MIMEType() string
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// Returns the proper image size for a given output format. If the input size
|
|
Packit Service |
4d2de5 |
// is 0 the default value for the format will be returned.
|
|
Packit Service |
4d2de5 |
Size(size uint64) uint64
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// Returns the default packages to include and exclude when making the image
|
|
Packit Service |
4d2de5 |
// type.
|
|
Packit Service |
4d2de5 |
Packages(bp blueprint.Blueprint) ([]string, []string)
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// Returns the build packages for the output type.
|
|
Packit Service |
4d2de5 |
BuildPackages() []string
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// Returns an osbuild manifest, containing the sources and pipeline necessary
|
|
Packit Service |
4d2de5 |
// to build an image, given output format with all packages and customizations
|
|
Packit Service |
4d2de5 |
// specified in the given blueprint.
|
|
Packit Service |
4d2de5 |
Manifest(b *blueprint.Customizations, options ImageOptions, repos []rpmmd.RepoConfig, packageSpecs, buildPackageSpecs []rpmmd.PackageSpec) (Manifest, error)
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// The ImageOptions specify options for a specific image build
|
|
Packit Service |
4d2de5 |
type ImageOptions struct {
|
|
Packit Service |
4d2de5 |
OSTree OSTreeImageOptions
|
|
Packit Service |
4d2de5 |
Size uint64
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// The OSTreeImageOptions specify ostree-specific image options
|
|
Packit Service |
4d2de5 |
type OSTreeImageOptions struct {
|
|
Packit Service |
4d2de5 |
Ref string
|
|
Packit Service |
4d2de5 |
Parent string
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// A Manifest is an opaque JSON object, which is a valid input to osbuild
|
|
Packit Service |
4d2de5 |
type Manifest []byte
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func (m Manifest) MarshalJSON() ([]byte, error) {
|
|
Packit Service |
4d2de5 |
return json.RawMessage(m).MarshalJSON()
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func (m *Manifest) UnmarshalJSON(payload []byte) error {
|
|
Packit Service |
4d2de5 |
var raw json.RawMessage
|
|
Packit Service |
4d2de5 |
err := (&raw).UnmarshalJSON(payload)
|
|
Packit Service |
4d2de5 |
if err != nil {
|
|
Packit Service |
4d2de5 |
return err
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
*m = Manifest(raw)
|
|
Packit Service |
4d2de5 |
return nil
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
type Registry struct {
|
|
Packit Service |
4d2de5 |
distros map[string]Distro
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func NewRegistry(distros ...Distro) (*Registry, error) {
|
|
Packit Service |
4d2de5 |
reg := &Registry{
|
|
Packit Service |
4d2de5 |
distros: make(map[string]Distro),
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
for _, distro := range distros {
|
|
Packit Service |
4d2de5 |
name := distro.Name()
|
|
Packit Service |
4d2de5 |
if _, exists := reg.distros[name]; exists {
|
|
Packit Service |
4d2de5 |
return nil, fmt.Errorf("NewRegistry: passed two distros with the same name: %s", distro.Name())
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
reg.distros[name] = distro
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
return reg, nil
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func (r *Registry) GetDistro(name string) Distro {
|
|
Packit Service |
4d2de5 |
distro, ok := r.distros[name]
|
|
Packit Service |
4d2de5 |
if !ok {
|
|
Packit Service |
4d2de5 |
return nil
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
return distro
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// List returns the names of all distros in a Registry, sorted alphabetically.
|
|
Packit Service |
4d2de5 |
func (r *Registry) List() []string {
|
|
Packit Service |
4d2de5 |
list := []string{}
|
|
Packit Service |
4d2de5 |
for _, distro := range r.distros {
|
|
Packit Service |
4d2de5 |
list = append(list, distro.Name())
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
sort.Strings(list)
|
|
Packit Service |
4d2de5 |
return list
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func (r *Registry) FromHost() (Distro, bool, error) {
|
|
Packit Service |
4d2de5 |
name, beta, err := GetHostDistroName()
|
|
Packit Service |
4d2de5 |
if err != nil {
|
|
Packit Service |
4d2de5 |
return nil, false, err
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
d := r.GetDistro(name)
|
|
Packit Service |
4d2de5 |
if d == nil {
|
|
Packit Service |
4d2de5 |
return nil, false, errors.New("unknown distro: " + name)
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
return d, beta, nil
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func GetHostDistroName() (string, bool, error) {
|
|
Packit Service |
4d2de5 |
f, err := os.Open("/etc/os-release")
|
|
Packit Service |
4d2de5 |
if err != nil {
|
|
Packit Service |
4d2de5 |
return "", false, err
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
defer f.Close()
|
|
Packit Service |
4d2de5 |
osrelease, err := readOSRelease(f)
|
|
Packit Service |
4d2de5 |
if err != nil {
|
|
Packit Service |
4d2de5 |
return "", false, err
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// NOTE: We only consider major releases
|
|
Packit Service |
4d2de5 |
version := strings.Split(osrelease["VERSION_ID"], ".")
|
|
Packit Service |
4d2de5 |
name := osrelease["ID"] + "-" + version[0]
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
// TODO: We should probably index these things by the full CPE
|
|
Packit Service |
4d2de5 |
beta := strings.Contains(osrelease["CPE_NAME"], "beta")
|
|
Packit Service |
4d2de5 |
return name, beta, nil
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
func readOSRelease(r io.Reader) (map[string]string, error) {
|
|
Packit Service |
4d2de5 |
osrelease := make(map[string]string)
|
|
Packit Service |
4d2de5 |
scanner := bufio.NewScanner(r)
|
|
Packit Service |
4d2de5 |
for scanner.Scan() {
|
|
Packit Service |
4d2de5 |
line := strings.TrimSpace(scanner.Text())
|
|
Packit Service |
4d2de5 |
if len(line) == 0 {
|
|
Packit Service |
4d2de5 |
continue
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
parts := strings.SplitN(line, "=", 2)
|
|
Packit Service |
4d2de5 |
if len(parts) != 2 {
|
|
Packit Service |
4d2de5 |
return nil, errors.New("readOSRelease: invalid input")
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
key := strings.TrimSpace(parts[0])
|
|
Packit Service |
4d2de5 |
value := strings.TrimSpace(parts[1])
|
|
Packit Service |
4d2de5 |
if value[0] == '"' {
|
|
Packit Service |
4d2de5 |
if len(value) < 2 || value[len(value)-1] != '"' {
|
|
Packit Service |
4d2de5 |
return nil, errors.New("readOSRelease: invalid input")
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
value = value[1 : len(value)-1]
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
osrelease[key] = value
|
|
Packit Service |
4d2de5 |
}
|
|
Packit Service |
4d2de5 |
|
|
Packit Service |
4d2de5 |
return osrelease, nil
|
|
Packit Service |
4d2de5 |
}
|