| Tim Windelschmidt | 6d33a43 | 2025-02-04 14:34:25 +0100 | [diff] [blame] | 1 | // Copyright The Monogon Project Authors. |
| 2 | // SPDX-License-Identifier: Apache-2.0 |
| 3 | |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 4 | package main |
| 5 | |
| 6 | import ( |
| Jan Schär | 4cc3d4d | 2025-04-14 11:46:47 +0000 | [diff] [blame] | 7 | "context" |
| Lorenz Brun | 54a5a05 | 2023-10-02 16:40:11 +0200 | [diff] [blame] | 8 | _ "embed" |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 9 | "errors" |
| 10 | "fmt" |
| Tim Windelschmidt | 5832112 | 2024-09-10 02:26:03 +0200 | [diff] [blame] | 11 | "os" |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 12 | "path/filepath" |
| Jan Schär | 4cc3d4d | 2025-04-14 11:46:47 +0000 | [diff] [blame] | 13 | "time" |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 14 | |
| 15 | "github.com/cenkalti/backoff/v4" |
| 16 | "google.golang.org/protobuf/proto" |
| 17 | |
| Tim Windelschmidt | b21bdf9 | 2025-05-28 18:37:35 +0200 | [diff] [blame] | 18 | apb "source.monogon.dev/cloud/agent/api" |
| Jan Schär | 4cc3d4d | 2025-04-14 11:46:47 +0000 | [diff] [blame] | 19 | npb "source.monogon.dev/osbase/net/proto" |
| 20 | |
| Jan Schär | e19d279 | 2025-06-23 12:37:58 +0000 | [diff] [blame] | 21 | metropolisInstall "source.monogon.dev/metropolis/installer/install" |
| Tim Windelschmidt | 9f21f53 | 2024-05-07 15:14:20 +0200 | [diff] [blame] | 22 | "source.monogon.dev/osbase/blockdev" |
| 23 | "source.monogon.dev/osbase/efivarfs" |
| Jan Schär | 2963b68 | 2025-07-17 17:03:44 +0200 | [diff] [blame^] | 24 | "source.monogon.dev/osbase/oci" |
| Jan Schär | e19d279 | 2025-06-23 12:37:58 +0000 | [diff] [blame] | 25 | "source.monogon.dev/osbase/oci/osimage" |
| Jan Schär | 4cc3d4d | 2025-04-14 11:46:47 +0000 | [diff] [blame] | 26 | "source.monogon.dev/osbase/oci/registry" |
| Jan Schär | c1b6df4 | 2025-03-20 08:52:18 +0000 | [diff] [blame] | 27 | "source.monogon.dev/osbase/structfs" |
| Jan Schär | 4cc3d4d | 2025-04-14 11:46:47 +0000 | [diff] [blame] | 28 | "source.monogon.dev/osbase/supervisor" |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 29 | ) |
| 30 | |
| Jan Schär | 2b9a0a0 | 2025-07-09 07:54:12 +0000 | [diff] [blame] | 31 | //go:embed metropolis/node/abloader/abloader.efi |
| Lorenz Brun | 54a5a05 | 2023-10-02 16:40:11 +0200 | [diff] [blame] | 32 | var abloader []byte |
| 33 | |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 34 | // install dispatches OSInstallationRequests to the appropriate installer |
| 35 | // method |
| Tim Windelschmidt | b21bdf9 | 2025-05-28 18:37:35 +0200 | [diff] [blame] | 36 | func install(ctx context.Context, req *apb.OSInstallationRequest, netConfig *npb.Net) error { |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 37 | switch reqT := req.Type.(type) { |
| Tim Windelschmidt | b21bdf9 | 2025-05-28 18:37:35 +0200 | [diff] [blame] | 38 | case *apb.OSInstallationRequest_Metropolis: |
| Jan Schär | 4cc3d4d | 2025-04-14 11:46:47 +0000 | [diff] [blame] | 39 | return installMetropolis(ctx, reqT.Metropolis, netConfig) |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 40 | default: |
| 41 | return errors.New("unknown installation request type") |
| 42 | } |
| 43 | } |
| 44 | |
| Tim Windelschmidt | b21bdf9 | 2025-05-28 18:37:35 +0200 | [diff] [blame] | 45 | func installMetropolis(ctx context.Context, req *apb.MetropolisInstallationRequest, netConfig *npb.Net) error { |
| Jan Schär | 4cc3d4d | 2025-04-14 11:46:47 +0000 | [diff] [blame] | 46 | l := supervisor.Logger(ctx) |
| Tim Windelschmidt | 5832112 | 2024-09-10 02:26:03 +0200 | [diff] [blame] | 47 | // Validate we are running via EFI. |
| 48 | if _, err := os.Stat("/sys/firmware/efi"); os.IsNotExist(err) { |
| Tim Windelschmidt | 1f51cf4 | 2024-10-01 17:04:28 +0200 | [diff] [blame] | 49 | // nolint:ST1005 |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 50 | return errors.New("Monogon OS can only be installed on EFI-booted machines, this one is not") |
| 51 | } |
| Tim Windelschmidt | fac4874 | 2023-04-24 19:04:55 +0200 | [diff] [blame] | 52 | |
| 53 | // Override the NodeParameters.NetworkConfig with the current NetworkConfig |
| 54 | // if it's missing. |
| 55 | if req.NodeParameters.NetworkConfig == nil { |
| 56 | req.NodeParameters.NetworkConfig = netConfig |
| 57 | } |
| 58 | |
| Jan Schär | 4cc3d4d | 2025-04-14 11:46:47 +0000 | [diff] [blame] | 59 | if req.OsImage == nil { |
| 60 | return fmt.Errorf("missing OS image in OS installation request") |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 61 | } |
| Jan Schär | 4cc3d4d | 2025-04-14 11:46:47 +0000 | [diff] [blame] | 62 | if req.OsImage.Digest == "" { |
| 63 | return fmt.Errorf("missing digest in OS installation request") |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 64 | } |
| Jan Schär | 4cc3d4d | 2025-04-14 11:46:47 +0000 | [diff] [blame] | 65 | |
| 66 | client := ®istry.Client{ |
| 67 | GetBackOff: func() backoff.BackOff { |
| 68 | return backoff.NewExponentialBackOff() |
| 69 | }, |
| 70 | RetryNotify: func(err error, d time.Duration) { |
| 71 | l.Warningf("Error while fetching OS image, retrying in %v: %v", d, err) |
| 72 | }, |
| 73 | UserAgent: "Monogon-Cloud-Agent", |
| 74 | Scheme: req.OsImage.Scheme, |
| 75 | Host: req.OsImage.Host, |
| 76 | Repository: req.OsImage.Repository, |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 77 | } |
| Jan Schär | 4cc3d4d | 2025-04-14 11:46:47 +0000 | [diff] [blame] | 78 | |
| Jan Schär | 2963b68 | 2025-07-17 17:03:44 +0200 | [diff] [blame^] | 79 | image, err := oci.AsImage(client.Read(ctx, req.OsImage.Tag, req.OsImage.Digest)) |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 80 | if err != nil { |
| Jan Schär | 4cc3d4d | 2025-04-14 11:46:47 +0000 | [diff] [blame] | 81 | return fmt.Errorf("failed to fetch OS image: %w", err) |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 82 | } |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 83 | |
| Jan Schär | e19d279 | 2025-06-23 12:37:58 +0000 | [diff] [blame] | 84 | osImage, err := osimage.Read(image) |
| Jan Schär | 4cc3d4d | 2025-04-14 11:46:47 +0000 | [diff] [blame] | 85 | if err != nil { |
| 86 | return fmt.Errorf("failed to fetch OS image: %w", err) |
| 87 | } |
| 88 | |
| Jan Schär | 4cc3d4d | 2025-04-14 11:46:47 +0000 | [diff] [blame] | 89 | l.Info("OS image config downloaded") |
| 90 | |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 91 | nodeParamsRaw, err := proto.Marshal(req.NodeParameters) |
| 92 | if err != nil { |
| 93 | return fmt.Errorf("failed marshaling: %w", err) |
| 94 | } |
| 95 | |
| Lorenz Brun | ad13188 | 2023-06-28 16:42:20 +0200 | [diff] [blame] | 96 | rootDev, err := blockdev.Open(filepath.Join("/dev", req.RootDevice)) |
| 97 | if err != nil { |
| 98 | return fmt.Errorf("failed to open root device: %w", err) |
| 99 | } |
| 100 | |
| Jan Schär | e19d279 | 2025-06-23 12:37:58 +0000 | [diff] [blame] | 101 | installParams := metropolisInstall.Params{ |
| 102 | PartitionSize: metropolisInstall.PartitionSizeInfo{ |
| Lorenz Brun | 35fcf03 | 2023-06-29 04:15:58 +0200 | [diff] [blame] | 103 | ESP: 384, |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 104 | System: 4096, |
| 105 | Data: 128, |
| 106 | }, |
| Jan Schär | daf9e95 | 2025-06-23 13:28:16 +0000 | [diff] [blame] | 107 | OSImage: osImage, |
| Jan Schär | c1b6df4 | 2025-03-20 08:52:18 +0000 | [diff] [blame] | 108 | ABLoader: structfs.Bytes(abloader), |
| 109 | NodeParameters: structfs.Bytes(nodeParamsRaw), |
| Lorenz Brun | ad13188 | 2023-06-28 16:42:20 +0200 | [diff] [blame] | 110 | Output: rootDev, |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 111 | } |
| 112 | |
| Jan Schär | e19d279 | 2025-06-23 12:37:58 +0000 | [diff] [blame] | 113 | be, err := metropolisInstall.Write(&installParams) |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 114 | if err != nil { |
| 115 | return err |
| 116 | } |
| Lorenz Brun | ca1cff0 | 2023-06-26 17:52:44 +0200 | [diff] [blame] | 117 | bootEntryIdx, err := efivarfs.AddBootEntry(be) |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 118 | if err != nil { |
| 119 | return fmt.Errorf("error creating EFI boot entry: %w", err) |
| 120 | } |
| Lorenz Brun | 9933ef0 | 2023-07-06 18:28:29 +0200 | [diff] [blame] | 121 | if err := efivarfs.SetBootOrder(efivarfs.BootOrder{uint16(bootEntryIdx)}); err != nil { |
| Lorenz Brun | aadeb79 | 2023-03-27 15:53:56 +0200 | [diff] [blame] | 122 | return fmt.Errorf("error setting EFI boot order: %w", err) |
| 123 | } |
| 124 | l.Info("Metropolis installation completed") |
| 125 | return nil |
| 126 | } |