Blame internal/distro/distro.go

Packit 63bb0d
package distro
Packit 63bb0d
Packit 63bb0d
import (
Packit 63bb0d
	"bufio"
Packit 63bb0d
	"encoding/json"
Packit 63bb0d
	"errors"
Packit 63bb0d
	"fmt"
Packit 63bb0d
	"io"
Packit Service 509fd4
	"io/ioutil"
Packit 63bb0d
	"os"
Packit 63bb0d
	"sort"
Packit 63bb0d
	"strings"
Packit 63bb0d
Packit 63bb0d
	"github.com/osbuild/osbuild-composer/internal/blueprint"
Packit 63bb0d
	"github.com/osbuild/osbuild-composer/internal/rpmmd"
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
// A Distro represents composer's notion of what a given distribution is.
Packit 63bb0d
type Distro interface {
Packit 63bb0d
	// Returns the name of the distro.
Packit 63bb0d
	Name() string
Packit 63bb0d
Packit 63bb0d
	// Returns the module platform id of the distro. This is used by DNF
Packit 63bb0d
	// for modularity support.
Packit 63bb0d
	ModulePlatformID() string
Packit 63bb0d
Packit 63bb0d
	// Returns a sorted list of the names of the architectures this distro
Packit 63bb0d
	// supports.
Packit 63bb0d
	ListArches() []string
Packit 63bb0d
Packit 63bb0d
	// Returns an object representing the given architecture as support
Packit 63bb0d
	// by this distro.
Packit 63bb0d
	GetArch(arch string) (Arch, error)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// An Arch represents a given distribution's support for a given architecture.
Packit 63bb0d
type Arch interface {
Packit 63bb0d
	// Returns the name of the architecture.
Packit 63bb0d
	Name() string
Packit 63bb0d
Packit 63bb0d
	// Returns a sorted list of the names of the image types this architecture
Packit 63bb0d
	// supports.
Packit 63bb0d
	ListImageTypes() []string
Packit 63bb0d
Packit 63bb0d
	// Returns an object representing a given image format for this architecture,
Packit 63bb0d
	// on this distro.
Packit 63bb0d
	GetImageType(imageType string) (ImageType, error)
Packit 63bb0d
Packit 63bb0d
	// Returns the parent distro
Packit 63bb0d
	Distro() Distro
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// An ImageType represents a given distribution's support for a given Image Type
Packit 63bb0d
// for a given architecture.
Packit 63bb0d
type ImageType interface {
Packit 63bb0d
	// Returns the name of the image type.
Packit 63bb0d
	Name() string
Packit 63bb0d
Packit 63bb0d
	// Returns the parent architecture
Packit 63bb0d
	Arch() Arch
Packit 63bb0d
Packit 63bb0d
	// Returns the canonical filename for the image type.
Packit 63bb0d
	Filename() string
Packit 63bb0d
Packit 63bb0d
	// Retrns the MIME-type for the image type.
Packit 63bb0d
	MIMEType() string
Packit 63bb0d
Packit Service bcdfb1
	// Returns the default OSTree ref for the image type.
Packit Service bcdfb1
	OSTreeRef() string
Packit Service bcdfb1
Packit 63bb0d
	// Returns the proper image size for a given output format. If the input size
Packit 63bb0d
	// is 0 the default value for the format will be returned.
Packit 63bb0d
	Size(size uint64) uint64
Packit 63bb0d
Packit Service 15f37d
	// Returns the sets of packages to include and exclude when building the image.
Packit Service 15f37d
	// Indexed by a string label. How each set is labeled and used depends on the
Packit Service 15f37d
	// image type.
Packit Service 15f37d
	PackageSets(bp blueprint.Blueprint) map[string]rpmmd.PackageSet
Packit 63bb0d
Packit Service 15f37d
	// Returns the names of the stages that will produce the build output.
Packit Service 15f37d
	Exports() []string
Packit 63bb0d
Packit 63bb0d
	// Returns an osbuild manifest, containing the sources and pipeline necessary
Packit 63bb0d
	// to build an image, given output format with all packages and customizations
Packit Service 15f37d
	// specified in the given blueprint. The packageSpecSets must be labelled in
Packit Service 15f37d
	// the same way as the originating PackageSets.
Packit Service 15f37d
	Manifest(b *blueprint.Customizations, options ImageOptions, repos []rpmmd.RepoConfig, packageSpecSets map[string][]rpmmd.PackageSpec, seed int64) (Manifest, error)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// The ImageOptions specify options for a specific image build
Packit 63bb0d
type ImageOptions struct {
Packit Service 509fd4
	OSTree       OSTreeImageOptions
Packit Service 509fd4
	Size         uint64
Packit Service 509fd4
	Subscription *SubscriptionImageOptions
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// The OSTreeImageOptions specify ostree-specific image options
Packit 63bb0d
type OSTreeImageOptions struct {
Packit 63bb0d
	Ref    string
Packit 63bb0d
	Parent string
Packit Service 15f37d
	URL    string
Packit 63bb0d
}
Packit 63bb0d
Packit Service 509fd4
// The SubscriptionImageOptions specify subscription-specific image options
Packit Service 509fd4
// ServerUrl denotes the host to register the system with
Packit Service 509fd4
// BaseUrl specifies the repository URL for DNF
Packit Service 509fd4
type SubscriptionImageOptions struct {
Packit Service 509fd4
	Organization  int
Packit Service 509fd4
	ActivationKey string
Packit Service 509fd4
	ServerUrl     string
Packit Service 509fd4
	BaseUrl       string
Packit Service 509fd4
	Insights      bool
Packit Service 509fd4
}
Packit Service 509fd4
Packit 63bb0d
// A Manifest is an opaque JSON object, which is a valid input to osbuild
Packit 63bb0d
type Manifest []byte
Packit 63bb0d
Packit 63bb0d
func (m Manifest) MarshalJSON() ([]byte, error) {
Packit 63bb0d
	return json.RawMessage(m).MarshalJSON()
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (m *Manifest) UnmarshalJSON(payload []byte) error {
Packit 63bb0d
	var raw json.RawMessage
Packit 63bb0d
	err := (&raw).UnmarshalJSON(payload)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return err
Packit 63bb0d
	}
Packit 63bb0d
	*m = Manifest(raw)
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
type Registry struct {
Packit 63bb0d
	distros map[string]Distro
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func NewRegistry(distros ...Distro) (*Registry, error) {
Packit 63bb0d
	reg := &Registry{
Packit 63bb0d
		distros: make(map[string]Distro),
Packit 63bb0d
	}
Packit 63bb0d
	for _, distro := range distros {
Packit 63bb0d
		name := distro.Name()
Packit 63bb0d
		if _, exists := reg.distros[name]; exists {
Packit 63bb0d
			return nil, fmt.Errorf("NewRegistry: passed two distros with the same name: %s", distro.Name())
Packit 63bb0d
		}
Packit 63bb0d
		reg.distros[name] = distro
Packit 63bb0d
	}
Packit 63bb0d
	return reg, nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (r *Registry) GetDistro(name string) Distro {
Packit 63bb0d
	distro, ok := r.distros[name]
Packit 63bb0d
	if !ok {
Packit 63bb0d
		return nil
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return distro
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// List returns the names of all distros in a Registry, sorted alphabetically.
Packit 63bb0d
func (r *Registry) List() []string {
Packit 63bb0d
	list := []string{}
Packit 63bb0d
	for _, distro := range r.distros {
Packit 63bb0d
		list = append(list, distro.Name())
Packit 63bb0d
	}
Packit 63bb0d
	sort.Strings(list)
Packit 63bb0d
	return list
Packit 63bb0d
}
Packit 63bb0d
Packit Service bcdfb1
func (r *Registry) FromHost() (Distro, bool, bool, error) {
Packit Service bcdfb1
	name, beta, isStream, err := GetHostDistroName()
Packit 63bb0d
	if err != nil {
Packit Service bcdfb1
		return nil, false, false, err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	d := r.GetDistro(name)
Packit 63bb0d
	if d == nil {
Packit Service bcdfb1
		return nil, false, false, errors.New("unknown distro: " + name)
Packit 63bb0d
	}
Packit 63bb0d
Packit Service bcdfb1
	return d, beta, isStream, nil
Packit 63bb0d
}
Packit 63bb0d
Packit Service bcdfb1
func GetHostDistroName() (string, bool, bool, error) {
Packit 63bb0d
	f, err := os.Open("/etc/os-release")
Packit 63bb0d
	if err != nil {
Packit Service bcdfb1
		return "", false, false, err
Packit 63bb0d
	}
Packit 63bb0d
	defer f.Close()
Packit 63bb0d
	osrelease, err := readOSRelease(f)
Packit 63bb0d
	if err != nil {
Packit Service bcdfb1
		return "", false, false, err
Packit 63bb0d
	}
Packit 63bb0d
Packit Service bcdfb1
	isStream := osrelease["NAME"] == "CentOS Stream"
Packit Service bcdfb1
Packit Service 509fd4
	// NOTE: We only consider major releases up until rhel 8.4
Packit 63bb0d
	version := strings.Split(osrelease["VERSION_ID"], ".")
Packit 63bb0d
	name := osrelease["ID"] + "-" + version[0]
Packit Service 509fd4
	if osrelease["ID"] == "rhel" && version[0] == "8" && version[1] >= "4" {
Packit Service 509fd4
		name = name + version[1]
Packit Service 509fd4
	}
Packit 63bb0d
Packit 63bb0d
	// TODO: We should probably index these things by the full CPE
Packit 63bb0d
	beta := strings.Contains(osrelease["CPE_NAME"], "beta")
Packit Service bcdfb1
	return name, beta, isStream, nil
Packit 63bb0d
}
Packit 63bb0d
Packit Service 509fd4
// GetRedHatRelease returns the content of /etc/redhat-release
Packit Service 509fd4
// without the trailing new-line.
Packit Service 509fd4
func GetRedHatRelease() (string, error) {
Packit Service 509fd4
	raw, err := ioutil.ReadFile("/etc/redhat-release")
Packit Service 509fd4
	if err != nil {
Packit Service 509fd4
		return "", fmt.Errorf("cannot read /etc/redhat-release: %v", err)
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	//Remove the trailing new-line.
Packit Service 509fd4
	redHatRelease := strings.TrimSpace(string(raw))
Packit Service 509fd4
Packit Service 509fd4
	return redHatRelease, nil
Packit Service 509fd4
}
Packit Service 509fd4
Packit 63bb0d
func readOSRelease(r io.Reader) (map[string]string, error) {
Packit 63bb0d
	osrelease := make(map[string]string)
Packit 63bb0d
	scanner := bufio.NewScanner(r)
Packit 63bb0d
	for scanner.Scan() {
Packit 63bb0d
		line := strings.TrimSpace(scanner.Text())
Packit 63bb0d
		if len(line) == 0 {
Packit 63bb0d
			continue
Packit 63bb0d
		}
Packit 63bb0d
Packit 63bb0d
		parts := strings.SplitN(line, "=", 2)
Packit 63bb0d
		if len(parts) != 2 {
Packit 63bb0d
			return nil, errors.New("readOSRelease: invalid input")
Packit 63bb0d
		}
Packit 63bb0d
Packit 63bb0d
		key := strings.TrimSpace(parts[0])
Packit 63bb0d
		value := strings.TrimSpace(parts[1])
Packit 63bb0d
		if value[0] == '"' {
Packit 63bb0d
			if len(value) < 2 || value[len(value)-1] != '"' {
Packit 63bb0d
				return nil, errors.New("readOSRelease: invalid input")
Packit 63bb0d
			}
Packit 63bb0d
			value = value[1 : len(value)-1]
Packit 63bb0d
		}
Packit 63bb0d
Packit 63bb0d
		osrelease[key] = value
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return osrelease, nil
Packit 63bb0d
}