Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: refactor internal/pkg/install package #10342

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions internal/pkg/install/export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// 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

func WrapOnErr(fn func() error, msg string) func() error {
return wrapOnErr(fn, msg)
}

func LogError(clientClose func() error) {
logError(clientClose)
}
95 changes: 65 additions & 30 deletions internal/pkg/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ package install

import (
"bytes"
"cmp"
"context"
"fmt"
"io"
"log"
"os"
"strconv"
"sync"

containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/pkg/cio"
Expand Down Expand Up @@ -62,22 +64,22 @@ func RunInstallerContainer(
extensionsConfig = cfg.Machine().Install().Extensions()
}

ctx, cancel := context.WithCancel(context.Background())
ctx, cancel := context.WithCancel(namespaces.WithNamespace(context.Background(), constants.SystemContainerdNamespace))
defer cancel()

ctx = namespaces.WithNamespace(ctx, constants.SystemContainerdNamespace)

client, err := containerd.New(constants.SystemContainerdAddress)
if err != nil {
return err
}

defer client.Close() //nolint:errcheck
clientClose := wrapOnErr(client.Close, "failed to close containerd client")

defer logError(clientClose)

var done func(context.Context) error
ctx, done, err := client.WithLease(ctx)
clientDone := wrapOnErr(func() error { return done(ctx) }, "failed to release containerd lease")

ctx, done, err = client.WithLease(ctx)
defer done(ctx) //nolint:errcheck
defer logError(clientDone)

var img containerd.Image

Expand All @@ -89,15 +91,18 @@ func RunInstallerContainer(
log.Printf("pulling %q", ref)

img, err = image.Pull(ctx, registryBuilder, client, ref)
if err == nil {
log.Printf("pulled %q", ref)
}
}

if err != nil {
return err
return fmt.Errorf("error pulling %q: %w", ref, err)
}

puller, err := extensions.NewPuller(client)
if err != nil {
return err
return fmt.Errorf("error creating system extensions puller: %w", err)
}

if extensionsConfig != nil {
Expand All @@ -113,9 +118,8 @@ func RunInstallerContainer(
}()

// See if there's previous container/snapshot to clean up
var oldcontainer containerd.Container

if oldcontainer, err = client.LoadContainer(ctx, containerID); err == nil {
oldcontainer, err := client.LoadContainer(ctx, containerID)
if err == nil {
if err = oldcontainer.Delete(ctx, containerd.WithSnapshotCleanup); err != nil {
return fmt.Errorf("error deleting old container instance: %w", err)
}
Expand Down Expand Up @@ -158,19 +162,15 @@ func RunInstallerContainer(
config = *c
}

upgrade := strconv.FormatBool(options.Upgrade)
force := strconv.FormatBool(options.Force)
zero := strconv.FormatBool(options.Zero)

args := []string{
"/bin/installer",
"install",
"--disk=" + disk,
"--platform=" + platform,
"--config=" + config,
"--upgrade=" + upgrade,
"--force=" + force,
"--zero=" + zero,
"--upgrade=" + strconv.FormatBool(options.Upgrade),
"--force=" + strconv.FormatBool(options.Force),
"--zero=" + strconv.FormatBool(options.Zero),
}

for _, arg := range options.ExtraKernelArgs {
Expand Down Expand Up @@ -223,17 +223,24 @@ func RunInstallerContainer(

container, err := client.NewContainer(ctx, containerID, containerOpts...)
if err != nil {
return err
return fmt.Errorf("failed to create %q container: %w", containerID, err)
}

defer container.Delete(ctx, containerd.WithSnapshotCleanup) //nolint:errcheck
containerClose := wrapOnErr(
func() error { return container.Delete(ctx, containerd.WithSnapshotCleanup) },
"failed to delete container",
)

defer logError(containerClose)

f, err := os.OpenFile("/dev/kmsg", os.O_RDWR|unix.O_CLOEXEC|unix.O_NONBLOCK|unix.O_NOCTTY, 0o666)
if err != nil {
return fmt.Errorf("failed to open /dev/kmsg: %w", err)
}
//nolint:errcheck
defer f.Close()

fClose := wrapOnErr(f.Close, "failed to close /dev/kmsg")

defer logError(fClose)

w := &kmsg.Writer{KmsgWriter: f}

Expand All @@ -260,14 +267,19 @@ func RunInstallerContainer(

t, err := container.NewTask(ctx, creator)
if err != nil {
return err
return fmt.Errorf("failed to create %q task: %w", containerID, err)
}

if r != nil {
go r.WaitAndClose(ctx, t)
}

defer t.Delete(ctx) //nolint:errcheck
tDelete := wrapOnErr(
func() error { return takeErr(t.Delete(ctx)) },
"failed to delete task",
)

defer logError(tDelete)

if err = t.Start(ctx); err != nil {
return fmt.Errorf("failed to start %q task: %w", "upgrade", err)
Expand All @@ -278,14 +290,11 @@ func RunInstallerContainer(
return fmt.Errorf("failed waiting for %q task: %w", "upgrade", err)
}

status := <-statusC

code := status.ExitCode()
if code != 0 {
if code := (<-statusC).ExitCode(); code != 0 {
return fmt.Errorf("task %q failed: exit code %d", "upgrade", code)
}

return nil
return cmp.Or(tDelete(), fClose(), containerClose(), clientDone(), clientClose())
}

// OptionsFromUpgradeRequest builds installer options from upgrade request.
Expand All @@ -302,3 +311,29 @@ func OptionsFromUpgradeRequest(r runtime.Runtime, in *machineapi.UpgradeRequest)

return opts
}

func wrapOnErr(fn func() error, msg string) func() error {
var once sync.Once

return func() error {
var err error

// Return error only once
once.Do(func() {
err = fn()
if err != nil {
err = fmt.Errorf("%s: %w", msg, err)
}
})

return err
}
}

func logError(clientClose func() error) {
if err := clientClose(); err != nil {
log.Output(2, err.Error()) //nolint:errcheck
}
}

func takeErr[T any](_ T, e error) error { return e }
31 changes: 31 additions & 0 deletions internal/pkg/install/install_test.go
Original file line number Diff line number Diff line change
@@ -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 install_test

import (
"errors"
"log"
"os"

"github.com/siderolabs/talos/internal/pkg/install"
)

func ExampleWrapOnErr() {
log.SetFlags(log.Lshortfile)
log.SetOutput(os.Stdout)

fn := install.WrapOnErr(alwaysErr, "context for the error")

defer install.LogError(fn)

install.LogError(fn)

// Output:
// export_test.go:12: context for the error: always an error
}

func alwaysErr() error {
return errors.New("always an error")
}
30 changes: 19 additions & 11 deletions internal/pkg/install/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ func PullAndValidateInstallerImage(ctx context.Context, registryBuilder image.Re
return err
}

defer client.Close() //nolint:errcheck
clientClose := wrapOnErr(client.Close, "error closing containerd client")

defer logError(clientClose)

img, err := image.Pull(containerdctx, registryBuilder, client, ref, image.WithSkipIfAlreadyPulled())
if err != nil {
Expand Down Expand Up @@ -75,16 +77,24 @@ func PullAndValidateInstallerImage(ctx context.Context, registryBuilder image.Re
return err
}

//nolint:errcheck
defer container.Delete(containerdctx, containerd.WithSnapshotCleanup)
containerDelete := wrapOnErr(
func() error { return container.Delete(containerdctx, containerd.WithSnapshotCleanup) },
"error deleting container",
)

defer logError(containerDelete)

task, err := container.NewTask(containerdctx, cio.NullIO)
if err != nil {
return err
}

//nolint:errcheck
defer task.Delete(containerdctx)
taskDelete := wrapOnErr(
func() error { return takeErr(task.Delete(containerdctx)) },
"error deleting task",
)

defer logError(taskDelete)

exitStatusC, err := task.Wait(containerdctx)
if err != nil {
Expand All @@ -95,14 +105,12 @@ func PullAndValidateInstallerImage(ctx context.Context, registryBuilder image.Re
return err
}

status := <-exitStatusC
code, _, err := (<-exitStatusC).Result()

code, _, err := status.Result()
if err != nil {
switch {
case err != nil:
return err
}

if code != 0 {
case code != 0:
return errors.New("installer help returned non-zero exit. assuming invalid installer")
}

Expand Down