refactor: move setup logic into machined

The responsibility of init should only be to mount the rootfs. This
change moves Talos specific logic into machined. This will allow us to
define a version of Talos in a single binary instead of split across
two. This will enable cleaner upgrades and helps make the codebase
easier to reason about.

Signed-off-by: Andrew Rynhard <andrew@andrewrynhard.com>
This commit is contained in:
Andrew Rynhard 2019-07-26 06:28:34 +00:00
parent a7d76b9410
commit b7a9acbe88
22 changed files with 159 additions and 183 deletions

View File

@ -218,14 +218,6 @@ COPY --from=rootfs-base /rootfs /
FROM build AS initramfs-archive
WORKDIR /initramfs
COPY --from=docker.io/autonomy/fhs:8467184 / /initramfs
COPY --from=docker.io/autonomy/ca-certificates:20f39f7 / /initramfs
COPY --from=docker.io/autonomy/dosfstools:767dee6 / /initramfs
COPY --from=docker.io/autonomy/musl:9bc7430 / /initramfs
COPY --from=docker.io/autonomy/syslinux:85e1f9c / /initramfs
COPY --from=docker.io/autonomy/xfsprogs:5e50579 / /initramfs
COPY ./hack/cleanup.sh /toolchain/bin/cleanup.sh
RUN cleanup.sh /initramfs
COPY --from=rootfs-squashfs /rootfs.sqsh .
COPY --from=init /init .
RUN set -o pipefail && find . 2>/dev/null | cpio -H newc -o | xz -v -C crc32 -0 -e -T 0 -z >/initramfs.xz

View File

