talos/internal/pkg/install/install.go
Spencer Smith 5e8cab4dd5 fix: check for installer image before proceeding with upgrade
This PR will add in some code to pre-pull the installer image before we
run an upgrade of a given talos node. Additionally, this will add some
functional args to the install package to allow for specifying whether
or not to pull the installer image. This was needed since there was no
sense in pulling the installer again once we made it that far into the
upgrade process.

Signed-off-by: Spencer Smith <robertspencersmith@gmail.com>
2020-01-09 15:05:28 -08:00

128 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/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 image containerd.Image
if options.ImagePull {
image, err = client.Pull(ctx, r.Config().Machine().Install().Image(), []containerd.RemoteOpt{containerd.WithPullUnpack}...)
if err != nil {
return err
}
} else {
image, 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(image),
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(image),
containerd.WithNewSnapshot("upgrade", image),
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
}