refactor: split machined into phases

This change aims to standardize the boot process. It introduces the
concept of a phase, which is comprised of tasks. Phases are ran in serial and
the tasks that make up a phase are ran concurrently.

Signed-off-by: Andrew Rynhard <andrew@andrewrynhard.com>
This commit is contained in:
Andrew Rynhard 2019-07-27 08:09:50 +00:00
parent f56a9d5b96
commit e63c882b89
62 changed files with 1725 additions and 819 deletions

View File

@ -73,7 +73,7 @@ ARG SHA
ARG TAG
ARG VERSION_PKG="github.com/talos-systems/talos/internal/pkg/version"
WORKDIR /src/internal/app/machined
RUN --mount=type=cache,target=/.cache/go-build go build -ldflags "-s -w -X ${VERSION_PKG}.Name=Server -X ${VERSION_PKG}.SHA=${SHA} -X ${VERSION_PKG}.Tag=${TAG}" -o /machined
RUN --mount=type=cache,target=/.cache/go-build go build -ldflags "-s -w -X ${VERSION_PKG}.Name=Talos -X ${VERSION_PKG}.SHA=${SHA} -X ${VERSION_PKG}.Tag=${TAG}" -o /machined
RUN chmod +x /machined
FROM scratch AS machined
@ -209,7 +209,7 @@ RUN ln -s /etc/ssl /rootfs/etc/ca-certificates
FROM rootfs-base AS rootfs-squashfs
COPY --from=rootfs / /rootfs
RUN mksquashfs /rootfs /rootfs.sqsh -all-root -noappend -comp xz -Xdict-size 100%
RUN mksquashfs /rootfs /rootfs.sqsh -all-root -noappend -comp xz -Xdict-size 100% -no-progress
FROM scratch AS rootfs
COPY --from=rootfs-base /rootfs /

View File

@ -54,15 +54,11 @@ DOCKER_TEST_ARGS = --security-opt seccomp:unconfined --privileged -v /var/lib/co
TESTPKGS ?= ./...
all: ci drone
.PHONY: drone
drone: rootfs initramfs kernel binaries installer talos
all: ci rootfs initramfs kernel osctl installer talos
.PHONY: ci
ci: builddeps buildkitd
.PHONY: builddeps
builddeps: gitmeta buildctl
@ -109,8 +105,8 @@ ifneq ($(BUILDKIT_CONTAINER_RUNNING),$(BUILDKIT_CONTAINER_NAME))
endif
endif
.PHONY: binaries
binaries: osctl-linux osctl-darwin
.PHONY: osctl
osctl: osctl-linux osctl-darwin
base: buildkitd
@$(BINDIR)/buildctl --addr $(BUILDKIT_HOST) \
@ -137,7 +133,7 @@ initramfs: buildkitd
$(COMMON_ARGS)
.PHONY: rootfs
rootfs: buildkitd
rootfs: buildkitd osd trustd proxyd ntpd
@$(BINDIR)/buildctl --addr $(BUILDKIT_HOST) \
build \
--output type=local,dest=build \
@ -182,6 +178,27 @@ talos-aws:
-e AWS_DEFAULT_REGION=$(AWS_DEFAULT_REGION) \
autonomy/installer:$(TAG) ami -var regions=${AWS_PUBLISH_REGIONS} -var visibility=all
.PHONY: talos-azure
talos-azure:
@docker run --rm -v /dev:/dev -v $(PWD)/build:/out \
--privileged $(DOCKER_ARGS) \
autonomy/installer:$(TAG) \
install \
-n disk \
-r \
-p azure \
-u none \
-e rootdelay=300
@docker run --rm -v $(PWD)/build:/out $(DOCKER_ARGS) \
--entrypoint qemu-img \
autonomy/installer:$(TAG) \
convert \
-f raw \
-o subformat=fixed,force_size \
-O vpc /out/disk.raw /out/disk.vhd
@tar -C $(PWD)/build -czf $(PWD)/build/$@.tar.gz disk.vhd
@rm -rf $(PWD)/build/disk.raw $(PWD)/build/disk.vhd
.PHONY: talos-raw
talos-raw:
@docker run --rm -v /dev:/dev -v $(PWD)/build:/out --privileged $(DOCKER_ARGS) autonomy/installer:$(TAG) install -n rootfs -r -b
@ -295,24 +312,3 @@ push: gitmeta
.PHONY: clean
clean:
@-rm -rf build images vendor
.PHONY: talos-azure
talos-azure:
@docker run --rm -v /dev:/dev -v $(PWD)/build:/out \
--privileged $(DOCKER_ARGS) \
autonomy/installer:$(TAG) \
install \
-n disk \
-r \
-p azure \
-u none \
-e rootdelay=300
@docker run --rm -v $(PWD)/build:/out $(DOCKER_ARGS) \
--entrypoint qemu-img \
autonomy/installer:$(TAG) \
convert \
-f raw \
-o subformat=fixed,force_size \
-O vpc /out/disk.raw /out/disk.vhd
@tar -C $(PWD)/build -czf $(PWD)/build/$@.tar.gz disk.vhd
@rm -rf $(PWD)/build/disk.raw $(PWD)/build/disk.vhd

View File

