diff --git a/cmd/osbuild-pipeline/main.go b/cmd/osbuild-pipeline/main.go index 7ac3de033..579ff3461 100644 --- a/cmd/osbuild-pipeline/main.go +++ b/cmd/osbuild-pipeline/main.go @@ -31,8 +31,8 @@ func main() { } } - f30 := distro.New("fedora-30") - pipeline, err := f30.Pipeline(blueprint, format) + d := distro.New("") + pipeline, err := d.Pipeline(blueprint, format) if err != nil { panic(err.Error()) } diff --git a/internal/distro/distro.go b/internal/distro/distro.go index 6ba2c385c..42bbd6dcf 100644 --- a/internal/distro/distro.go +++ b/internal/distro/distro.go @@ -1,6 +1,13 @@ package distro import ( + "bufio" + "errors" + "io" + "log" + "os" + "strings" + "github.com/osbuild/osbuild-composer/internal/blueprint" "github.com/osbuild/osbuild-composer/internal/pipeline" ) @@ -33,16 +40,78 @@ func (e *InvalidOutputFormatError) Error() string { 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 +} diff --git a/internal/distro/osrelease_test.go b/internal/distro/osrelease_test.go new file mode 100644 index 000000000..490040bf3 --- /dev/null +++ b/internal/distro/osrelease_test.go @@ -0,0 +1,54 @@ +package distro + +import ( + "reflect" + "strings" + "testing" +) + +func TestOSRelease(t *testing.T) { + var cases = []struct { + Input string + OSRelease map[string]string + }{ + { + ``, + map[string]string{}, + }, + { + `NAME=Fedora +VERSION="30 (Workstation Edition)" +ID=fedora +VERSION_ID=30 +VERSION_CODENAME="" +PLATFORM_ID="platform:f30" +PRETTY_NAME="Fedora 30 (Workstation Edition)" +VARIANT="Workstation Edition" +VARIANT_ID=workstation`, + map[string]string{ + "NAME": "Fedora", + "VERSION": "30 (Workstation Edition)", + "ID": "fedora", + "VERSION_ID": "30", + "VERSION_CODENAME": "", + "PLATFORM_ID": "platform:f30", + "PRETTY_NAME": "Fedora 30 (Workstation Edition)", + "VARIANT": "Workstation Edition", + "VARIANT_ID": "workstation", + }, + }, + } + + for i, c := range cases { + r := strings.NewReader(c.Input) + + osrelease, err := readOSRelease(r) + if err != nil { + t.Fatalf("%d: readOSRelease: %v", i, err) + } + + if !reflect.DeepEqual(osrelease, c.OSRelease) { + t.Fatalf("%d: readOSRelease returned unexpected result: %#v", i, osrelease) + } + } +} diff --git a/internal/store/store.go b/internal/store/store.go index 102dcd301..6816be987 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -396,8 +396,8 @@ func (s *Store) PushCompose(composeID uuid.UUID, bp *blueprint.Blueprint, compos targets := []*target.Target{ target.NewLocalTarget(target.NewLocalTargetOptions("/var/lib/osbuild-composer/outputs/" + composeID.String())), } - f30 := distro.New("fedora-30") - pipeline, err := f30.Pipeline(bp, composeType) + d := distro.New("") + pipeline, err := d.Pipeline(bp, composeType) if err != nil { return err } @@ -475,8 +475,8 @@ func (s *Store) GetImage(composeID uuid.UUID) (*Image, error) { if compose.QueueStatus != "FINISHED" { return nil, &InvalidRequestError{"compose was not finished"} } - f30 := distro.New("fedora-30") - name, mime, err := f30.FilenameFromType(compose.OutputType) + d := distro.New("") + name, mime, err := d.FilenameFromType(compose.OutputType) if err != nil { panic("invalid output type") } diff --git a/internal/weldr/api.go b/internal/weldr/api.go index fb451547a..818c7c1c6 100644 --- a/internal/weldr/api.go +++ b/internal/weldr/api.go @@ -850,8 +850,8 @@ func (api *API) composeTypesHandler(writer http.ResponseWriter, request *http.Re Types []composeType `json:"types"` } - f30 := distro.New("fedora-30") - for _, format := range f30.ListOutputFormats() { + d := distro.New("") + for _, format := range d.ListOutputFormats() { reply.Types = append(reply.Types, composeType{format, true}) }