talos/internal/pkg/install/install.go
Andrew Rynhard 28782c2d46 fix: stop race condition between kubelet and networkd
The kubelet fails to start if a machine's hostname is not set. If
networkd doesn't set it in time, the kubelet service fails to start.
Addionally, this adds retries to container pulls to ensure that any
temporary network failures don't cause fatal errors if we can't pull
images.

Signed-off-by: Andrew Rynhard <andrew@andrewrynhard.com>
2020-01-20 10:52:53 -05:00

129 lines
3.3 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package install
import (
"context"
"fmt"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cio"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/oci"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/talos-systems/talos/internal/pkg/containers/image"
"github.com/talos-systems/talos/internal/pkg/kernel"
"github.com/talos-systems/talos/internal/pkg/runtime"
"github.com/talos-systems/talos/pkg/constants"
)
// RunInstallerContainer performs an installation via the installer container.
//nolint: gocyclo
func RunInstallerContainer(r runtime.Runtime, opts ...Option) error {
options := DefaultInstallOptions()
for _, opt := range opts {
if err := opt(&options); err != nil {
return err
}
}
ctx := namespaces.WithNamespace(context.Background(), constants.SystemContainerdNamespace)
client, err := containerd.New(constants.SystemContainerdAddress)
if err != nil {
return err
}
var img containerd.Image
if options.ImagePull {
img, err = image.Pull(ctx, client, r.Config().Machine().Install().Image())
if err != nil {
return err
}
} else {
img, err = client.GetImage(ctx, r.Config().Machine().Install().Image())
if err != nil {
return err
}
}
mounts := []specs.Mount{
{Type: "bind", Destination: "/dev", Source: "/dev", Options: []string{"rbind", "rshared", "rw"}},
}
// TODO(andrewrynhard): To handle cases when the newer version changes the
// platform name, this should be determined in the installer container.
var config *string
if config = kernel.ProcCmdline().Get(constants.KernelParamConfig).First(); config == nil {
return fmt.Errorf("no config option was found")
}
upgrade := "false"
if r.Sequence() == runtime.Upgrade {
upgrade = "true"
}
args := []string{
"/bin/installer",
"install",
"--disk=" + r.Config().Machine().Install().Disk(),
"--platform=" + r.Platform().Name(),
"--config=" + *config,
"--upgrade=" + upgrade,
}
for _, arg := range r.Config().Machine().Install().ExtraKernelArgs() {
args = append(args, []string{"--extra-kernel-arg", arg}...)
}
specOpts := []oci.SpecOpts{
oci.WithImageConfig(img),
oci.WithProcessArgs(args...),
oci.WithHostNamespace(specs.NetworkNamespace),
oci.WithHostNamespace(specs.PIDNamespace),
oci.WithMounts(mounts),
oci.WithHostHostsFile,
oci.WithHostResolvconf,
oci.WithParentCgroupDevices,
oci.WithPrivileged,
}
containerOpts := []containerd.NewContainerOpts{
containerd.WithImage(img),
containerd.WithNewSnapshot("upgrade", img),
containerd.WithNewSpec(specOpts...),
}
container, err := client.NewContainer(ctx, "upgrade", containerOpts...)
if err != nil {
return err
}
t, err := container.NewTask(ctx, cio.LogFile("/dev/kmsg"))
if err != nil {
return err
}
if err = t.Start(ctx); err != nil {
return fmt.Errorf("failed to start %q task: %w", "upgrade", err)
}
statusC, err := t.Wait(ctx)
if err != nil {
return fmt.Errorf("failed waiting for %q task: %w", "upgrade", err)
}
status := <-statusC
code := status.ExitCode()
if code != 0 {
return fmt.Errorf("task %q failed: exit code %d", "upgrade", code)
}
return nil
}