blob: 9ada49be51b8f70c975952608ad65090754903ea [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
Lorenz Brunaadeb792023-03-27 15:53:56 +02004package main
5
6import (
Jan Schär4cc3d4d2025-04-14 11:46:47 +00007 "context"
Lorenz Brun54a5a052023-10-02 16:40:11 +02008 _ "embed"
Lorenz Brunaadeb792023-03-27 15:53:56 +02009 "errors"
10 "fmt"
Tim Windelschmidt58321122024-09-10 02:26:03 +020011 "os"
Lorenz Brunaadeb792023-03-27 15:53:56 +020012 "path/filepath"
Jan Schär4cc3d4d2025-04-14 11:46:47 +000013 "time"
Lorenz Brunaadeb792023-03-27 15:53:56 +020014
15 "github.com/cenkalti/backoff/v4"
16 "google.golang.org/protobuf/proto"
17
Tim Windelschmidtb21bdf92025-05-28 18:37:35 +020018 apb "source.monogon.dev/cloud/agent/api"
Jan Schär4cc3d4d2025-04-14 11:46:47 +000019 npb "source.monogon.dev/osbase/net/proto"
20
Jan Schäre19d2792025-06-23 12:37:58 +000021 metropolisInstall "source.monogon.dev/metropolis/installer/install"
Tim Windelschmidt9f21f532024-05-07 15:14:20 +020022 "source.monogon.dev/osbase/blockdev"
23 "source.monogon.dev/osbase/efivarfs"
Jan Schäre19d2792025-06-23 12:37:58 +000024 "source.monogon.dev/osbase/oci/osimage"
Jan Schär4cc3d4d2025-04-14 11:46:47 +000025 "source.monogon.dev/osbase/oci/registry"
Jan Schärc1b6df42025-03-20 08:52:18 +000026 "source.monogon.dev/osbase/structfs"
Jan Schär4cc3d4d2025-04-14 11:46:47 +000027 "source.monogon.dev/osbase/supervisor"
Lorenz Brunaadeb792023-03-27 15:53:56 +020028)
29
Jan Schär2b9a0a02025-07-09 07:54:12 +000030//go:embed metropolis/node/abloader/abloader.efi
Lorenz Brun54a5a052023-10-02 16:40:11 +020031var abloader []byte
32
Lorenz Brunaadeb792023-03-27 15:53:56 +020033// install dispatches OSInstallationRequests to the appropriate installer
34// method
Tim Windelschmidtb21bdf92025-05-28 18:37:35 +020035func install(ctx context.Context, req *apb.OSInstallationRequest, netConfig *npb.Net) error {
Lorenz Brunaadeb792023-03-27 15:53:56 +020036 switch reqT := req.Type.(type) {
Tim Windelschmidtb21bdf92025-05-28 18:37:35 +020037 case *apb.OSInstallationRequest_Metropolis:
Jan Schär4cc3d4d2025-04-14 11:46:47 +000038 return installMetropolis(ctx, reqT.Metropolis, netConfig)
Lorenz Brunaadeb792023-03-27 15:53:56 +020039 default:
40 return errors.New("unknown installation request type")
41 }
42}
43
Tim Windelschmidtb21bdf92025-05-28 18:37:35 +020044func installMetropolis(ctx context.Context, req *apb.MetropolisInstallationRequest, netConfig *npb.Net) error {
Jan Schär4cc3d4d2025-04-14 11:46:47 +000045 l := supervisor.Logger(ctx)
Tim Windelschmidt58321122024-09-10 02:26:03 +020046 // Validate we are running via EFI.
47 if _, err := os.Stat("/sys/firmware/efi"); os.IsNotExist(err) {
Tim Windelschmidt1f51cf42024-10-01 17:04:28 +020048 // nolint:ST1005
Lorenz Brunaadeb792023-03-27 15:53:56 +020049 return errors.New("Monogon OS can only be installed on EFI-booted machines, this one is not")
50 }
Tim Windelschmidtfac48742023-04-24 19:04:55 +020051
52 // Override the NodeParameters.NetworkConfig with the current NetworkConfig
53 // if it's missing.
54 if req.NodeParameters.NetworkConfig == nil {
55 req.NodeParameters.NetworkConfig = netConfig
56 }
57
Jan Schär4cc3d4d2025-04-14 11:46:47 +000058 if req.OsImage == nil {
59 return fmt.Errorf("missing OS image in OS installation request")
Lorenz Brunaadeb792023-03-27 15:53:56 +020060 }
Jan Schär4cc3d4d2025-04-14 11:46:47 +000061 if req.OsImage.Digest == "" {
62 return fmt.Errorf("missing digest in OS installation request")
Lorenz Brunaadeb792023-03-27 15:53:56 +020063 }
Jan Schär4cc3d4d2025-04-14 11:46:47 +000064
65 client := &registry.Client{
66 GetBackOff: func() backoff.BackOff {
67 return backoff.NewExponentialBackOff()
68 },
69 RetryNotify: func(err error, d time.Duration) {
70 l.Warningf("Error while fetching OS image, retrying in %v: %v", d, err)
71 },
72 UserAgent: "Monogon-Cloud-Agent",
73 Scheme: req.OsImage.Scheme,
74 Host: req.OsImage.Host,
75 Repository: req.OsImage.Repository,
Lorenz Brunaadeb792023-03-27 15:53:56 +020076 }
Jan Schär4cc3d4d2025-04-14 11:46:47 +000077
78 image, err := client.Read(ctx, req.OsImage.Tag, req.OsImage.Digest)
Lorenz Brunaadeb792023-03-27 15:53:56 +020079 if err != nil {
Jan Schär4cc3d4d2025-04-14 11:46:47 +000080 return fmt.Errorf("failed to fetch OS image: %w", err)
Lorenz Brunaadeb792023-03-27 15:53:56 +020081 }
Lorenz Brunaadeb792023-03-27 15:53:56 +020082
Jan Schäre19d2792025-06-23 12:37:58 +000083 osImage, err := osimage.Read(image)
Jan Schär4cc3d4d2025-04-14 11:46:47 +000084 if err != nil {
85 return fmt.Errorf("failed to fetch OS image: %w", err)
86 }
87
Jan Schär4cc3d4d2025-04-14 11:46:47 +000088 l.Info("OS image config downloaded")
89
Lorenz Brunaadeb792023-03-27 15:53:56 +020090 nodeParamsRaw, err := proto.Marshal(req.NodeParameters)
91 if err != nil {
92 return fmt.Errorf("failed marshaling: %w", err)
93 }
94
Lorenz Brunad131882023-06-28 16:42:20 +020095 rootDev, err := blockdev.Open(filepath.Join("/dev", req.RootDevice))
96 if err != nil {
97 return fmt.Errorf("failed to open root device: %w", err)
98 }
99
Jan Schäre19d2792025-06-23 12:37:58 +0000100 installParams := metropolisInstall.Params{
101 PartitionSize: metropolisInstall.PartitionSizeInfo{
Lorenz Brun35fcf032023-06-29 04:15:58 +0200102 ESP: 384,
Lorenz Brunaadeb792023-03-27 15:53:56 +0200103 System: 4096,
104 Data: 128,
105 },
Jan Schärdaf9e952025-06-23 13:28:16 +0000106 OSImage: osImage,
Jan Schärc1b6df42025-03-20 08:52:18 +0000107 ABLoader: structfs.Bytes(abloader),
108 NodeParameters: structfs.Bytes(nodeParamsRaw),
Lorenz Brunad131882023-06-28 16:42:20 +0200109 Output: rootDev,
Lorenz Brunaadeb792023-03-27 15:53:56 +0200110 }
111
Jan Schäre19d2792025-06-23 12:37:58 +0000112 be, err := metropolisInstall.Write(&installParams)
Lorenz Brunaadeb792023-03-27 15:53:56 +0200113 if err != nil {
114 return err
115 }
Lorenz Brunca1cff02023-06-26 17:52:44 +0200116 bootEntryIdx, err := efivarfs.AddBootEntry(be)
Lorenz Brunaadeb792023-03-27 15:53:56 +0200117 if err != nil {
118 return fmt.Errorf("error creating EFI boot entry: %w", err)
119 }
Lorenz Brun9933ef02023-07-06 18:28:29 +0200120 if err := efivarfs.SetBootOrder(efivarfs.BootOrder{uint16(bootEntryIdx)}); err != nil {
Lorenz Brunaadeb792023-03-27 15:53:56 +0200121 return fmt.Errorf("error setting EFI boot order: %w", err)
122 }
123 l.Info("Metropolis installation completed")
124 return nil
125}