fix(init): retry mounts (#220)

This commit is contained in:
Andrew Rynhard 2018-11-20 21:33:13 -08:00 committed by GitHub
parent 0c80b7e035
commit 51118bd5f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 100 additions and 78 deletions

View File

@ -4,10 +4,10 @@
package proto
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
empty "github.com/golang/protobuf/ptypes/empty"
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
math "math"
)

View File

@ -6,7 +6,6 @@ import "C"
import (
"flag"
"fmt"
"log"
"os"
@ -18,6 +17,7 @@ import (
"github.com/autonomy/talos/src/initramfs/cmd/init/pkg/system"
"github.com/autonomy/talos/src/initramfs/cmd/init/pkg/system/services"
"github.com/autonomy/talos/src/initramfs/pkg/userdata"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
@ -26,82 +26,75 @@ var (
switchRoot *bool
)
func recovery() {
// We should only reach this point if something within initram() fails.
if r := recover(); r != nil {
log.Printf("recovered from: %v\n", r)
}
select {}
}
func init() {
switchRoot = flag.Bool("switch-root", false, "perform a switch_root")
flag.Parse()
}
func kmsg(prefix string) error {
func kmsg(prefix string) (*os.File, error) {
out, err := os.OpenFile("/dev/kmsg", os.O_RDWR|unix.O_CLOEXEC|unix.O_NONBLOCK|unix.O_NOCTTY, 0666)
if err != nil {
return fmt.Errorf("failed to open /dev/kmsg: %v", err)
return nil, errors.Wrap(err, "failed to open /dev/kmsg")
}
log.SetOutput(out)
log.SetPrefix(prefix + " ")
log.SetFlags(0)
return nil
return out, nil
}
func initram() (err error) {
func initram() error {
// Read the block devices and populate the mount point definitions.
if err = mount.Init(constants.NewRoot); err != nil {
return
if err := mount.Init(constants.NewRoot); err != nil {
return err
}
// Setup logging to /dev/kmsg.
// NB: We should not attempt to open /dev/kmsg until after /dev is mounted.
if err = kmsg("[talos] [initramfs]"); err != nil {
return
var f *os.File
f, err := kmsg("[talos] [initramfs]")
if err != nil {
return err
}
// Discover the platform.
log.Println("discovering the platform")
p, err := platform.NewPlatform()
if err != nil {
return
return err
}
// Retrieve the user data.
log.Printf("retrieving the user data for the platform: %s", p.Name())
data, err := p.UserData()
if err != nil {
return
return err
}
log.Printf("preparing the node for the platform: %s", p.Name())
// Perform any tasks required by a particular platform.
if err = p.Prepare(data); err != nil {
return
if err := p.Prepare(data); err != nil {
return err
}
// Prepare the necessary files in the rootfs.
log.Println("preparing the root filesystem")
if err = rootfs.Prepare(constants.NewRoot, data); err != nil {
return
if err := rootfs.Prepare(constants.NewRoot, data); err != nil {
return err
}
// Unmount the ROOT and DATA block devices.
log.Println("unmounting the ROOT and DATA partitions")
if err = mount.Unmount(); err != nil {
return
if err := mount.Unmount(); err != nil {
return err
}
// Perform the equivalent of switch_root.
log.Println("entering the new root")
if err = switchroot.Switch(constants.NewRoot); err != nil {
return
f.Close() // nolint: errcheck
if err := switchroot.Switch(constants.NewRoot); err != nil {
return err
}
return nil
}
func root() (err error) {
func root() error {
// Setup logging to /dev/kmsg.
if err = kmsg("[talos]"); err != nil {
return
if _, err := kmsg("[talos]"); err != nil {
return err
}
// Read the user data.
log.Printf("reading the user data: %s\n", constants.UserDataPath)
@ -112,14 +105,14 @@ func root() (err error) {
// Write any user specified files to disk.
log.Println("writing the files specified in the user data to disk")
if err = data.WriteFiles(); err != nil {
if err := data.WriteFiles(); err != nil {
return err
}
// Set the requested environment variables.
log.Println("setting environment variables")
for key, val := range data.Env {
if err = os.Setenv(key, val); err != nil {
if err := os.Setenv(key, val); err != nil {
log.Printf("WARNING failed to set enivronment variable: %v", err)
}
}
@ -150,21 +143,35 @@ func root() (err error) {
return nil
}
func recovery() {
if r := recover(); r != nil {
log.Printf("recovered from: %+v\n", r)
}
select {}
}
func main() {
defer recovery()
// TODO(andrewrynhard): Remove this and be explicit.
if err := os.Setenv("PATH", constants.PATH); err != nil {
panic(err)
panic(errors.New("error setting PATH"))
}
if *switchRoot {
if err := root(); err != nil {
panic(err)
panic(errors.Wrap(err, "boot failed"))
}
// Hang forever.
select {}
}
if err := initram(); err != nil {
panic(err)
panic(errors.Wrap(err, "early boot failed"))
}
// We should never reach this point if things are working as intended.
panic(errors.New("unkown error"))
}

View File

@ -8,12 +8,15 @@ import (
"path"
"strings"
"sync"
"time"
"github.com/autonomy/talos/src/initramfs/cmd/init/pkg/constants"
"github.com/autonomy/talos/src/initramfs/cmd/init/pkg/fs/xfs"
"github.com/autonomy/talos/src/initramfs/cmd/init/pkg/mount/blkid"
"github.com/autonomy/talos/src/initramfs/pkg/blockdevice"
gptpartition "github.com/autonomy/talos/src/initramfs/pkg/blockdevice/table/gpt/partition"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
@ -68,10 +71,10 @@ func Init(s string) (err error) {
}
blockdevices, err := probe()
if err != nil {
return fmt.Errorf("probe block devices: %s", err.Error())
return fmt.Errorf("error probing block devices: %v", err)
}
if err = mountBlockDevices(blockdevices, s); err != nil {
return fmt.Errorf("error mounting block devices: %v", err)
return fmt.Errorf("error mounting partitions: %v", err)
}
return nil
@ -86,16 +89,16 @@ func Move(s string) error {
// Move the special mounts to the new root.
for label, mountpoint := range instance.special {
target := path.Join(s, mountpoint.target)
if err := unix.Mount(mountpoint.target, target, "", unix.MS_MOVE, ""); err != nil {
return fmt.Errorf("move mount point %s to %s: %s", mountpoint.target, target, err.Error())
if err := UnixMountWithRetry(mountpoint.target, target, "", unix.MS_MOVE, ""); err != nil {
return fmt.Errorf("move mount point %s to %s: %v", mountpoint.target, target, err)
}
if label == "dev" {
mountpoint = &Point{"devpts", path.Join(s, "/dev/pts"), "devpts", unix.MS_NOSUID | unix.MS_NOEXEC, "ptmxmode=000,mode=620,gid=5"}
if err := os.MkdirAll(mountpoint.target, os.ModeDir); err != nil {
return fmt.Errorf("create %s: %s", mountpoint.target, err.Error())
return fmt.Errorf("error creating mount point directory %s: %v", mountpoint.target, err)
}
if err := unix.Mount(mountpoint.source, mountpoint.target, mountpoint.fstype, mountpoint.flags, mountpoint.data); err != nil {
return fmt.Errorf("mount %s: %s", mountpoint.target, err.Error())
if err := UnixMountWithRetry(mountpoint.source, mountpoint.target, mountpoint.fstype, mountpoint.flags, mountpoint.data); err != nil {
return fmt.Errorf("error moving special device from %s to %s: %v", mountpoint.source, mountpoint.target, err)
}
}
}
@ -118,7 +121,7 @@ func Mount(s string) error {
if ok {
mountpoint.flags = unix.MS_RDONLY | unix.MS_NOATIME
if err := unix.Mount(mountpoint.source, mountpoint.target, mountpoint.fstype, mountpoint.flags, mountpoint.data); err != nil {
return fmt.Errorf("mount %s: %s", mountpoint.target, err.Error())
return fmt.Errorf("error mounting partition %s: %v", mountpoint.target, err)
}
// MS_SHARED:
// Make this mount point shared. Mount and unmount events
@ -132,13 +135,13 @@ func Mount(s string) error {
// See http://man7.org/linux/man-pages/man2/mount.2.html
// https://github.com/kubernetes/kubernetes/issues/61058
if err := unix.Mount("", mountpoint.target, "", unix.MS_SHARED, ""); err != nil {
return fmt.Errorf("mount %s as shared: %s", mountpoint.target, err.Error())
return fmt.Errorf("error making making mount point %s shared: %v", mountpoint.target, err)
}
}
mountpoint, ok = instance.blockdevices[constants.DataPartitionLabel]
if ok {
if err := unix.Mount(mountpoint.source, mountpoint.target, mountpoint.fstype, mountpoint.flags, mountpoint.data); err != nil {
return fmt.Errorf("mount %s: %s", mountpoint.target, err.Error())
return fmt.Errorf("error mounting partition %s: %v", mountpoint.target, err)
}
}
@ -150,13 +153,13 @@ func Unmount() error {
mountpoint, ok := instance.blockdevices[constants.DataPartitionLabel]
if ok {
if err := unix.Unmount(mountpoint.target, 0); err != nil {
return fmt.Errorf("unmount mount point %s: %s", mountpoint.target, err.Error())
return fmt.Errorf("unmount mount point %s: %v", mountpoint.target, err)
}
}
mountpoint, ok = instance.blockdevices[constants.RootPartitionLabel]
if ok {
if err := unix.Unmount(mountpoint.target, 0); err != nil {
return fmt.Errorf("unmount mount point %s: %s", mountpoint.target, err.Error())
return fmt.Errorf("unmount mount point %s: %v", mountpoint.target, err)
}
}
@ -166,16 +169,35 @@ func Unmount() error {
func mountSpecialDevices() (err error) {
for _, mountpoint := range instance.special {
if err = os.MkdirAll(mountpoint.target, os.ModeDir); err != nil {
return fmt.Errorf("create %s: %s", mountpoint.target, err.Error())
return fmt.Errorf("error creating mount point directory %s: %v", mountpoint.target, err)
}
if err = unix.Mount(mountpoint.source, mountpoint.target, mountpoint.fstype, mountpoint.flags, mountpoint.data); err != nil {
return fmt.Errorf("mount %s: %s", mountpoint.target, err.Error())
if err = UnixMountWithRetry(mountpoint.source, mountpoint.target, mountpoint.fstype, mountpoint.flags, mountpoint.data); err != nil {
return fmt.Errorf("error mounting special device %s: %v", mountpoint.target, err)
}
}
return nil
}
// UnixMountWithRetry attempts to retry a mount on EBUSY. It will attempt a
// retry every 100 milliseconds over the course of 5 seconds.
func UnixMountWithRetry(source string, target string, fstype string, flags uintptr, data string) (err error) {
for i := 0; i < 50; i++ {
if err = unix.Mount(source, target, fstype, flags, data); err != nil {
switch err {
case unix.EBUSY:
time.Sleep(100 * time.Millisecond)
continue
default:
return err
}
}
return nil
}
return errors.Errorf("mount timeout: %v", err)
}
// nolint: gocyclo
func fixDataPartition(blockdevices []*BlockDevice) error {
for _, b := range blockdevices {
@ -241,10 +263,10 @@ func mountBlockDevices(blockdevices []*BlockDevice, s string) (err error) {
}
if err = os.MkdirAll(mountpoint.target, os.ModeDir); err != nil {
return fmt.Errorf("create %s: %s", mountpoint.target, err.Error())
return fmt.Errorf("error creating mount point directory %s: %v", mountpoint.target, err)
}
if err = unix.Mount(mountpoint.source, mountpoint.target, mountpoint.fstype, mountpoint.flags, mountpoint.data); err != nil {
return fmt.Errorf("mount %s: %s", mountpoint.target, err.Error())
return fmt.Errorf("error mounting partition %s: %v", mountpoint.target, err)
}
if b.LABEL == constants.DataPartitionLabel {
@ -285,7 +307,7 @@ func appendBlockDeviceWithLabel(b *[]*BlockDevice, value string) error {
blockDevice, err := probeDevice(devname)
if err != nil {
return fmt.Errorf("faild to probe block device %q: %v", devname, err)
return fmt.Errorf("failed to probe block device %q: %v", devname, err)
}
*b = append(*b, blockDevice)

View File

@ -3,12 +3,12 @@
package switchroot
import (
"fmt"
"os"
"syscall"
"github.com/autonomy/talos/src/initramfs/cmd/init/pkg/mount"
"github.com/autonomy/talos/src/initramfs/cmd/init/pkg/mount/cgroups"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
@ -87,36 +87,36 @@ func getDev(fd int) (dev uint64, err error) {
func Switch(s string) error {
// Mount the ROOT and DATA block devices at the new root.
if err := mount.Mount(s); err != nil {
panic(err)
return errors.Wrap(err, "error mounting block device")
}
// Move the special mount points to the new root.
if err := mount.Move(s); err != nil {
panic(err)
return errors.Wrap(err, "error moving special devices")
}
// Mount the cgroups file systems to the new root.
if err := cgroups.Mount(s); err != nil {
panic(err)
return errors.Wrap(err, "error mounting cgroups")
}
if err := unix.Chdir(s); err != nil {
return fmt.Errorf("chdir: %s", err.Error())
return errors.Wrapf(err, "error changing working directory to %s", s)
}
oldRoot, err := os.Open("/")
if err != nil {
return err
return errors.Wrap(err, "error opening /")
}
// nolint: errcheck
defer oldRoot.Close()
if err := mount.Finalize(s); err != nil {
return err
return errors.Wrap(err, "error moving /")
}
if err := unix.Chroot("."); err != nil {
return fmt.Errorf("chroot: %s", err.Error())
return errors.Wrap(err, "error chroot")
}
if err := recursiveDelete(int(oldRoot.Fd())); err != nil {
panic(err)
return errors.Wrap(err, "error deleting initramfs")
}
if err := syscall.Exec("/proc/self/exe", []string{"exe", "--switch-root"}, []string{}); err != nil {
return fmt.Errorf("exec /proc/self/exe: %s", err.Error())
return errors.Wrap(err, "error executing /proc/self/exe")
}
return nil

View File

@ -12,8 +12,6 @@ import (
"github.com/autonomy/talos/src/initramfs/cmd/init/pkg/system/runner"
processlogger "github.com/autonomy/talos/src/initramfs/cmd/init/pkg/system/runner/process/log"
"github.com/autonomy/talos/src/initramfs/pkg/userdata"
"golang.org/x/sys/unix"
)
// Process is a runner.Runner that runs a process on the host.
@ -55,12 +53,7 @@ func (p *Process) build(data *userdata.UserData, args *runner.Args, opts *runner
var writer io.Writer
if data.Debug {
out, err := os.OpenFile("/dev/kmsg", os.O_RDWR|unix.O_CLOEXEC|unix.O_NONBLOCK|unix.O_NOCTTY, 0666)
if err != nil {
return nil, err
}
writer = io.MultiWriter(w, out)
writer = io.MultiWriter(w, os.Stdout)
} else {
writer = w
}

View File

@ -4,10 +4,10 @@
package proto
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
empty "github.com/golang/protobuf/ptypes/empty"
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
math "math"
)

View File

@ -4,9 +4,9 @@
package proto
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
math "math"
)

View File

@ -49,7 +49,7 @@ require (
github.com/opencontainers/runc v0.1.1 // indirect
github.com/opencontainers/runtime-spec v0.1.2-0.20180710222632-d810dbc60d8c
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.8.0 // indirect
github.com/pkg/errors v0.8.0
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sirupsen/logrus v1.0.6 // indirect
github.com/spf13/afero v1.1.2 // indirect

View File

@ -1593,7 +1593,7 @@ CONFIG_HAVE_EBPF_JIT=y
CONFIG_UEVENT_HELPER=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_DEVTMPFS_MOUNT is not set
CONFIG_STANDALONE=y
CONFIG_PREVENT_FIRMWARE_BUILD=y