@ -13,7 +13,6 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/strslice"
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
"github.com/talos-systems/talos/pkg/userdata/generate"
@ -44,7 +43,7 @@ func NewNode(clusterName string, req *Request) (err error) {
containerConfig := &container.Config{
Hostname: req.Name,
Image: req.Image,
Cmd: strslice.StrSlice{"--in-container", "--userdata=" + b64data},
Env: []string{"PLATFORM=container", "USERDATA=" + b64data},
Labels: map[string]string{
"talos.owned": "true",
"talos.cluster.name": clusterName,

2
go.mod
View File

@ -79,7 +79,7 @@ require (
go.uber.org/multierr v1.1.0 // indirect
go.uber.org/zap v1.10.0 // indirect
golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c // indirect
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a // indirect
golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect
golang.org/x/sys v0.0.0-20190508220229-2d0786266e9c

4
go.sum
View File

@ -253,8 +253,8 @@ golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190328230028-74de082e2cca/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=

View File

@ -104,7 +104,6 @@ linters:
disable-all: false
fast: false
issues:
# List of regexps of issue texts to exclude, empty list by default.
# But independently from this option we use default exclude patterns,
@ -116,6 +115,9 @@ issues:
- path: cmd/osctl/cmd
linters:
- dupl
- path: internal/app/machined/internal/phase
linters:
- dupl
# Independently from option `exclude` we use default exclude patterns,
# it can be disabled by this option. To list all

View File

@ -5,21 +5,11 @@
package mount
import (
"fmt"
"log"
"os"
"path"
"github.com/pkg/errors"
"github.com/talos-systems/talos/internal/pkg/blockdevice"
"github.com/talos-systems/talos/internal/pkg/blockdevice/filesystem/xfs"
"github.com/talos-systems/talos/internal/pkg/blockdevice/probe"
gptpartition "github.com/talos-systems/talos/internal/pkg/blockdevice/table/gpt/partition"
"github.com/talos-systems/talos/internal/pkg/blockdevice/util"
"github.com/talos-systems/talos/internal/pkg/constants"
"github.com/talos-systems/talos/internal/pkg/mount"
"github.com/talos-systems/talos/internal/pkg/mount/cgroups"
"github.com/talos-systems/talos/pkg/userdata"
"golang.org/x/sys/unix"
"gopkg.in/freddierice/go-losetup.v1"
)
@ -28,7 +18,6 @@ import (
type Initializer struct {
prefix string
owned *mount.Points
special *mount.Points
}
@ -49,18 +38,13 @@ func NewInitializer(prefix string) (initializer *Initializer, err error) {
return initializer, nil
}
// Owned returns the OS owned block devices.
func (i *Initializer) Owned() *mount.Points {
return i.owned
}
// Special returns the special devices.
func (i *Initializer) Special() *mount.Points {
return i.special
}
// InitSpecial initializes and mounts the special devices in the early boot
// stage.
// tasks.
func (i *Initializer) InitSpecial() (err error) {
iter := i.special.Iter()
for iter.Next() {
@ -75,6 +59,23 @@ func (i *Initializer) InitSpecial() (err error) {
return nil
}
// Rootfs initializes and mounts the OS owned block devices in the early boot
// tasks.
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
}
// MoveSpecial moves the special device mount points to the new root.
func (i *Initializer) MoveSpecial() (err error) {
iter := i.special.Iter()
@ -99,106 +100,6 @@ func (i *Initializer) MoveSpecial() (err error) {
return nil
}
// Rootfs initializes and mounts the OS owned block devices in the early boot
// stage.
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)
}
i.owned = owned
if mountpoint, ok := i.owned.Get(constants.DataPartitionLabel); ok {
if err = repair(mountpoint); err != nil {
return errors.Errorf("error fixing data partition: %v", err)
}
}
iter := i.owned.Iter()
for iter.Next() {
if err = mount.WithRetry(iter.Value(), mount.WithPrefix(i.prefix)); err != nil {
return errors.Errorf("error mounting partitions: %v", err)
}
}
if iter.Err() != nil {
return iter.Err()
}
if mountpoint, ok := i.owned.Get(constants.DataPartitionLabel); ok {
// NB: The XFS partition MUST be mounted, or this will fail.
log.Printf("growing the %s partition", constants.DataPartitionLabel)
if err = xfs.GrowFS(path.Join(i.prefix, mountpoint.Target())); err != nil {
return errors.Errorf("error growing data partition file system: %v", err)
}
}
return nil
}
// ExtraDevices mounts the extra devices.
func ExtraDevices(data *userdata.UserData) (err error) {
if data.Install == nil || data.Install.ExtraDevices == nil {
return nil
}
for _, extra := range data.Install.ExtraDevices {
for i, part := range extra.Partitions {
devname := fmt.Sprintf("%s%d", extra.Device, i+1)
mountpoint := mount.NewMountPoint(devname, part.MountPoint, "xfs", unix.MS_NOATIME, "")
if err = mount.WithRetry(mountpoint); err != nil {
return errors.Errorf("failed to mount %s at %s: %v", devname, part.MountPoint, err)
}
}
}
return nil
}
// MountOwned mounts the OS owned block devices.
func (i *Initializer) MountOwned() (err error) {
iter := i.owned.Iter()
for iter.Next() {
if err = mount.WithRetry(iter.Value(), mount.WithPrefix(i.prefix)); err != nil {
return errors.Errorf("error mounting partitions: %v", err)
}
}
if iter.Err() != nil {
return iter.Err()
}
return nil
}
// UnmountOwned unmounts the OS owned block devices.
func (i *Initializer) UnmountOwned() (err error) {
iter := i.owned.IterRev()
for iter.Next() {
if err = mount.UnWithRetry(iter.Value(), mount.WithPrefix(i.prefix)); err != nil {
return errors.Errorf("error mounting partitions: %v", err)
}
}
if iter.Err() != nil {
return iter.Err()
}
return nil
}
// Switch moves the root to a specified directory. See
// https://github.com/karelzak/util-linux/blob/master/sys-utils/switch_root.c.
// nolint: gocyclo
@ -207,11 +108,6 @@ func (i *Initializer) Switch() (err error) {
return errors.Wrap(err, "error moving special devices")
}
// Mount the cgroups to the new root.
if err = cgroups.Mount(i.prefix); err != nil {
return errors.Wrap(err, "error mounting cgroups")
}
if err = unix.Chdir(i.prefix); err != nil {
return errors.Wrapf(err, "error changing working directory to %s", i.prefix)
}
@ -244,78 +140,6 @@ func (i *Initializer) Switch() (err error) {
return nil
}
// nolint: dupl
func mountpoints() (mountpoints *mount.Points, err error) {
mountpoints = mount.NewMountPoints()
for _, name := range []string{constants.DataPartitionLabel, constants.BootPartitionLabel} {
var target string
switch name {
case constants.DataPartitionLabel:
target = constants.DataMountPoint
case constants.BootPartitionLabel:
target = constants.BootMountPoint
}
var dev *probe.ProbedBlockDevice
if dev, err = probe.GetDevWithFileSystemLabel(name); err != nil {
if name == constants.BootPartitionLabel {
// A bootloader is not always required.
log.Println("WARNING: no ESP partition was found")
continue
}
return nil, errors.Errorf("failed to find device with label %s: %v", name, err)
}
mountpoint := mount.NewMountPoint(dev.Path, target, dev.SuperBlock.Type(), unix.MS_NOATIME, "")
mountpoints.Set(name, mountpoint)
}
return mountpoints, nil
}
func repair(mountpoint *mount.Point) (err error) {
var devname string
if devname, err = util.DevnameFromPartname(mountpoint.Source()); err != nil {
return err
}
bd, err := blockdevice.Open("/dev/" + devname)
if err != nil {
return errors.Errorf("error opening block device %q: %v", devname, err)
}
// nolint: errcheck
defer bd.Close()
pt, err := bd.PartitionTable(true)
if err != nil {
return err
}
if err := pt.Repair(); err != nil {
return err
}
for _, partition := range pt.Partitions() {
if partition.(*gptpartition.Partition).Name == constants.DataPartitionLabel {
if err := pt.Resize(partition); err != nil {
return err
}
}
}
if err := pt.Write(); err != nil {
return err
}
// Rereading the partition table requires that all partitions be unmounted
// or it will fail with EBUSY.
if err := bd.RereadPartitionTable(); err != nil {
return err
}
return nil
}
func recursiveDelete(fd int) error {
parentDev, err := getDev(fd)
if err != nil {

View File

@ -6,27 +6,13 @@ package main
import (
"log"
"os"
"github.com/pkg/errors"
"github.com/talos-systems/talos/internal/app/init/internal/mount"
"github.com/talos-systems/talos/internal/pkg/constants"
"github.com/talos-systems/talos/internal/pkg/rootfs/mount"
"golang.org/x/sys/unix"
"github.com/talos-systems/talos/internal/pkg/kmsg"
)
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 nil, errors.Wrap(err, "failed to open /dev/kmsg")
}
log.SetOutput(out)
log.SetPrefix(prefix + " ")
log.SetFlags(0)
return out, nil
}
// nolint: gocyclo
func initram() (err error) {
var initializer *mount.Initializer
@ -40,7 +26,7 @@ func initram() (err error) {
}
// Setup logging to /dev/kmsg.
_, err = kmsg("[talos] [initramfs]")
_, err = kmsg.Setup("[talos] [initramfs]")
if err != nil {
return err
}

View File

@ -26,8 +26,8 @@ var (
)
// Mount creates the cgroup mount points.
func Mount(s string) error {
target := path.Join(s, "/sys/fs/cgroup")
func Mount() error {
target := "/sys/fs/cgroup"
if err := os.MkdirAll(target, os.ModeDir); err != nil {
return errors.Errorf("failed to create %s: %+v", target, err)
}
@ -50,7 +50,7 @@ func Mount(s string) error {
"pids",
}
for _, c := range cgroups {
p := path.Join(s, "/sys/fs/cgroup", c)
p := path.Join("/sys/fs/cgroup", c)
if err := os.MkdirAll(p, os.ModeDir); err != nil {
return errors.Errorf("failed to create %s: %+v", p, err)
}
@ -60,7 +60,7 @@ func Mount(s string) error {
}
// See https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt
target = path.Join(s, "/sys/fs/cgroup", memoryCgroup, memoryUseHierarchy)
target = path.Join("/sys/fs/cgroup", memoryCgroup, memoryUseHierarchy)
err := ioutil.WriteFile(target, memoryUseHierarchyContents, memoryUseHierarchyPermissions)
return err

View File

@ -0,0 +1,198 @@
/* 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 mount
import (
"fmt"
"log"
"path"
"github.com/pkg/errors"
"github.com/talos-systems/talos/internal/pkg/blockdevice"
"github.com/talos-systems/talos/internal/pkg/blockdevice/filesystem/xfs"
"github.com/talos-systems/talos/internal/pkg/blockdevice/probe"
gptpartition "github.com/talos-systems/talos/internal/pkg/blockdevice/table/gpt/partition"
"github.com/talos-systems/talos/internal/pkg/blockdevice/util"
"github.com/talos-systems/talos/internal/pkg/constants"
"github.com/talos-systems/talos/internal/pkg/mount"
"github.com/talos-systems/talos/pkg/userdata"
"golang.org/x/sys/unix"
)
// Initializer represents the early boot initialization control.
type Initializer struct {
prefix string
owned *mount.Points
}
// NewInitializer initializes and returns an Initializer struct.
func NewInitializer(prefix string) (initializer *Initializer, err error) {
initializer = &Initializer{
prefix: prefix,
}
return initializer, nil
}
// Owned returns the OS owned block devices.
func (i *Initializer) Owned() *mount.Points {
return i.owned
}
// InitOwned initializes and mounts the OS owned block devices in the early boot
// tasks.
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)
}
i.owned = owned
if mountpoint, ok := i.owned.Get(constants.DataPartitionLabel); ok {
if err = repair(mountpoint); err != nil {
return errors.Errorf("error fixing data partition: %v", err)
}
}
iter := i.owned.Iter()
for iter.Next() {
if err = mount.WithRetry(iter.Value(), mount.WithPrefix(i.prefix)); err != nil {
return errors.Errorf("error mounting partitions: %v", err)
}
}
if iter.Err() != nil {
return iter.Err()
}
if mountpoint, ok := i.owned.Get(constants.DataPartitionLabel); ok {
// NB: The XFS partition MUST be mounted, or this will fail.
log.Printf("growing the %s partition", constants.DataPartitionLabel)
if err = xfs.GrowFS(path.Join(i.prefix, mountpoint.Target())); err != nil {
return errors.Errorf("error growing data partition file system: %v", err)
}
}
return nil
}
// ExtraDevices mounts the extra devices.
func ExtraDevices(data *userdata.UserData) (err error) {
if data.Install == nil || data.Install.ExtraDevices == nil {
return nil
}
for _, extra := range data.Install.ExtraDevices {
for i, part := range extra.Partitions {
devname := fmt.Sprintf("%s%d", extra.Device, i+1)
mountpoint := mount.NewMountPoint(devname, part.MountPoint, "xfs", unix.MS_NOATIME, "")
if err = mount.WithRetry(mountpoint); err != nil {
return errors.Errorf("failed to mount %s at %s: %v", devname, part.MountPoint, err)
}
}
}
return nil
}
// MountOwned mounts the OS owned block devices.
func (i *Initializer) MountOwned() (err error) {
iter := i.owned.Iter()
for iter.Next() {
if err = mount.WithRetry(iter.Value(), mount.WithPrefix(i.prefix)); err != nil {
return errors.Errorf("error mounting partitions: %v", err)
}
}
if iter.Err() != nil {
return iter.Err()
}
return nil
}
// UnmountOwned unmounts the OS owned block devices.
func (i *Initializer) UnmountOwned() (err error) {
iter := i.owned.IterRev()
for iter.Next() {
if err = mount.UnWithRetry(iter.Value(), mount.WithPrefix(i.prefix)); err != nil {
return errors.Errorf("error mounting partitions: %v", err)
}
}
if iter.Err() != nil {
return iter.Err()
}
return nil
}
// nolint: dupl
func mountpoints() (mountpoints *mount.Points, err error) {
mountpoints = mount.NewMountPoints()
for _, name := range []string{constants.DataPartitionLabel, constants.BootPartitionLabel} {
var target string
switch name {
case constants.DataPartitionLabel:
target = constants.DataMountPoint
case constants.BootPartitionLabel:
target = constants.BootMountPoint
}
var dev *probe.ProbedBlockDevice
if dev, err = probe.GetDevWithFileSystemLabel(name); err != nil {
if name == constants.BootPartitionLabel {
// A bootloader is not always required.
log.Println("WARNING: no ESP partition was found")
continue
}
return nil, errors.Errorf("failed to find device with label %s: %v", name, err)
}
mountpoint := mount.NewMountPoint(dev.Path, target, dev.SuperBlock.Type(), unix.MS_NOATIME, "")
mountpoints.Set(name, mountpoint)
}
return mountpoints, nil
}
func repair(mountpoint *mount.Point) (err error) {
var devname string
if devname, err = util.DevnameFromPartname(mountpoint.Source()); err != nil {
return err
}
bd, err := blockdevice.Open("/dev/" + devname)
if err != nil {
return errors.Errorf("error opening block device %q: %v", devname, err)
}
// nolint: errcheck
defer bd.Close()
pt, err := bd.PartitionTable(true)
if err != nil {
return err
}
if err := pt.Repair(); err != nil {
return err
}
for _, partition := range pt.Partitions() {
if partition.(*gptpartition.Partition).Name == constants.DataPartitionLabel {
if err := pt.Resize(partition); err != nil {
return err
}
}
}
if err := pt.Write(); err != nil {
return err
}
// Rereading the partition table requires that all partitions be unmounted
// or it will fail with EBUSY.
if err := bd.RereadPartitionTable(); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,14 @@
/* 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 mount_test
import "testing"
func TestEmpty(t *testing.T) {
// added for accurate coverage estimation
//
// please remove it once any unit-test is added
// for this package
}

View File

@ -0,0 +1,47 @@
/* 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 api
import (
"log"
"time"
"golang.org/x/sys/unix"
)
func sync() {
syncdone := make(chan struct{})
go func() {
defer close(syncdone)
unix.Sync()
}()
log.Printf("waiting for sync...")
for i := 29; i >= 0; i-- {
select {
case <-syncdone:
log.Printf("sync done")
return
case <-time.After(time.Second):
}
if i != 0 {
log.Printf("waiting %d more seconds for sync to finish", i)
}
}
log.Printf("sync hasn't completed in time, aborting...")
}
func reboot() {
// See http://man7.org/linux/man-pages/man2/reboot.2.html.
sync()
// nolint: errcheck
unix.Reboot(unix.LINUX_REBOOT_CMD_RESTART)
select {}
}

View File

@ -0,0 +1,114 @@
/* 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 api
import (
"log"
"os"
"os/signal"
"syscall"
"github.com/talos-systems/talos/internal/app/machined/internal/phase"
"github.com/talos-systems/talos/internal/app/machined/internal/phase/api/reg"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"github.com/talos-systems/talos/internal/app/machined/pkg/system"
"github.com/talos-systems/talos/internal/pkg/constants"
"github.com/talos-systems/talos/internal/pkg/grpc/factory"
"github.com/talos-systems/talos/pkg/userdata"
"golang.org/x/sys/unix"
"google.golang.org/grpc"
)
var (
rebootCmd int
)
// API represents the API task.
type API struct{}
// NewAPITask initializes and returns an API task.
func NewAPITask() phase.Task {
return &API{}
}
// RuntimeFunc returns the runtime function.
func (task *API) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc {
switch mode {
case runtime.Standard:
return task.standard
case runtime.Container:
return task.container
default:
return nil
}
}
func (task *API) standard(platform platform.Platform, data *userdata.UserData) (err error) {
var (
api *reg.Registrator
server *grpc.Server
)
if api, server, err = start(data); err != nil {
return err
}
go func() {
poweroffCh, err := listenForPowerButton()
if err != nil {
log.Printf("WARNING: power off events will be ignored: %+v", err)
}
termCh := make(chan os.Signal, 1)
signal.Notify(termCh, syscall.SIGTERM)
// NB: Defer is FILO.
defer reboot()
defer server.Stop()
defer system.Services(data).Shutdown()
select {
case <-api.ShutdownCh:
log.Printf("poweroff via API received")
// poweroff, proceed to shutdown but mark as poweroff
rebootCmd = unix.LINUX_REBOOT_CMD_POWER_OFF
case <-poweroffCh:
log.Printf("poweroff via ACPI received")
// poweroff, proceed to shutdown but mark as poweroff
rebootCmd = unix.LINUX_REBOOT_CMD_POWER_OFF
case <-termCh:
log.Printf("SIGTERM received, rebooting...")
case <-api.RebootCh:
log.Printf("reboot via API received, rebooting...")
rebootCmd = unix.LINUX_REBOOT_CMD_RESTART
}
}()
return nil
}
func (task *API) container(platform platform.Platform, data *userdata.UserData) (err error) {
if _, _, err = start(data); err != nil {
return err
}
return nil
}
func start(data *userdata.UserData) (api *reg.Registrator, server *grpc.Server, err error) {
api = reg.NewRegistrator(data)
server = factory.NewServer(api)
listener, err := factory.NewListener(factory.Network("unix"), factory.SocketPath(constants.InitSocketPath))
if err != nil {
return nil, nil, err
}
go func() {
// nolint: errcheck
server.Serve(listener)
}()
return api, server, nil
}

View File

@ -2,7 +2,7 @@
* 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 proc_test
package api_test
import "testing"

View File

@ -2,7 +2,7 @@
* 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 main
package api
import (
"log"

View File

@ -0,0 +1,59 @@
/* 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 install
import (
"log"
"github.com/talos-systems/talos/internal/app/machined/internal/mount"
"github.com/talos-systems/talos/internal/app/machined/internal/phase"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"github.com/talos-systems/talos/pkg/userdata"
)
// Install represents the Install task.
type Install struct{}
// NewInstallTask initializes and returns an Install task.
func NewInstallTask() phase.Task {
return &Install{}
}
// RuntimeFunc returns the runtime function.
func (task *Install) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc {
switch mode {
case runtime.Standard:
return task.runtime
default:
return nil
}
}
func (task *Install) runtime(platform platform.Platform, data *userdata.UserData) (err error) {
// Perform any tasks required by a particular platform.
log.Printf("performing platform specific tasks")
if err = platform.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 = platform.Install(data); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,14 @@
/* 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 install_test
import "testing"
func TestEmpty(t *testing.T) {
// added for accurate coverage estimation
//
// please remove it once any unit-test is added
// for this package
}

View File

@ -0,0 +1,39 @@
/* 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 network
import (
"github.com/talos-systems/talos/internal/app/machined/internal/phase"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"github.com/talos-systems/talos/internal/pkg/network"
"github.com/talos-systems/talos/pkg/userdata"
)
// UserDefinedNetwork represents the UserDefinedNetwork task.
type UserDefinedNetwork struct{}
// NewUserDefinedNetworkTask initializes and returns an UserDefinedNetwork task.
func NewUserDefinedNetworkTask() phase.Task {
return &UserDefinedNetwork{}
}
// RuntimeFunc returns the runtime function.
func (task *UserDefinedNetwork) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc {
switch mode {
case runtime.Standard:
return task.runtime
default:
return nil
}
}
func (task *UserDefinedNetwork) runtime(platform platform.Platform, data *userdata.UserData) (err error) {
if err = network.SetupNetwork(data); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,14 @@
/* 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 network_test
import "testing"
func TestEmpty(t *testing.T) {
// added for accurate coverage estimation
//
// please remove it once any unit-test is added
// for this package
}

View File

@ -0,0 +1,111 @@
/* 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 phase
import (
"log"
"sync"
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/platform/container"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"github.com/talos-systems/talos/internal/pkg/kmsg"
"github.com/talos-systems/talos/pkg/userdata"
)
// Runner represents a management layer for phases.
type Runner struct {
platform platform.Platform
phases []*Phase
mode runtime.Mode
data *userdata.UserData
}
// NewRunner initializes and returns a Runner.
func NewRunner(data *userdata.UserData) (*Runner, error) {
platform, err := platform.NewPlatform()
if err != nil {
return nil, err
}
mode := runtime.Standard
switch platform.(type) {
case *container.Container:
mode = runtime.Container
default:
// Setup logging to /dev/kmsg.
if _, err = kmsg.Setup("[talos]"); err != nil {
return nil, errors.Errorf("failed to setup logging to /dev/kmsg: %v", err)
}
}
return &Runner{
platform: platform,
mode: mode,
data: data,
}, nil
}
// Run executes all phases known to a Runner.
func (r *Runner) Run() error {
for _, phase := range r.phases {
var (
result *multierror.Error
wg sync.WaitGroup
)
wg.Add(len(phase.tasks))
log.Printf("[phase]: %s", phase.name)
go func(p *Phase) {
for _, task := range p.tasks {
defer wg.Done()
var f RuntimeFunc
if f = task.RuntimeFunc(r.mode); f == nil {
// A task is not defined for this runtime mode.
continue
}
if err := f(r.platform, r.data); err != nil {
result = multierror.Append(result, err)
}
}
}(phase)
wg.Wait()
if result != nil {
return result.ErrorOrNil()
}
}
return nil
}
// Add adds a phase to a Runner.
func (r *Runner) Add(phase ...*Phase) {
r.phases = append(r.phases, phase...)
}
// Phase represents a phase in the boot process.
type Phase struct {
name string
tasks []Task
}
// NewPhase initializes and returns a Phase.
func NewPhase(name string, tasks ...Task) *Phase {
tasks = append([]Task{}, tasks...)
return &Phase{
name: name,
tasks: tasks,
}
}
// RuntimeFunc defines the function that a task must return. The function
// envelopes the task logic for a given runtim mode.
type RuntimeFunc func(platform.Platform, *userdata.UserData) error
// Task represents a task within a Phase.
type Task interface {
RuntimeFunc(runtime.Mode) RuntimeFunc
}

View File

@ -74,11 +74,11 @@ func Hosts() (err error) {
return
}
if err = ioutil.WriteFile("/run/hosts", writer.Bytes(), 0644); err != nil {
if err = ioutil.WriteFile("/run/system/etc/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 {
if err = unix.Mount("/run/system/etc/hosts", "/etc/hosts", "", unix.MS_BIND, ""); err != nil {
return errors.Wrap(err, "failed to create bind mount for /etc/hosts")
}
@ -88,7 +88,7 @@ func Hosts() (err error) {
// ResolvConf copies the resolv.conf generated in the early boot to the new
// root.
func ResolvConf() (err error) {
target := "/run/resolv.conf"
target := "/run/system/etc/resolv.conf"
var f *os.File
if f, err = os.OpenFile(target, os.O_WRONLY|os.O_CREATE, 0644); err != nil {
return err
@ -96,7 +96,7 @@ func ResolvConf() (err error) {
// nolint: errcheck
defer f.Close()
if err = unix.Mount("/run/resolv.conf", "/etc/resolv.conf", "", unix.MS_BIND, ""); err != nil {
if err = unix.Mount("/run/system/etc/resolv.conf", "/etc/resolv.conf", "", unix.MS_BIND, ""); err != nil {
return errors.Wrap(err, "failed to create bind mount for /etc/resolv.conf")
}
@ -134,11 +134,11 @@ func OSRelease() (err error) {
return
}
if err = ioutil.WriteFile("/run/os-release", writer.Bytes(), 0644); err != nil {
return fmt.Errorf("write /run/os-release: %v", err)
if err = ioutil.WriteFile("/run/system/etc/os-release", writer.Bytes(), 0644); err != nil {
return fmt.Errorf("write /run/system/etc/os-release: %v", err)
}
if err = unix.Mount("/run/os-release", "/etc/os-release", "", unix.MS_BIND, ""); err != nil {
if err = unix.Mount("/run/system/etc/os-release", "/etc/os-release", "", unix.MS_BIND, ""); err != nil {
return errors.Wrap(err, "failed to create bind mount for /etc/os-release")
}

View File

@ -0,0 +1,40 @@
/* 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 rootfs
import (
"github.com/pkg/errors"
"github.com/talos-systems/talos/internal/app/machined/internal/mount/cgroups"
"github.com/talos-systems/talos/internal/app/machined/internal/phase"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"github.com/talos-systems/talos/pkg/userdata"
)
// MountCgroups represents the MountCgroups task.
type MountCgroups struct{}
// NewMountCgroupsTask initializes and returns an MountCgroups task.
func NewMountCgroupsTask() phase.Task {
return &MountCgroups{}
}
// RuntimeFunc returns the runtime function.
func (task *MountCgroups) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc {
switch mode {
case runtime.Standard:
return task.runtime
default:
return nil
}
}
func (task *MountCgroups) runtime(platform platform.Platform, data *userdata.UserData) (err error) {
if err = cgroups.Mount(); err != nil {
return errors.Wrap(err, "error mounting cgroups")
}
return nil
}

View File

@ -0,0 +1,95 @@
/* 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 rootfs
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/pkg/errors"
"github.com/talos-systems/talos/internal/app/machined/internal/phase"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"github.com/talos-systems/talos/pkg/userdata"
"golang.org/x/sys/unix"
)
const (
// SystemVarPath is the path to write runtime system related files and
// directories.
SystemVarPath = "/var/system"
)
// MountOverlay represents the MountOverlay task.
type MountOverlay struct{}
// NewMountOverlayTask initializes and returns an MountOverlay task.
func NewMountOverlayTask() phase.Task {
return &MountOverlay{}
}
// RuntimeFunc returns the runtime function.
func (task *MountOverlay) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc {
switch mode {
case runtime.Standard:
return task.standard
case runtime.Container:
return task.container
default:
return nil
}
}
func (task *MountOverlay) standard(platform platform.Platform, data *userdata.UserData) (err error) {
overlays := []string{
"/etc/kubernetes",
"/etc/cni",
"/usr/libexec/kubernetes",
"/usr/etc/udev",
"/opt",
}
// Create all overlay mounts.
for _, o := range overlays {
if err = overlay(o); err != nil {
return err
}
}
return nil
}
func (task *MountOverlay) container(platform platform.Platform, data *userdata.UserData) (err error) {
targets := []string{"/", "/var/lib/kubelet", "/etc/cni"}
for _, t := range targets {
if err = unix.Mount("", t, "", unix.MS_SHARED, ""); err != nil {
return err
}
}
return nil
}
func overlay(path string) error {
parts := strings.Split(path, "/")
prefix := strings.Join(parts[1:], "-")
diff := fmt.Sprintf(filepath.Join(SystemVarPath, "%s-diff"), prefix)
workdir := fmt.Sprintf(filepath.Join(SystemVarPath, "%s-workdir"), prefix)
for _, s := range []string{diff, workdir} {
if err := os.MkdirAll(s, 0700); err != nil {
return err
}
}
opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", path, diff, workdir)
if err := unix.Mount("overlay", path, "overlay", 0, opts); err != nil {
return errors.Errorf("error creating overlay mount to %s: %v", path, err)
}
return nil
}

View File

@ -0,0 +1,45 @@
/* 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 rootfs
import (
"github.com/talos-systems/talos/internal/app/machined/internal/phase"
"github.com/talos-systems/talos/internal/app/machined/internal/phase/rootfs/etc"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"github.com/talos-systems/talos/pkg/userdata"
)
// NetworkConfiguration represents the NetworkConfiguration task.
type NetworkConfiguration struct{}
// NewNetworkConfigurationTask initializes and returns an NetworkConfiguration task.
func NewNetworkConfigurationTask() phase.Task {
return &NetworkConfiguration{}
}
// RuntimeFunc returns the runtime function.
func (task *NetworkConfiguration) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc {
switch mode {
case runtime.Standard:
return task.runtime
default:
return nil
}
}
func (task *NetworkConfiguration) runtime(platform platform.Platform, data *userdata.UserData) (err error) {
// Create /etc/hosts.
if err = etc.Hosts(); err != nil {
return err
}
// Create /etc/resolv.conf.
if err = etc.ResolvConf(); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,31 @@
/* 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 rootfs
import (
"github.com/talos-systems/talos/internal/app/machined/internal/phase"
"github.com/talos-systems/talos/internal/app/machined/internal/phase/rootfs/etc"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"github.com/talos-systems/talos/pkg/userdata"
)
// OSRelease represents the OSRelease task.
type OSRelease struct{}
// NewOSReleaseTask initializes and returns an OSRelease task.
func NewOSReleaseTask() phase.Task {
return &OSRelease{}
}
// RuntimeFunc returns the runtime function.
func (task *OSRelease) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc {
return task.runtime
}
func (task *OSRelease) runtime(platform platform.Platform, data *userdata.UserData) (err error) {
// Create /etc/os-release.
return etc.OSRelease()
}

View File

@ -0,0 +1,31 @@
/* 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 rootfs
import (
"os"
"github.com/talos-systems/talos/internal/app/machined/internal/phase"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"github.com/talos-systems/talos/pkg/userdata"
)
// SystemDirectory represents the SystemDirectory task.
type SystemDirectory struct{}
// NewSystemDirectoryTask initializes and returns an SystemDirectory task.
func NewSystemDirectoryTask() phase.Task {
return &SystemDirectory{}
}
// RuntimeFunc returns the runtime function.
func (task *SystemDirectory) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc {
return task.runtime
}
func (task *SystemDirectory) runtime(platform platform.Platform, data *userdata.UserData) (err error) {
return os.MkdirAll("/run/system/etc", os.ModeDir)
}

View File

@ -0,0 +1,37 @@
/* 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 rootfs
import (
"os"
"github.com/talos-systems/talos/internal/app/machined/internal/phase"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"github.com/talos-systems/talos/pkg/userdata"
)
// VarDirectories represents the VarDirectories task.
type VarDirectories struct{}
// NewVarDirectoriesTask initializes and returns an VarDirectories task.
func NewVarDirectoriesTask() phase.Task {
return &VarDirectories{}
}
// RuntimeFunc returns the runtime function.
func (task *VarDirectories) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc {
return task.runtime
}
func (task *VarDirectories) runtime(platform platform.Platform, data *userdata.UserData) (err error) {
for _, p := range []string{"/var/log", "/var/lib/kubelet", "/var/log/pods"} {
if err = os.MkdirAll(p, 0700); err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,42 @@
/* 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 security
import (
"github.com/talos-systems/talos/internal/app/machined/internal/phase"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"github.com/talos-systems/talos/internal/pkg/security/kspp"
"github.com/talos-systems/talos/pkg/userdata"
)
// Security represents the Security task.
type Security struct{}
// NewSecurityTask initializes and returns an Security task.
func NewSecurityTask() phase.Task {
return &Security{}
}
// RuntimeFunc returns the runtime function.
func (task *Security) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc {
switch mode {
case runtime.Standard:
return task.runtime
default:
return nil
}
}
func (task *Security) runtime(platform platform.Platform, data *userdata.UserData) (err error) {
if err = kspp.EnforceKSPPKernelParameters(); err != nil {
return err
}
if err = kspp.EnforceKSPPSysctls(); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,14 @@
/* 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 security_test
import "testing"
func TestEmpty(t *testing.T) {
// added for accurate coverage estimation
//
// please remove it once any unit-test is added
// for this package
}

View File

@ -0,0 +1,63 @@
/* 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 services
import (
"github.com/talos-systems/talos/internal/app/machined/internal/phase"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"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/pkg/userdata"
)
// Services represents the Services task.
type Services struct{}
// NewServicesTask initializes and returns an Services task.
func NewServicesTask() phase.Task {
return &Services{}
}
// RuntimeFunc returns the runtime function.
func (task *Services) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc {
return task.runtime
}
func (task *Services) runtime(platform platform.Platform, data *userdata.UserData) (err error) {
go startSystemServices(data)
go startKubernetesServices(data)
return nil
}
func startSystemServices(data *userdata.UserData) {
svcs := system.Services(data)
// Start the services common to all nodes.
svcs.Start(
&services.Networkd{},
&services.Containerd{},
&services.Udevd{},
&services.UdevdTrigger{},
&services.OSD{},
&services.NTPd{},
)
// Start the services common to all master nodes.
if data.Services.Kubeadm.IsControlPlane() {
svcs.Start(
&services.Trustd{},
&services.Proxyd{},
)
}
}
func startKubernetesServices(data *userdata.UserData) {
svcs := system.Services(data)
svcs.Start(
&services.Kubelet{},
&services.Kubeadm{},
)
}

View File

@ -0,0 +1,14 @@
/* 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 services_test
import "testing"
func TestEmpty(t *testing.T) {
// added for accurate coverage estimation
//
// please remove it once any unit-test is added
// for this package
}

View File

@ -0,0 +1,30 @@
/* 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 sysctls
import (
"github.com/talos-systems/talos/internal/app/machined/internal/phase"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"github.com/talos-systems/talos/internal/pkg/proc"
"github.com/talos-systems/talos/pkg/userdata"
)
// Sysctls represents the Sysctls task.
type Sysctls struct{}
// NewSysctlsTask initializes and returns an UserData task.
func NewSysctlsTask() phase.Task {
return &Sysctls{}
}
// RuntimeFunc returns the runtime function.
func (task *Sysctls) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc {
return task.runtime
}
func (task *Sysctls) runtime(platform platform.Platform, data *userdata.UserData) (err error) {
return proc.WriteSystemProperty(&proc.SystemProperty{Key: "net.ipv4.ip_forward", Value: "1"})
}

View File

@ -0,0 +1,14 @@
/* 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 sysctls_test
import "testing"
func TestEmpty(t *testing.T) {
// added for accurate coverage estimation
//
// please remove it once any unit-test is added
// for this package
}

View File

@ -0,0 +1,35 @@
/* 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 userdata
import (
"github.com/talos-systems/talos/internal/app/machined/internal/mount"
"github.com/talos-systems/talos/internal/app/machined/internal/phase"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"github.com/talos-systems/talos/pkg/userdata"
)
// ExtraDevices represents the ExtraDevices task.
type ExtraDevices struct{}
// NewExtraDevicesTask initializes and returns an UserData task.
func NewExtraDevicesTask() phase.Task {
return &ExtraDevices{}
}
// RuntimeFunc returns the runtime function.
func (task *ExtraDevices) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc {
return task.runtime
}
func (task *ExtraDevices) runtime(platform platform.Platform, data *userdata.UserData) (err error) {
// Mount the extra devices.
if err = mount.ExtraDevices(data); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,39 @@
/* 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 userdata
import (
"log"
"os"
"github.com/talos-systems/talos/internal/app/machined/internal/phase"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"github.com/talos-systems/talos/pkg/userdata"
)
// ExtraEnvVars represents the ExtraEnvVars task.
type ExtraEnvVars struct{}
// NewExtraEnvVarsTask initializes and returns an UserData task.
func NewExtraEnvVarsTask() phase.Task {
return &ExtraEnvVars{}
}
// RuntimeFunc returns the runtime function.
func (task *ExtraEnvVars) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc {
return task.runtime
}
func (task *ExtraEnvVars) runtime(platform platform.Platform, data *userdata.UserData) (err error) {
// Set the requested environment variables.
for key, val := range data.Env {
if err = os.Setenv(key, val); err != nil {
log.Printf("WARNING failed to set enivronment variable: %v", err)
}
}
return nil
}

View File

@ -0,0 +1,46 @@
/* 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 userdata
import (
"io/ioutil"
"os"
"path/filepath"
"github.com/hashicorp/go-multierror"
"github.com/talos-systems/talos/internal/app/machined/internal/phase"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"github.com/talos-systems/talos/pkg/userdata"
)
// ExtraFiles represents the ExtraFiles task.
type ExtraFiles struct{}
// NewExtraFilesTask initializes and returns an UserData task.
func NewExtraFilesTask() phase.Task {
return &ExtraFiles{}
}
// RuntimeFunc returns the runtime function.
func (task *ExtraFiles) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc {
return task.runtime
}
func (task *ExtraFiles) runtime(platform platform.Platform, data *userdata.UserData) (err error) {
var result *multierror.Error
for _, f := range data.Files {
p := filepath.Join("/var", f.Path)
if err = os.MkdirAll(filepath.Dir(p), os.ModeDir); err != nil {
result = multierror.Append(result, err)
}
if err = ioutil.WriteFile(p, []byte(f.Contents), f.Permissions); err != nil {
result = multierror.Append(result, err)
}
}
return result.ErrorOrNil()
}

View File

@ -0,0 +1,61 @@
/* 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 userdata
import (
"log"
"time"
"github.com/pkg/errors"
"github.com/talos-systems/talos/internal/app/machined/internal/phase"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"github.com/talos-systems/talos/internal/pkg/constants"
"github.com/talos-systems/talos/internal/pkg/grpc/gen"
"github.com/talos-systems/talos/pkg/crypto/x509"
"github.com/talos-systems/talos/pkg/userdata"
)
// PKI represents the PKI task.
type PKI struct{}
// NewPKITask initializes and returns an UserData task.
func NewPKITask() phase.Task {
return &PKI{}
}
// RuntimeFunc returns the runtime function.
func (task *PKI) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc {
return task.runtime
}
func (task *PKI) runtime(platform platform.Platform, data *userdata.UserData) (err error) {
if data.Services.Kubeadm.IsControlPlane() {
log.Println("generating PKI locally")
var csr *x509.CertificateSigningRequest
if csr, err = data.NewIdentityCSR(); err != nil {
return err
}
var crt *x509.Certificate
crt, err = x509.NewCertificateFromCSRBytes(data.Security.OS.CA.Crt, data.Security.OS.CA.Key, csr.X509CertificateRequestPEM, x509.NotAfter(time.Now().Add(time.Duration(8760)*time.Hour)))
if err != nil {
return err
}
data.Security.OS.Identity.Crt = crt.X509CertificatePEM
return nil
}
log.Println("generating PKI from trustd")
var generator *gen.Generator
generator, err = gen.NewGenerator(data, constants.TrustdPort)
if err != nil {
return errors.Wrap(err, "failed to create trustd client")
}
if err = generator.Identity(data); err != nil {
return errors.Wrap(err, "failed to generate identity")
}
return nil
}

View File

@ -0,0 +1,53 @@
/* 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 userdata
import (
"io/ioutil"
"log"
"os"
"github.com/talos-systems/talos/internal/app/machined/internal/phase"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"github.com/talos-systems/talos/internal/pkg/constants"
"github.com/talos-systems/talos/pkg/userdata"
"gopkg.in/yaml.v2"
)
// SaveUserData represents the SaveUserData task.
type SaveUserData struct{}
// NewSaveUserDataTask initializes and returns an UserData task.
func NewSaveUserDataTask() phase.Task {
return &SaveUserData{}
}
// RuntimeFunc returns the runtime function.
func (task *SaveUserData) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc {
return task.runtime
}
func (task *SaveUserData) runtime(platform platform.Platform, data *userdata.UserData) (err error) {
if _, err = os.Stat(constants.UserDataPath); os.IsNotExist(err) {
log.Println("saving userdata to disk")
var dataBytes []byte
dataBytes, err = yaml.Marshal(data)
if err != nil {
return err
}
if err = ioutil.WriteFile(constants.UserDataPath, dataBytes, 0400); err != nil {
return err
}
return nil
}
log.Println("refusing to overwrite userdata on disk")
return nil
}

View File

@ -0,0 +1,73 @@
/* 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 userdata
import (
"github.com/talos-systems/talos/internal/app/machined/internal/phase"
"github.com/talos-systems/talos/internal/app/machined/internal/platform"
"github.com/talos-systems/talos/internal/app/machined/internal/runtime"
"github.com/talos-systems/talos/internal/pkg/network"
"github.com/talos-systems/talos/pkg/userdata"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
// UserData represents the UserData task.
type UserData struct{}
// NewUserDataTask initializes and returns an UserData task.
func NewUserDataTask() phase.Task {
return &UserData{}
}
// RuntimeFunc returns the runtime function.
func (task *UserData) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc {
switch mode {
case runtime.Standard:
return task.standard
case runtime.Container:
return task.container
default:
return nil
}
}
func (task *UserData) standard(platform platform.Platform, data *userdata.UserData) (err error) {
// Setup basic networking for the purposes of downloading the userdata.
if err = network.InitNetwork(); err != nil {
return err
}
var d *userdata.UserData
d, err = platform.UserData()
if err != nil {
return err
}
*data = *d
return nil
}
func (task *UserData) container(platform platform.Platform, data *userdata.UserData) (err error) {
var d *userdata.UserData
d, err = platform.UserData()
if err != nil {
return err
}
*data = *d
data.Services.Kubeadm.IgnorePreflightErrors = []string{"FileContent--proc-sys-net-bridge-bridge-nf-call-iptables", "Swap", "SystemVerification"}
initConfiguration, ok := data.Services.Kubeadm.Configuration.(*kubeadmapi.InitConfiguration)
if ok {
initConfiguration.ClusterConfiguration.ComponentConfigs.Kubelet.FailSwapOn = false
// See https://github.com/kubernetes/kubernetes/issues/58610#issuecomment-359552443
max := int32(0)
maxPerCore := int32(0)
initConfiguration.ClusterConfiguration.ComponentConfigs.KubeProxy.Conntrack.Max = &max
initConfiguration.ClusterConfiguration.ComponentConfigs.KubeProxy.Conntrack.MaxPerCore = &maxPerCore
}
return nil
}

View File

@ -0,0 +1,14 @@
/* 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 userdata_test
import "testing"
func TestEmpty(t *testing.T) {
// added for accurate coverage estimation
//
// please remove it once any unit-test is added
// for this package
}

View File

@ -0,0 +1,51 @@
/* 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 container
import (
"encoding/base64"
"os"
"github.com/pkg/errors"
"github.com/talos-systems/talos/pkg/userdata"
"gopkg.in/yaml.v2"
)
// Container is a platform for installing Talos via an Container image.
type Container struct{}
// Name implements the platform.Platform interface.
func (c *Container) Name() string {
return "Container"
}
// UserData implements the platform.Platform interface.
func (c *Container) UserData() (data *userdata.UserData, err error) {
s, ok := os.LookupEnv("USERDATA")
if !ok {
return nil, errors.New("missing USERDATA environment variable")
}
var decoded []byte
if decoded, err = base64.StdEncoding.DecodeString(s); err != nil {
return nil, err
}
data = &userdata.UserData{}
if err = yaml.Unmarshal(decoded, data); err != nil {
return nil, err
}
return data, nil
}
// Prepare implements the platform.Platform interface.
func (c *Container) Prepare(data *userdata.UserData) (err error) {
return nil
}
// Install implements the platform.Platform interface.
func (c *Container) Install(data *userdata.UserData) error {
return nil
}

View File

@ -5,6 +5,8 @@
package platform
import (
"os"
"github.com/pkg/errors"
"github.com/talos-systems/talos/internal/app/machined/internal/platform/baremetal"
"github.com/talos-systems/talos/internal/app/machined/internal/platform/cloud/aws"
@ -12,6 +14,7 @@ import (
"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/container"
"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"
@ -27,19 +30,31 @@ type Platform interface {
}
// NewPlatform is a helper func for discovering the current platform.
//
// nolint: gocyclo
func NewPlatform() (p Platform, err error) {
var platform *string
if platform = kernel.Cmdline().Get(constants.KernelParamPlatform).First(); platform == nil {
return nil, errors.Errorf("kernel parameter %s was not found", constants.KernelParamPlatform)
var platform string
if p := kernel.Cmdline().Get(constants.KernelParamPlatform).First(); p != nil {
platform = *p
}
switch *platform {
if p, ok := os.LookupEnv("PLATFORM"); ok {
platform = p
}
if platform == "" {
return nil, errors.New("failed to determine platform")
}
switch platform {
case "aws":
p = &aws.AWS{}
case "azure":
p = &azure.Azure{}
case "bare-metal":
p = &baremetal.BareMetal{}
case "container":
p = &container.Container{}
case "googlecloud":
p = &googlecloud.GoogleCloud{}
case "iso":
@ -49,7 +64,7 @@ func NewPlatform() (p Platform, err error) {
case "vmware":
p = &vmware.VMware{}
default:
return nil, errors.Errorf("platform not supported: %s", *platform)
return nil, errors.Errorf("platform not supported: %s", platform)
}
return p, nil

View File

@ -0,0 +1,20 @@
/* 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 runtime
// Mode is a runtime mode.
type Mode int
const (
// Standard represents a runtime mode.
Standard Mode = iota
// Container represents a runtime mode.
Container
)
// String returns the string representation of a Mode.
func (m Mode) String() string {
return [...]string{"Standard", "Container"}[m]
}

View File

@ -5,379 +5,95 @@
package main
import (
"encoding/base64"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"os/signal"
"strings"
"syscall"
"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/app/machined/internal/phase"
apiptask "github.com/talos-systems/talos/internal/app/machined/internal/phase/api"
"github.com/talos-systems/talos/internal/app/machined/internal/phase/install"
"github.com/talos-systems/talos/internal/app/machined/internal/phase/network"
"github.com/talos-systems/talos/internal/app/machined/internal/phase/rootfs"
"github.com/talos-systems/talos/internal/app/machined/internal/phase/security"
"github.com/talos-systems/talos/internal/app/machined/internal/phase/services"
"github.com/talos-systems/talos/internal/app/machined/internal/phase/sysctls"
userdatatask "github.com/talos-systems/talos/internal/app/machined/internal/phase/userdata"
"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"
"golang.org/x/sys/unix"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
var (
inContainer *bool
rebootFlag = unix.LINUX_REBOOT_CMD_RESTART
userdataArg *string
)
func init() {
inContainer = flag.Bool("in-container", false, "run Talos in a container")
userdataArg = flag.String("userdata", "", "the base64 encoded userdata")
flag.Parse()
}
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 nil, errors.Wrap(err, "failed to open /dev/kmsg")
}
log.SetOutput(out)
log.SetPrefix(prefix + " ")
log.SetFlags(0)
return out, nil
}
func container() (err error) {
log.Println("preparing container based deploy")
log.Println("remounting volumes as shared mounts")
targets := []string{"/", "/var/lib/kubelet", "/etc/cni"}
for _, t := range targets {
if err = unix.Mount("", t, "", unix.MS_SHARED, ""); err != nil {
return err
}
}
if *userdataArg != "" {
log.Printf("writing the user data: %s\n", constants.UserDataPath)
var decoded []byte
if decoded, err = base64.StdEncoding.DecodeString(*userdataArg); err != nil {
return err
}
if err = ioutil.WriteFile(constants.UserDataPath, decoded, 0400); err != nil {
return err
}
}
var data *userdata.UserData
if data, err = userdata.Open(constants.UserDataPath); err != nil {
func run() (err error) {
if err = startup.RandSeed(); err != nil {
return err
}
// Workarounds for running in a container.
data.Services.Kubeadm.IgnorePreflightErrors = []string{"FileContent--proc-sys-net-bridge-bridge-nf-call-iptables", "Swap", "SystemVerification"}
initConfiguration, ok := data.Services.Kubeadm.Configuration.(*kubeadmapi.InitConfiguration)
if ok {
initConfiguration.ClusterConfiguration.ComponentConfigs.Kubelet.FailSwapOn = false
// See https://github.com/kubernetes/kubernetes/issues/58610#issuecomment-359552443
max := int32(0)
maxPerCore := int32(0)
initConfiguration.ClusterConfiguration.ComponentConfigs.KubeProxy.Conntrack.Max = &max
initConfiguration.ClusterConfiguration.ComponentConfigs.KubeProxy.Conntrack.MaxPerCore = &maxPerCore
if err = os.Setenv("PATH", constants.PATH); err != nil {
return errors.New("error setting PATH")
}
log.Println("preparing the root filesystem")
if err = rootfs.Prepare("", true, data); err != nil {
data := &userdata.UserData{}
phaserunner, err := phase.NewRunner(data)
if err != nil {
return err
}
phaserunner.Add(
phase.NewPhase(
"system requirements",
security.NewSecurityTask(),
rootfs.NewSystemDirectoryTask(),
rootfs.NewMountCgroupsTask(),
sysctls.NewSysctlsTask(),
),
phase.NewPhase(
"basic system configuration",
rootfs.NewNetworkConfigurationTask(),
rootfs.NewOSReleaseTask(),
),
phase.NewPhase(
"userdata",
userdatatask.NewUserDataTask(),
),
phase.NewPhase(
"mount extra devices",
userdatatask.NewExtraDevicesTask(),
),
phase.NewPhase(
"user requests",
userdatatask.NewPKITask(),
network.NewUserDefinedNetworkTask(),
userdatatask.NewExtraEnvVarsTask(),
userdatatask.NewExtraFilesTask(),
),
phase.NewPhase(
"installation",
install.NewInstallTask(),
rootfs.NewMountOverlayTask(),
),
phase.NewPhase(
"setup /var",
rootfs.NewVarDirectoriesTask(),
),
phase.NewPhase(
"save userdata",
userdatatask.NewSaveUserDataTask(),
),
phase.NewPhase(
"service setup",
apiptask.NewAPITask(),
services.NewServicesTask(),
),
)
if err = phaserunner.Run(); err != nil {
return err
}
return nil
}
func createOverlay(path string) error {
log.Printf("mounting overlay for %s\n", path)
parts := strings.Split(path, "/")
prefix := strings.Join(parts[1:], "-")
diff := fmt.Sprintf("/var/%s-diff", prefix)
workdir := fmt.Sprintf("/var/%s-workdir", prefix)
for _, s := range []string{diff, workdir} {
if err := os.MkdirAll(s, 0700); err != nil {
return err
}
}
opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", path, diff, workdir)
if err := unix.Mount("overlay", path, "overlay", 0, opts); err != nil {
return errors.Errorf("error creating overlay mount to %s: %v", path, err)
}
return nil
}
// 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
}
}
}
for _, p := range []string{"/var/log", "/var/lib/kubelet", "/var/log/pods"} {
if err = os.MkdirAll(p, 0700); err != nil {
return err
}
}
// Read the user data.
log.Printf("reading the user data: %s\n", constants.UserDataPath)
var data *userdata.UserData
if data, err = userdata.Open(constants.UserDataPath); err != nil {
return err
}
// Mount the extra partitions.
log.Printf("mounting the extra partitions")
if err = mount.ExtraDevices(data); err != nil {
return err
}
// 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 {
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 {
log.Printf("WARNING failed to set enivronment variable: %v", err)
}
}
poweroffCh, err := listenForPowerButton()
if err != nil {
log.Printf("WARNING: power off events will be ignored: %+v", err)
}
termCh := make(chan os.Signal, 1)
signal.Notify(termCh, syscall.SIGTERM)
// Get a handle to the system services API.
svcs := system.Services(data)
defer svcs.Shutdown()
// Instantiate internal init API
api := reg.NewRegistrator(data)
server := factory.NewServer(api)
listener, err := factory.NewListener(
factory.Network("unix"),
factory.SocketPath(constants.InitSocketPath),
)
if err != nil {
panic(err)
}
defer server.Stop()
go func() {
// nolint: errcheck
server.Serve(listener)
}()
startSystemServices(data)
startKubernetesServices(data)
select {
case <-api.ShutdownCh:
log.Printf("poweroff via API received")
// poweroff, proceed to shutdown but mark as poweroff
rebootFlag = unix.LINUX_REBOOT_CMD_POWER_OFF
case <-poweroffCh:
log.Printf("poweroff via ACPI")
// poweroff, proceed to shutdown but mark as poweroff
rebootFlag = unix.LINUX_REBOOT_CMD_POWER_OFF
case <-termCh:
log.Printf("SIGTERM received, rebooting...")
case <-api.RebootCh:
log.Printf("reboot via API received, rebooting...")
}
return nil
}
func startSystemServices(data *userdata.UserData) {
svcs := system.Services(data)
log.Println("starting system services")
// Start the services common to all nodes.
svcs.Start(
&services.Networkd{},
&services.Containerd{},
&services.Udevd{},
&services.UdevdTrigger{},
&services.OSD{},
&services.NTPd{},
)
// Start the services common to all master nodes.
if data.Services.Kubeadm.IsControlPlane() {
svcs.Start(
&services.Trustd{},
&services.Proxyd{},
)
}
}
func startKubernetesServices(data *userdata.UserData) {
svcs := system.Services(data)
log.Println("starting kubernetes services")
svcs.Start(
&services.Kubelet{},
&services.Kubeadm{},
)
}
func sync() {
syncdone := make(chan struct{})
go func() {
defer close(syncdone)
unix.Sync()
}()
log.Printf("waiting for sync...")
for i := 29; i >= 0; i-- {
select {
case <-syncdone:
log.Printf("sync done")
return
case <-time.After(time.Second):
}
if i != 0 {
log.Printf("waiting %d more seconds for sync to finish", i)
}
}
log.Printf("sync hasn't completed in time, aborting...")
}
func reboot() {
// See http://man7.org/linux/man-pages/man2/reboot.2.html.
sync()
// nolint: errcheck
unix.Reboot(rebootFlag)
if *inContainer {
return
}
select {}
}
func recovery() {
if r := recover(); r != nil {
log.Printf("recovered from: %+v\n", r)
@ -401,26 +117,12 @@ func main() {
// services are gracefully shutdown.
// on any return from init.main(), initiate host reboot or shutdown
defer reboot()
// handle any panics in the main goroutine, and proceed to reboot() above
defer recovery()
if err := startup.RandSeed(); err != nil {
panic(err)
}
// TODO(andrewrynhard): Remove this and be explicit.
if err := os.Setenv("PATH", constants.PATH); err != nil {
panic(errors.New("error setting PATH"))
}
if *inContainer {
if err := container(); err != nil {
panic(errors.Wrap(err, "failed to prepare container based deploy"))
}
}
if err := root(); err != nil {
if err := run(); err != nil {
panic(errors.Wrap(err, "boot failed"))
}
select {}
}

View File

@ -17,6 +17,7 @@ import (
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/talos-systems/talos/internal/app/machined/internal/cni"
"github.com/talos-systems/talos/internal/app/machined/pkg/system"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/conditions"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/health"
@ -24,7 +25,6 @@ import (
"github.com/talos-systems/talos/internal/app/machined/pkg/system/runner/containerd"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/runner/restart"
"github.com/talos-systems/talos/internal/pkg/constants"
"github.com/talos-systems/talos/internal/pkg/rootfs/cni"
"github.com/talos-systems/talos/pkg/userdata"
)

View File

@ -9,6 +9,7 @@ import (
"context"
"fmt"
"net"
"path/filepath"
containerdapi "github.com/containerd/containerd"
"github.com/containerd/containerd/oci"
@ -74,7 +75,7 @@ func (o *OSD) Runner(data *userdata.UserData) (runner.Runner, error) {
{Type: "bind", Destination: "/etc/kubernetes", Source: "/etc/kubernetes", Options: []string{"bind", "rw"}},
{Type: "bind", Destination: "/etc/ssl", Source: "/etc/ssl", Options: []string{"bind", "ro"}},
{Type: "bind", Destination: "/var/log", Source: "/var/log", Options: []string{"rbind", "rw"}},
{Type: "bind", Destination: "/var/lib/init", Source: "/var/lib/init", Options: []string{"rbind", "rw"}},
{Type: "bind", Destination: filepath.Dir(constants.InitSocketPath), Source: filepath.Dir(constants.InitSocketPath), Options: []string{"rbind", "rw"}},
}
env := []string{}

View File

@ -143,7 +143,7 @@ const (
TalosConfigEnvVar = "TALOSCONFIG"
// InitSocketPath is the path to file socket of init API
InitSocketPath = "/var/lib/init/init.sock"
InitSocketPath = "/run/system/init/init.sock"
// KernelAsset defines a well known name for our kernel filename
KernelAsset = "vmlinuz"

27
internal/pkg/kmsg/kmsg.go Normal file
View File

@ -0,0 +1,27 @@
/* 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 kmsg
import (
"log"
"os"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
// Setup configures the log package to write to the kernel ring buffer via
// /dev/kmsg.
func Setup(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 nil, errors.Wrap(err, "failed to open /dev/kmsg")
}
log.SetOutput(out)
log.SetPrefix(prefix + " ")
log.SetFlags(0)
return out, nil
}

View File

@ -0,0 +1,14 @@
/* 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 kmsg_test
import "testing"
func TestEmpty(t *testing.T) {
// added for accurate coverage estimation
//
// please remove it once any unit-test is added
// for this package
}

View File

@ -6,12 +6,26 @@ package proc
import (
"fmt"
"io/ioutil"
"path"
"strings"
"code.cloudfoundry.org/bytefmt"
"github.com/prometheus/procfs"
)
// SystemProperty represents a kernel system property.
type SystemProperty struct {
Key string
Value string
}
// WriteSystemProperty writes a value to a key under /proc/sys.
func WriteSystemProperty(prop *SystemProperty) error {
keyPath := path.Join("/proc/sys", strings.Replace(prop.Key, ".", "/", -1))
return ioutil.WriteFile(keyPath, []byte(prop.Value), 0644)
}
// ProcessList contains all of the process stats we want
// to display via top
type ProcessList struct {

View File

@ -1,23 +0,0 @@
/* 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 proc
import (
"io/ioutil"
"path"
"strings"
)
// SystemProperty represents a kernel system property.
type SystemProperty struct {
Key string
Value string
}
// WriteSystemProperty writes a value to a key under /proc/sys.
func WriteSystemProperty(prop *SystemProperty) error {
keyPath := path.Join("/proc/sys", strings.Replace(prop.Key, ".", "/", -1))
return ioutil.WriteFile(keyPath, []byte(prop.Value), 0644)
}

View File

@ -1,138 +0,0 @@
/* 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 rootfs
import (
"io/ioutil"
"log"
"os"
"path/filepath"
"time"
"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/proc"
"github.com/talos-systems/talos/pkg/crypto/x509"
"github.com/talos-systems/talos/pkg/userdata"
yaml "gopkg.in/yaml.v2"
)
// Prepare creates the files required by the installed binaries and libraries.
// nolint: gocyclo
func Prepare(s string, inContainer bool, data *userdata.UserData) (err error) {
if !inContainer {
// Enable IP forwarding.
if err = proc.WriteSystemProperty(&proc.SystemProperty{Key: "net.ipv4.ip_forward", Value: "1"}); err != nil {
return
}
// Kernel Self Protection Project recommended settings.
if err = kernelHardening(); err != nil {
return
}
}
// Generate the identity certificate.
if err = generatePKI(data); err != nil {
return
}
// Save the user data to disk.
if _, err = os.Stat(filepath.Join(s, constants.UserDataPath)); os.IsNotExist(err) || inContainer {
log.Println("saving userdata to disk")
var dataBytes []byte
dataBytes, err = yaml.Marshal(data)
if err != nil {
return err
}
if err = ioutil.WriteFile(filepath.Join(s, constants.UserDataPath), dataBytes, 0400); err != nil {
return err
}
} else {
log.Println("using existing userdata on disk")
}
if err != nil {
log.Printf("encountered error reading userdata: %v", err)
return err
}
return nil
}
func generatePKI(data *userdata.UserData) (err error) {
log.Println("generating node identity PKI")
if data.Services.Kubeadm.IsControlPlane() {
log.Println("generating PKI locally")
var csr *x509.CertificateSigningRequest
if csr, err = data.NewIdentityCSR(); err != nil {
return err
}
var crt *x509.Certificate
crt, err = x509.NewCertificateFromCSRBytes(data.Security.OS.CA.Crt, data.Security.OS.CA.Key, csr.X509CertificateRequestPEM, x509.NotAfter(time.Now().Add(time.Duration(8760)*time.Hour)))
if err != nil {
return err
}
data.Security.OS.Identity.Crt = crt.X509CertificatePEM
return nil
}
log.Println("generating PKI from trustd")
var generator *gen.Generator
generator, err = gen.NewGenerator(data, constants.TrustdPort)
if err != nil {
return errors.Wrap(err, "failed to create trustd client")
}
if err = generator.Identity(data); err != nil {
return errors.Wrap(err, "failed to generate identity")
}
return nil
}
func kernelHardening() (err error) {
props := []*proc.SystemProperty{
{
Key: "kernel.kptr_restrict",
Value: "1",
},
{
Key: "kernel.dmesg_restrict",
Value: "1",
},
{
Key: "kernel.perf_event_paranoid",
Value: "3",
},
// We can skip this sysctl because CONFIG_KEXEC is not set.
// {
// Key: "kernel.kexec_load_disabled",
// Value: "1",
// },
{
Key: "kernel.yama.ptrace_scope",
Value: "1",
},
{
Key: "user.max_user_namespaces",
Value: "0",
},
{
Key: "kernel.unprivileged_bpf_disabled",
Value: "1",
},
{
Key: "net.core.bpf_jit_harden",
Value: "2",
},
}
for _, prop := range props {
if err = proc.WriteSystemProperty(prop); err != nil {
return
}
}
return nil
}

View File

@ -8,6 +8,7 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"github.com/talos-systems/talos/internal/pkg/kernel"
"github.com/talos-systems/talos/internal/pkg/proc"
)
var (
@ -40,3 +41,51 @@ func EnforceKSPPKernelParameters() error {
return result.ErrorOrNil()
}
// EnforceKSPPSysctls verifies that all required KSPP kernel sysctls are set
// with the right value.
func EnforceKSPPSysctls() (err error) {
props := []*proc.SystemProperty{
{
Key: "kernel.kptr_restrict",
Value: "1",
},
{
Key: "kernel.dmesg_restrict",
Value: "1",
},
{
Key: "kernel.perf_event_paranoid",
Value: "3",
},
// We can skip this sysctl because CONFIG_KEXEC is not set.
// {
// Key: "kernel.kexec_load_disabled",
// Value: "1",
// },
{
Key: "kernel.yama.ptrace_scope",
Value: "1",
},
{
Key: "user.max_user_namespaces",
Value: "0",
},
{
Key: "kernel.unprivileged_bpf_disabled",
Value: "1",
},
{
Key: "net.core.bpf_jit_harden",
Value: "2",
},
}
for _, prop := range props {
if err = proc.WriteSystemProperty(prop); err != nil {
return
}
}
return nil
}

View File

@ -144,9 +144,6 @@ func Reset() (err error) {
"--force",
)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}

View File

@ -11,7 +11,6 @@ import (
"io/ioutil"
stdlibnet "net"
"os"
"path"
"strings"
"time"
@ -89,22 +88,6 @@ type File struct {
Path string `yaml:"path"`
}
// WriteFiles writes the requested files to disk.
func (data *UserData) WriteFiles() (err error) {
for _, f := range data.Files {
// TODO isnt there a const for the data mountpoint
p := path.Join("/var", f.Path)
if err = os.MkdirAll(path.Dir(p), os.ModeDir); err != nil {
return
}
if err = ioutil.WriteFile(p, []byte(f.Contents), f.Permissions); err != nil {
return
}
}
return nil
}
// NewIdentityCSR creates a new CSR for the node's identity certificate.
func (data *UserData) NewIdentityCSR() (csr *x509.CertificateSigningRequest, err error) {
var key *x509.Key