Blame internal/distro/distro.go

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
}