fix(init): retry mounts (#220)
This commit is contained in:
parent
0c80b7e035
commit
51118bd5f3
@ -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"
|
||||
)
|
||||
|
@ -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"))
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"
|
||||
)
|
||||
|
@ -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"
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user