Brad Beam da1f73249f fix(machined): Clean up installation process
This also includes a fix for #955 which had the unintended side effect
of breaking image creation ( since it would attempt to grow the filesystem
always ).

The refactor standardizes around looking for the DATA and ESP labels to
discover any existing installations/filesystems. If none are found, an
installation will proceed -- for both image creation and bare metal.
During bootup, the DATA partition will always attempt to expand/grow.

This also introduces a new phase to verify the installation through the
existance of /boot/installed ( migrated from install stage ).

Signed-off-by: Brad Beam <brad.beam@talos-systems.com>
2019-08-08 22:10:14 -05:00

171 lines
3.7 KiB
Go

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package syslinux
import (
"bytes"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"text/template"
"github.com/pkg/errors"
)
const syslinuxCfgTpl = `DEFAULT {{ .Default }}
SAY Talos
{{- range .Labels }}
INCLUDE /{{ .Root }}/include.cfg
{{- end }}`
const syslinuxLabelTpl = `LABEL {{ .Root }}
KERNEL {{ .Kernel }}
INITRD {{ .Initrd }}
APPEND {{ .Append }}
`
const (
gptmbrbin = "/usr/lib/syslinux/gptmbr.bin"
syslinuxefi = "/usr/lib/syslinux/syslinux.efi"
ldlinuxe64 = "/usr/lib/syslinux/ldlinux.e64"
)
// Cfg reprsents the syslinux.cfg file.
type Cfg struct {
Default string
Labels []*Label
}
// Label reprsents a label in the syslinux.cfg file.
type Label struct {
Root string
Kernel string
Initrd string
Append string
}
// Syslinux represents the syslinux bootloader.
type Syslinux struct{}
// Prepare implements the Bootloader interface. It works by writing
// gptmbr.bin to a block device.
func Prepare(dev string) (err error) {
b, err := ioutil.ReadFile(gptmbrbin)
if err != nil {
return err
}
f, err := os.OpenFile(dev, os.O_WRONLY, os.ModeDevice)
if err != nil {
return err
}
// nolint: errcheck
defer f.Close()
if _, err := f.Write(b); err != nil {
return err
}
return nil
}
// Install implements the Bootloader interface. It sets up syslinux with the
// specified kernel parameters.
func Install(base string, config interface{}) (err error) {
syslinuxcfg, ok := config.(*Cfg)
if !ok {
return errors.New("expected a syslinux config")
}
efiDir := filepath.Join(base, "EFI", "BOOT")
if err = os.MkdirAll(efiDir, 0700); err != nil {
return err
}
input, err := ioutil.ReadFile(syslinuxefi)
if err != nil {
return err
}
err = ioutil.WriteFile(efiDir+"/BOOTX64.EFI", input, 0600)
if err != nil {
return err
}
input, err = ioutil.ReadFile(ldlinuxe64)
if err != nil {
return err
}
err = ioutil.WriteFile(efiDir+"/ldlinux.e64", input, 0600)
if err != nil {
return err
}
paths := []string{filepath.Join(base, "syslinux", "syslinux.cfg"), filepath.Join(base, "EFI", "syslinux", "syslinux.cfg")}
for _, path := range paths {
if err = WriteSyslinuxCfg(base, path, syslinuxcfg); err != nil {
return err
}
}
if err = cmd("extlinux", "--install", filepath.Dir(paths[0])); err != nil {
return errors.Wrap(err, "failed to install extlinux")
}
return nil
}
// WriteSyslinuxCfg write syslinux.cfg to disk.
func WriteSyslinuxCfg(base, path string, syslinuxcfg *Cfg) (err error) {
b := []byte{}
wr := bytes.NewBuffer(b)
t := template.Must(template.New("syslinux").Parse(syslinuxCfgTpl))
if err = t.Execute(wr, syslinuxcfg); err != nil {
return err
}
dir := filepath.Dir(path)
if err = os.MkdirAll(dir, os.ModeDir); err != nil {
return err
}
log.Printf("writing %s to disk", path)
if err = ioutil.WriteFile(path, wr.Bytes(), 0600); err != nil {
return err
}
for _, label := range syslinuxcfg.Labels {
b = []byte{}
wr = bytes.NewBuffer(b)
t = template.Must(template.New("syslinux").Parse(syslinuxLabelTpl))
if err = t.Execute(wr, label); err != nil {
return err
}
dir = filepath.Join(base, label.Root)
if err = os.MkdirAll(dir, os.ModeDir); err != nil {
return err
}
log.Printf("writing syslinux label %s to disk", label.Root)
if err = ioutil.WriteFile(filepath.Join(dir, "include.cfg"), wr.Bytes(), 0600); err != nil {
return err
}
}
return nil
}
func cmd(name string, args ...string) error {
cmd := exec.Command(name, args...)
err := cmd.Start()
if err != nil {
return err
}
return cmd.Wait()
}