@ -9,14 +9,8 @@ import (
"os"
"github.com/pkg/errors"
"github.com/talos-systems/talos/internal/app/init/internal/platform"
"github.com/talos-systems/talos/internal/pkg/constants"
"github.com/talos-systems/talos/internal/pkg/kernel"
"github.com/talos-systems/talos/internal/pkg/network"
"github.com/talos-systems/talos/internal/pkg/rootfs"
"github.com/talos-systems/talos/internal/pkg/rootfs/mount"
"github.com/talos-systems/talos/internal/pkg/security/kspp"
"github.com/talos-systems/talos/pkg/userdata"
"golang.org/x/sys/unix"
)
@ -39,72 +33,26 @@ func initram() (err error) {
if initializer, err = mount.NewInitializer(constants.NewRoot); err != nil {
return err
}
// Mount the special devices.
if err = initializer.InitSpecial(); err != nil {
return err
}
// Setup logging to /dev/kmsg.
_, err = kmsg("[talos] [initramfs]")
if err != nil {
return err
}
// Enforce KSPP kernel parameters.
log.Println("checking for KSPP kernel parameters")
if err = kspp.EnforceKSPPKernelParameters(); err != nil {
return err
}
// Setup hostname if provided.
var hostname *string
if hostname = kernel.Cmdline().Get(constants.KernelParamHostname).First(); hostname != nil {
log.Println("setting hostname")
if err = unix.Sethostname([]byte(*hostname)); err != nil {
return err
}
log.Printf("hostname is: %s", *hostname)
}
// Discover the platform.
log.Println("discovering the platform")
var p platform.Platform
if p, err = platform.NewPlatform(); err != nil {
return err
}
log.Printf("platform is: %s", p.Name())
// Setup basic network.
if err = network.InitNetwork(); err != nil {
return err
}
// Retrieve the user data.
var data *userdata.UserData
log.Printf("retrieving user data")
if data, err = p.UserData(); err != nil {
log.Printf("encountered error retrieving userdata: %v", err)
return err
}
// Setup custom network.
if err = network.SetupNetwork(data); err != nil {
return err
}
// Perform any tasks required by a particular platform.
log.Printf("performing platform specific tasks")
if err = p.Prepare(data); err != nil {
return err
}
// Mount the owned partitions.
log.Printf("mounting the partitions")
if err = initializer.InitOwned(); err != nil {
return err
}
// Install handles additional system setup
if err = p.Install(data); err != nil {
return err
}
// Prepare the necessary files in the rootfs.
log.Println("preparing the root filesystem")
if err = rootfs.Prepare(constants.NewRoot, false, data); err != nil {
return err
}
// Perform the equivalent of switch_root.
log.Println("entering the new root")
log.Println("mounting the rootfs")
if err = initializer.Rootfs(); err != nil {
return err
}
// Perform the equivalent of switch_root.
log.Println("entering the rootfs")
if err = initializer.Switch(); err != nil {
return err
}
@ -113,10 +61,6 @@ func initram() (err error) {
}
func main() {
if err := os.Setenv("PATH", constants.PATH); err != nil {
panic(errors.New("error setting PATH"))
}
if err := initram(); err != nil {
panic(errors.Wrap(err, "early boot failed"))
}

View File

@ -6,13 +6,13 @@ package platform
import (
"github.com/pkg/errors"
"github.com/talos-systems/talos/internal/app/init/internal/platform/baremetal"
"github.com/talos-systems/talos/internal/app/init/internal/platform/cloud/aws"
"github.com/talos-systems/talos/internal/app/init/internal/platform/cloud/azure"
"github.com/talos-systems/talos/internal/app/init/internal/platform/cloud/googlecloud"
"github.com/talos-systems/talos/internal/app/init/internal/platform/cloud/packet"
"github.com/talos-systems/talos/internal/app/init/internal/platform/cloud/vmware"
"github.com/talos-systems/talos/internal/app/init/internal/platform/iso"
"github.com/talos-systems/talos/internal/app/machined/internal/platform/baremetal"
"github.com/talos-systems/talos/internal/app/machined/internal/platform/cloud/aws"
"github.com/talos-systems/talos/internal/app/machined/internal/platform/cloud/azure"
"github.com/talos-systems/talos/internal/app/machined/internal/platform/cloud/googlecloud"
"github.com/talos-systems/talos/internal/app/machined/internal/platform/cloud/packet"
"github.com/talos-systems/talos/internal/app/machined/internal/platform/cloud/vmware"
"github.com/talos-systems/talos/internal/app/machined/internal/platform/iso"
"github.com/talos-systems/talos/internal/pkg/constants"
"github.com/talos-systems/talos/internal/pkg/kernel"
"github.com/talos-systems/talos/pkg/userdata"

View File

@ -17,13 +17,17 @@ import (
"time"
"github.com/pkg/errors"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/reg"
"github.com/talos-systems/talos/internal/app/machined/pkg/system"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/services"
"github.com/talos-systems/talos/internal/pkg/constants"
"github.com/talos-systems/talos/internal/pkg/grpc/factory"
"github.com/talos-systems/talos/internal/pkg/network"
"github.com/talos-systems/talos/internal/pkg/rootfs"
"github.com/talos-systems/talos/internal/pkg/rootfs/etc"
"github.com/talos-systems/talos/internal/pkg/rootfs/mount"
"github.com/talos-systems/talos/internal/pkg/security/kspp"
"github.com/talos-systems/talos/internal/pkg/startup"
"github.com/talos-systems/talos/pkg/userdata"
@ -128,27 +132,95 @@ func createOverlay(path string) error {
// nolint: gocyclo
func root() (err error) {
// Create /etc/os-release.
if err = etc.OSRelease(); err != nil {
return
}
if !*inContainer {
// Setup logging to /dev/kmsg.
if _, err = kmsg("[talos]"); err != nil {
return fmt.Errorf("failed to setup logging to /dev/kmsg: %v", err)
}
// Enforce KSPP kernel parameters.
log.Println("checking for KSPP kernel parameters")
if err = kspp.EnforceKSPPKernelParameters(); err != nil {
return err
}
// Create /etc/hosts.
log.Println("creating /etc/hosts")
if err = etc.Hosts(); err != nil {
return err
}
// Create /etc/resolv.conf.
log.Println("creating /etc/resolv.conf")
if err = etc.ResolvConf(); err != nil {
return
}
// Discover the platform.
var p platform.Platform
if p, err = platform.NewPlatform(); err != nil {
return err
}
log.Printf("platform is %s", p.Name())
// Setup basic network.
log.Println("setting up basic network")
if err = network.InitNetwork(); err != nil {
return err
}
// Retrieve the user data.
var data *userdata.UserData
log.Printf("retrieving user data")
if data, err = p.UserData(); err != nil {
log.Printf("encountered error retrieving userdata: %v", err)
return err
}
// Setup custom network.
log.Println("setting up user defined network")
if err = network.SetupNetwork(data); err != nil {
return err
}
// Perform any tasks required by a particular platform.
log.Printf("performing platform specific tasks")
if err = p.Prepare(data); err != nil {
return err
}
var initializer *mount.Initializer
if initializer, err = mount.NewInitializer(""); err != nil {
return err
}
// Mount the owned partitions.
log.Printf("mounting the owned partitions")
if err = initializer.InitOwned(); err != nil {
return err
}
// Install handles additional system setup
if err = p.Install(data); err != nil {
return err
}
// Prepare the necessary files in the rootfs.
log.Println("preparing the root filesystem")
if err = rootfs.Prepare("", false, data); err != nil {
return err
}
for _, overlay := range []string{"/etc/kubernetes", "/etc/cni", "/usr/libexec/kubernetes", "/usr/etc/udev", "/opt"} {
if err = createOverlay(overlay); err != nil {
return err
}
}
if err = unix.Mount("/var/hosts", "/etc/hosts", "", unix.MS_BIND, ""); err != nil {
return errors.Wrap(err, "failed to create bind mount for /etc/hosts")
}
if err = unix.Mount("/var/resolv.conf", "/etc/resolv.conf", "", unix.MS_BIND, ""); err != nil {
return errors.Wrap(err, "failed to create bind mount for /etc/resolv.conf")
}
if err = unix.Mount("/var/os-release", "/etc/os-release", "", unix.MS_BIND, ""); err != nil {
return errors.Wrap(err, "failed to create bind mount for /etc/os-release")
}
}
for _, p := range []string{"/var/log", "/var/lib/kubelet", "/var/log/pods"} {

View File

@ -5,18 +5,20 @@
package etc
import (
"bufio"
"bytes"
"encoding/hex"
"fmt"
"io/ioutil"
"net"
"os"
"path"
"path/filepath"
"strings"
"text/template"
"github.com/pkg/errors"
"github.com/talos-systems/talos/internal/pkg/constants"
"github.com/talos-systems/talos/internal/pkg/kernel"
"github.com/talos-systems/talos/internal/pkg/version"
"golang.org/x/sys/unix"
)
const hostsTemplate = `
@ -38,7 +40,21 @@ BUG_REPORT_URL="https://github.com/talos-systems/talos/issues"
`
// Hosts renders a valid /etc/hosts file and writes it to disk.
func Hosts(s, hostname, ip string) (err error) {
func Hosts() (err error) {
var h *string
if h = kernel.Cmdline().Get(constants.KernelParamHostname).First(); h != nil {
if err = unix.Sethostname([]byte(*h)); err != nil {
return err
}
}
ip := ip()
var hostname string
if hostname, err = os.Hostname(); err != nil {
return err
}
data := struct {
IP string
Hostname string
@ -58,8 +74,12 @@ func Hosts(s, hostname, ip string) (err error) {
return
}
if err := ioutil.WriteFile(filepath.Join(s, "/var/hosts"), writer.Bytes(), 0644); err != nil {
return fmt.Errorf("write /var/hosts: %v", err)
if err = ioutil.WriteFile("/run/hosts", writer.Bytes(), 0644); err != nil {
return fmt.Errorf("write /run/hosts: %v", err)
}
if err = unix.Mount("/run/hosts", "/etc/hosts", "", unix.MS_BIND, ""); err != nil {
return errors.Wrap(err, "failed to create bind mount for /etc/hosts")
}
return nil
@ -67,15 +87,17 @@ func Hosts(s, hostname, ip string) (err error) {
// ResolvConf copies the resolv.conf generated in the early boot to the new
// root.
func ResolvConf(s string) (err error) {
source, err := ioutil.ReadFile("/etc/resolv.conf")
if err != nil {
func ResolvConf() (err error) {
target := "/run/resolv.conf"
var f *os.File
if f, err = os.OpenFile(target, os.O_WRONLY|os.O_CREATE, 0644); err != nil {
return err
}
// nolint: errcheck
defer f.Close()
target := filepath.Join(s, "/var/resolv.conf")
if err = ioutil.WriteFile(target, source, 0644); err != nil {
return err
if err = unix.Mount("/run/resolv.conf", "/etc/resolv.conf", "", unix.MS_BIND, ""); err != nil {
return errors.Wrap(err, "failed to create bind mount for /etc/resolv.conf")
}
return nil
@ -83,7 +105,7 @@ func ResolvConf(s string) (err error) {
// OSRelease renders a valid /etc/os-release file and writes it to disk. The
// node's OS Image field is reported by the node from /etc/os-release.
func OSRelease(s string) (err error) {
func OSRelease() (err error) {
var v string
switch version.Tag {
case "none":
@ -112,46 +134,29 @@ func OSRelease(s string) (err error) {
return
}
if err := ioutil.WriteFile(path.Join(s, "/var/os-release"), writer.Bytes(), 0644); err != nil {
return fmt.Errorf("write /var/os-release: %v", err)
if err = ioutil.WriteFile("/run/os-release", writer.Bytes(), 0644); err != nil {
return fmt.Errorf("write /run/os-release: %v", err)
}
if err = unix.Mount("/run/os-release", "/etc/os-release", "", unix.MS_BIND, ""); err != nil {
return errors.Wrap(err, "failed to create bind mount for /etc/os-release")
}
return nil
}
// DefaultGateway parses /proc/net/route for the IP address of the default
// gateway.
func DefaultGateway() (s string, err error) {
handle, err := os.Open("/proc/net/route")
func ip() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
return
return ""
}
// nolint: errcheck
defer handle.Close()
scanner := bufio.NewScanner(handle)
for scanner.Scan() {
parts := strings.Split(scanner.Text(), "\t")
if len(parts) < 3 {
return s, fmt.Errorf("expected at least 3 fields from /proc/net/route, got %d", len(parts))
}
// Skip the header.
if parts[0] == "Iface" {
continue
}
destination := parts[1]
gateway := parts[2]
// We are looking for the default gateway.
if destination == "00000000" {
var decoded []byte
decoded, err = hex.DecodeString(gateway)
if err != nil {
return
for _, address := range addrs {
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String()
}
s = fmt.Sprintf("%v.%v.%v.%v", decoded[3], decoded[2], decoded[1], decoded[0])
break
}
}
return s, nil
return ""
}

View File

@ -99,20 +99,26 @@ func (i *Initializer) MoveSpecial() (err error) {
return nil
}
// InitOwned initializes and mounts the OS owned block devices in the early boot
// Rootfs initializes and mounts the OS owned block devices in the early boot
// stage.
//
// nolint: gocyclo
func (i *Initializer) InitOwned() (err error) {
func (i *Initializer) Rootfs() (err error) {
var dev losetup.Device
dev, err = losetup.Attach("/"+constants.RootfsAsset, 0, true)
if err != nil {
return err
}
m := mount.NewMountPoint(dev.Path(), "/", "squashfs", unix.MS_RDONLY, "")
if err = mount.WithRetry(m, mount.WithPrefix(i.prefix), mount.WithReadOnly(true), mount.WithShared(true)); err != nil {
return errors.Wrap(err, "failed to mount squashfs")
}
return nil
}
// InitOwned initializes and mounts the OS owned block devices in the early boot
// stage.
func (i *Initializer) InitOwned() (err error) {
var owned *mount.Points
if owned, err = mountpoints(); err != nil {
return errors.Errorf("error initializing owned block devices: %v", err)
@ -197,14 +203,6 @@ func (i *Initializer) UnmountOwned() (err error) {
// https://github.com/karelzak/util-linux/blob/master/sys-utils/switch_root.c.
// nolint: gocyclo
func (i *Initializer) Switch() (err error) {
if err = i.UnmountOwned(); err != nil {
return err
}
if err = i.MountOwned(); err != nil {
return errors.Wrap(err, "error mounting block device")
}
if err = i.MoveSpecial(); err != nil {
return errors.Wrap(err, "error moving special devices")
}

View File

@ -7,7 +7,6 @@ package rootfs
import (
"io/ioutil"
"log"
stdlibnet "net"
"os"
"path/filepath"
"time"
@ -15,29 +14,12 @@ import (
"github.com/pkg/errors"
"github.com/talos-systems/talos/internal/pkg/constants"
"github.com/talos-systems/talos/internal/pkg/grpc/gen"
"github.com/talos-systems/talos/internal/pkg/rootfs/etc"
"github.com/talos-systems/talos/internal/pkg/rootfs/proc"
"github.com/talos-systems/talos/pkg/crypto/x509"
"github.com/talos-systems/talos/pkg/userdata"
yaml "gopkg.in/yaml.v2"
)
func ip() string {
addrs, err := stdlibnet.InterfaceAddrs()
if err != nil {
return ""
}
for _, address := range addrs {
if ipnet, ok := address.(*stdlibnet.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String()
}
}
}
return ""
}
// Prepare creates the files required by the installed binaries and libraries.
// nolint: gocyclo
func Prepare(s string, inContainer bool, data *userdata.UserData) (err error) {
@ -50,25 +32,8 @@ func Prepare(s string, inContainer bool, data *userdata.UserData) (err error) {
if err = kernelHardening(); err != nil {
return
}
// Create /etc/hosts.
var hostname string
if hostname, err = os.Hostname(); err != nil {
return
}
ip := ip()
if err = etc.Hosts(s, hostname, ip); err != nil {
return
}
// Create /etc/resolv.conf.
if err = etc.ResolvConf(s); err != nil {
return
}
}
// Create /etc/os-release.
if err = etc.OSRelease(s); err != nil {
return
}
// Generate the identity certificate.
if err = generatePKI(data); err != nil {
return