debian-forge-composer/internal/distro/distro.go
Lars Karlitski f9cbc8593f distro: add DetectHost
lorax-composer uses the host's repositories for building images. This is
prone to accidental configuration errors and duplicates functionality
(adding custom repositories via the source API is much more explicit).

However, blueprints don't specify the distribution they're based on.
This is something they should do in the future to enable cross-distro
image builds. Until then, read `/etc/os-release` to detect the host OS
and use that as the base distro for a blueprint.

Detection works by concatenating the ID field with a `-` and the
VERSION_ID field. This mandates how additional distributions should
register themselves to the `distro` package.

If composer cannot build the detected distro, fall back to fedora-30.

Use this detection everywhere that fedora-30 was hard-coded before.
2019-11-10 17:23:14 +01:00

117 lines
2.6 KiB
Go

package distro
import (
"bufio"
"errors"
"io"
"log"
"os"
"strings"
"github.com/osbuild/osbuild-composer/internal/blueprint"
"github.com/osbuild/osbuild-composer/internal/pipeline"
)
type Distro interface {
// Returns a sorted list of the output formats this distro supports.
ListOutputFormats() []string
// Returns the canonical filename and MIME type for a given output
// format
FilenameFromType(outputFormat string) (string, string, error)
// Returns an osbuild pipeline that generates an image in the given
// output format with all packages and customizations specified in the
// given blueprint. `outputFormat` must be one returned by
// ListOutputFormats().
Pipeline(b *blueprint.Blueprint, outputFormat string) (*pipeline.Pipeline, error)
}
// An InvalidOutputFormatError is returned when a requested output format is
// not supported. The requested format is included as the error message.
type InvalidOutputFormatError struct {
Format string
}
func (e *InvalidOutputFormatError) Error() string {
return e.Format
}
var registered = map[string]Distro{}
func New(name string) Distro {
if name == "" {
distro, err := FromHost()
if err == nil {
return distro
} else {
log.Println("cannot detect distro from host: " + err.Error())
log.Println("falling back to 'fedora-30'")
return New("fedora-30")
}
}
distro, ok := registered[name]
if !ok {
panic("unknown distro: " + name)
}
return distro
}
func FromHost() (Distro, error) {
f, err := os.Open("/etc/os-release")
if err != nil {
return nil, err
}
defer f.Close()
osrelease, err := readOSRelease(f)
if err != nil {
return nil, err
}
name := osrelease["ID"] + "-" + osrelease["VERSION_ID"]
distro, ok := registered[name]
if !ok {
return nil, errors.New("unknown distro: " + name)
}
return distro, nil
}
func Register(name string, distro Distro) {
if _, exists := registered[name]; exists {
panic("a distro with this name already exists: " + name)
}
registered[name] = distro
}
func readOSRelease(r io.Reader) (map[string]string, error) {
osrelease := make(map[string]string)
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if len(line) == 0 {
continue
}
parts := strings.SplitN(line, "=", 2)
if len(parts) != 2 {
return nil, errors.New("readOSRelease: invalid input")
}
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
if value[0] == '"' {
if len(value) < 2 || value[len(value) - 1] != '"' {
return nil, errors.New("readOSRelease: invalid input")
}
value = value[1:len(value) - 1]
}
osrelease[key] = value
}
return osrelease, nil
}