mirror of
https://github.com/siderolabs/talos.git
synced 2025-10-08 06:01:12 +02:00
This implements a simple way to upgrade Talos node running in maintenance mode (only if Talos is installed, i.e. if `STATE` and `EPHEMERAL` partitions are wiped). Upgrade is only available over SideroLink for security reasons. Upgrade in maintenance mode doesn't support any options, and it works without machine configuration, so proxy environment variables are not available, registry mirrors can't be used, and extensions are not installed. Fixes #6224 Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
111 lines
2.9 KiB
Go
111 lines
2.9 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/errdefs"
|
|
"github.com/containerd/containerd/namespaces"
|
|
"github.com/containerd/containerd/oci"
|
|
|
|
"github.com/talos-systems/talos/internal/pkg/containers/image"
|
|
"github.com/talos-systems/talos/pkg/machinery/config"
|
|
"github.com/talos-systems/talos/pkg/machinery/constants"
|
|
)
|
|
|
|
// PullAndValidateInstallerImage pulls down the installer and validates that it can run.
|
|
//
|
|
//nolint:gocyclo
|
|
func PullAndValidateInstallerImage(ctx context.Context, reg config.Registries, ref string) error {
|
|
// Pull down specified installer image early so we can bail if it doesn't exist in the upstream registry
|
|
containerdctx := namespaces.WithNamespace(ctx, constants.SystemContainerdNamespace)
|
|
|
|
const containerID = "validate"
|
|
|
|
client, err := containerd.New(constants.SystemContainerdAddress)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer client.Close() //nolint:errcheck
|
|
|
|
img, err := image.Pull(containerdctx, reg, client, ref, image.WithSkipIfAlreadyPulled())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// See if there's previous container/snapshot to clean up
|
|
var oldcontainer containerd.Container
|
|
|
|
if oldcontainer, err = client.LoadContainer(containerdctx, containerID); err == nil {
|
|
if err = oldcontainer.Delete(containerdctx, containerd.WithSnapshotCleanup); err != nil {
|
|
return fmt.Errorf("error deleting old container instance: %w", err)
|
|
}
|
|
}
|
|
|
|
if err = client.SnapshotService("").Remove(containerdctx, containerID); err != nil && !errdefs.IsNotFound(err) {
|
|
return fmt.Errorf("error cleaning up stale snapshot: %w", err)
|
|
}
|
|
|
|
// Launch the container with a known help command for a simple check to make sure the image is valid
|
|
args := []string{
|
|
"/bin/installer",
|
|
"--help",
|
|
}
|
|
|
|
specOpts := []oci.SpecOpts{
|
|
oci.WithImageConfig(img),
|
|
oci.WithProcessArgs(args...),
|
|
}
|
|
|
|
containerOpts := []containerd.NewContainerOpts{
|
|
containerd.WithImage(img),
|
|
containerd.WithNewSnapshot(containerID, img),
|
|
containerd.WithNewSpec(specOpts...),
|
|
}
|
|
|
|
container, err := client.NewContainer(containerdctx, containerID, containerOpts...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
//nolint:errcheck
|
|
defer container.Delete(containerdctx, containerd.WithSnapshotCleanup)
|
|
|
|
task, err := container.NewTask(containerdctx, cio.NullIO)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
//nolint:errcheck
|
|
defer task.Delete(containerdctx)
|
|
|
|
exitStatusC, err := task.Wait(containerdctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = task.Start(containerdctx); err != nil {
|
|
return err
|
|
}
|
|
|
|
status := <-exitStatusC
|
|
|
|
code, _, err := status.Result()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if code != 0 {
|
|
return fmt.Errorf("installer help returned non-zero exit. assuming invalid installer")
|
|
}
|
|
|
|
return nil
|
|
}
|