16
"github.com/containerd/containerd"
17
"github.com/containerd/containerd/cio"
18
"github.com/containerd/containerd/errdefs"
19
"github.com/containerd/containerd/namespaces"
20
"github.com/containerd/containerd/oci"
21
"github.com/opencontainers/runtime-spec/specs-go"
22
"github.com/siderolabs/go-kmsg"
23
"github.com/siderolabs/go-procfs/procfs"
24
"golang.org/x/sys/unix"
26
"github.com/aenix-io/talm/internal/app/machined/pkg/runtime"
27
containerdrunner "github.com/aenix-io/talm/internal/app/machined/pkg/system/runner/containerd"
28
"github.com/aenix-io/talm/internal/pkg/capability"
29
"github.com/aenix-io/talm/internal/pkg/containers/image"
30
"github.com/aenix-io/talm/internal/pkg/environment"
31
"github.com/aenix-io/talm/internal/pkg/extensions"
32
machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine"
33
configcore "github.com/siderolabs/talos/pkg/machinery/config"
34
"github.com/siderolabs/talos/pkg/machinery/config/config"
35
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
36
"github.com/siderolabs/talos/pkg/machinery/constants"
42
func RunInstallerContainer(disk, platform, ref string, cfg configcore.Config, cfgContainer configcore.Container, opts ...Option) error {
43
const containerID = "upgrade"
45
options := DefaultInstallOptions()
47
for _, opt := range opts {
48
if err := opt(&options); err != nil {
54
registriesConfig config.Registries
55
extensionsConfig []config.Extension
58
if cfg != nil && cfg.Machine() != nil {
59
registriesConfig = cfg.Machine().Registries()
60
extensionsConfig = cfg.Machine().Install().Extensions()
62
registriesConfig = &v1alpha1.RegistriesConfig{}
65
ctx, cancel := context.WithCancel(context.Background())
68
ctx = namespaces.WithNamespace(ctx, constants.SystemContainerdNamespace)
70
client, err := containerd.New(constants.SystemContainerdAddress)
77
var done func(context.Context) error
79
ctx, done, err = client.WithLease(ctx)
82
var img containerd.Image
85
img, err = client.GetImage(ctx, ref)
88
if img == nil || err != nil && errdefs.IsNotFound(err) {
89
log.Printf("pulling %q", ref)
91
img, err = image.Pull(ctx, registriesConfig, client, ref)
98
puller, err := extensions.NewPuller(client)
103
if extensionsConfig != nil {
104
if err = puller.PullAndMount(ctx, registriesConfig, extensionsConfig); err != nil {
110
if err = puller.Cleanup(ctx); err != nil {
111
log.Printf("error cleaning up pulled system extensions: %s", err)
116
var oldcontainer containerd.Container
118
if oldcontainer, err = client.LoadContainer(ctx, containerID); err == nil {
119
if err = oldcontainer.Delete(ctx, containerd.WithSnapshotCleanup); err != nil {
120
return fmt.Errorf("error deleting old container instance: %w", err)
124
if err = client.SnapshotService("").Remove(ctx, containerID); err != nil && !errdefs.IsNotFound(err) {
125
return fmt.Errorf("error cleaning up stale snapshot: %w", err)
128
mounts := []specs.Mount{
129
{Type: "bind", Destination: "/dev", Source: "/dev", Options: []string{"rbind", "rshared", "rw"}},
130
{Type: "bind", Destination: constants.SystemExtensionsPath, Source: constants.SystemExtensionsPath, Options: []string{"rbind", "rshared", "ro"}},
134
if _, err = os.Stat(constants.MachineSocketPath); err == nil {
135
mounts = append(mounts,
136
specs.Mount{Type: "bind", Destination: constants.MachineSocketPath, Source: constants.MachineSocketPath, Options: []string{"rbind", "rshared", "ro"}},
141
if _, err = os.Stat(constants.EFIVarsMountPoint); err == nil {
142
mounts = append(mounts,
143
specs.Mount{Type: "efivarfs", Source: "efivarfs", Destination: constants.EFIVarsMountPoint, Options: []string{"rw", "nosuid", "nodev", "noexec", "relatime"}},
148
if _, err = os.Stat(constants.SDStubDynamicInitrdPath); err == nil {
149
mounts = append(mounts,
150
specs.Mount{Type: "bind", Destination: constants.SDStubDynamicInitrdPath, Source: constants.SDStubDynamicInitrdPath, Options: []string{"rbind", "rshared", "ro"}},
156
config := constants.ConfigNone
157
if c := procfs.ProcCmdline().Get(constants.KernelParamConfig).First(); c != nil {
161
upgrade := strconv.FormatBool(options.Upgrade)
162
force := strconv.FormatBool(options.Force)
163
zero := strconv.FormatBool(options.Zero)
169
"--platform=" + platform,
170
"--config=" + config,
171
"--upgrade=" + upgrade,
176
for _, arg := range options.ExtraKernelArgs {
177
args = append(args, "--extra-kernel-arg", arg)
180
for _, preservedArg := range []string{
181
constants.KernelParamSideroLink,
182
constants.KernelParamEventsSink,
183
constants.KernelParamLoggingKernel,
184
constants.KernelParamEquinixMetalEvents,
185
constants.KernelParamDashboardDisabled,
186
constants.KernelParamNetIfnames,
188
if c := procfs.ProcCmdline().Get(preservedArg).First(); c != nil {
189
args = append(args, "--extra-kernel-arg", fmt.Sprintf("%s=%s", preservedArg, *c))
193
specOpts := []oci.SpecOpts{
194
oci.WithImageConfig(img),
195
oci.WithProcessArgs(args...),
196
oci.WithHostNamespace(specs.NetworkNamespace),
197
oci.WithHostNamespace(specs.PIDNamespace),
198
oci.WithMounts(mounts),
199
oci.WithHostHostsFile,
200
oci.WithHostResolvconf,
201
oci.WithParentCgroupDevices,
202
oci.WithCapabilities(capability.AllGrantableCapabilities()),
203
oci.WithMaskedPaths(nil),
204
oci.WithReadonlyPaths(nil),
205
oci.WithWriteableSysfs,
206
oci.WithWriteableCgroupfs,
207
oci.WithSelinuxLabel(""),
208
oci.WithApparmorProfile(""),
209
oci.WithSeccompUnconfined,
210
oci.WithAllDevicesAllowed,
211
oci.WithEnv(environment.Get(cfg)),
214
containerOpts := []containerd.NewContainerOpts{
215
containerd.WithImage(img),
216
containerd.WithNewSnapshot(containerID, img),
217
containerd.WithNewSpec(specOpts...),
220
container, err := client.NewContainer(ctx, containerID, containerOpts...)
225
defer container.Delete(ctx, containerd.WithSnapshotCleanup)
227
f, err := os.OpenFile("/dev/kmsg", os.O_RDWR|unix.O_CLOEXEC|unix.O_NONBLOCK|unix.O_NOCTTY, 0o666)
229
return fmt.Errorf("failed to open /dev/kmsg: %w", err)
234
w := &kmsg.Writer{KmsgWriter: f}
238
WaitAndClose(context.Context, containerd.Task)
241
if cfgContainer != nil {
242
var configBytes []byte
244
configBytes, err = cfgContainer.Bytes()
249
r = &containerdrunner.StdinCloser{
250
Stdin: bytes.NewReader(configBytes),
251
Closer: make(chan struct{}),
255
creator := cio.NewCreator(cio.WithStreams(r, w, w))
257
t, err := container.NewTask(ctx, creator)
263
go r.WaitAndClose(ctx, t)
268
if err = t.Start(ctx); err != nil {
269
return fmt.Errorf("failed to start %q task: %w", "upgrade", err)
272
statusC, err := t.Wait(ctx)
274
return fmt.Errorf("failed waiting for %q task: %w", "upgrade", err)
279
code := status.ExitCode()
281
return fmt.Errorf("task %q failed: exit code %d", "upgrade", code)
288
func OptionsFromUpgradeRequest(r runtime.Runtime, in *machineapi.UpgradeRequest) []Option {
292
WithForce(!in.GetPreserve()),
295
if r.Config() != nil && r.Config().Machine() != nil {
296
opts = append(opts, WithExtraKernelArgs(r.Config().Machine().Install().ExtraKernelArgs()))