blob: 914476950a704a1042553772662ee62191353f56 [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är2963b682025-07-17 17:03:44 +020024 "source.monogon.dev/osbase/oci"
Jan Schäre19d2792025-06-23 12:37:58 +000025 "source.monogon.dev/osbase/oci/osimage"
Jan Schär4cc3d4d2025-04-14 11:46:47 +000026 "source.monogon.dev/osbase/oci/registry"
Jan Schärc1b6df42025-03-20 08:52:18 +000027 "source.monogon.dev/osbase/structfs"
Jan Schär4cc3d4d2025-04-14 11:46:47 +000028 "source.monogon.dev/osbase/supervisor"
Lorenz Brunaadeb792023-03-27 15:53:56 +020029)
30
Jan Schär2b9a0a02025-07-09 07:54:12 +000031//go:embed metropolis/node/abloader/abloader.efi
Lorenz Brun54a5a052023-10-02 16:40:11 +020032var abloader []byte
33
Lorenz Brunaadeb792023-03-27 15:53:56 +020034// install dispatches OSInstallationRequests to the appropriate installer
35// method
Tim Windelschmidtb21bdf92025-05-28 18:37:35 +020036func install(ctx context.Context, req *apb.OSInstallationRequest, netConfig *npb.Net) error {
Lorenz Brunaadeb792023-03-27 15:53:56 +020037 switch reqT := req.Type.(type) {
Tim Windelschmidtb21bdf92025-05-28 18:37:35 +020038 case *apb.OSInstallationRequest_Metropolis:
Jan Schär4cc3d4d2025-04-14 11:46:47 +000039 return installMetropolis(ctx, reqT.Metropolis, netConfig)
Lorenz Brunaadeb792023-03-27 15:53:56 +020040 default:
41 return errors.New("unknown installation request type")
42 }
43}
44
Tim Windelschmidtb21bdf92025-05-28 18:37:35 +020045func installMetropolis(ctx context.Context, req *apb.MetropolisInstallationRequest, netConfig *npb.Net) error {
Jan Schär4cc3d4d2025-04-14 11:46:47 +000046 l := supervisor.Logger(ctx)
Tim Windelschmidt58321122024-09-10 02:26:03 +020047 // Validate we are running via EFI.
48 if _, err := os.Stat("/sys/firmware/efi"); os.IsNotExist(err) {
Tim Windelschmidt1f51cf42024-10-01 17:04:28 +020049 // nolint:ST1005
Lorenz Brunaadeb792023-03-27 15:53:56 +020050 return errors.New("Monogon OS can only be installed on EFI-booted machines, this one is not")
51 }
Tim Windelschmidtfac48742023-04-24 19:04:55 +020052
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är4cc3d4d2025-04-14 11:46:47 +000059 if req.OsImage == nil {
60 return fmt.Errorf("missing OS image in OS installation request")
Lorenz Brunaadeb792023-03-27 15:53:56 +020061 }
Jan Schär4cc3d4d2025-04-14 11:46:47 +000062 if req.OsImage.Digest == "" {
63 return fmt.Errorf("missing digest in OS installation request")
Lorenz Brunaadeb792023-03-27 15:53:56 +020064 }
Jan Schär4cc3d4d2025-04-14 11:46:47 +000065
66 client := &registry.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 Brunaadeb792023-03-27 15:53:56 +020077 }
Jan Schär4cc3d4d2025-04-14 11:46:47 +000078
Jan Schär2963b682025-07-17 17:03:44 +020079 image, err := oci.AsImage(client.Read(ctx, req.OsImage.Tag, req.OsImage.Digest))
Lorenz Brunaadeb792023-03-27 15:53:56 +020080 if err != nil {
Jan Schär4cc3d4d2025-04-14 11:46:47 +000081 return fmt.Errorf("failed to fetch OS image: %w", err)
Lorenz Brunaadeb792023-03-27 15:53:56 +020082 }
Lorenz Brunaadeb792023-03-27 15:53:56 +020083
Jan Schäre19d2792025-06-23 12:37:58 +000084 osImage, err := osimage.Read(image)
Jan Schär4cc3d4d2025-04-14 11:46:47 +000085 if err != nil {
86 return fmt.Errorf("failed to fetch OS image: %w", err)
87 }
88
Jan Schär4cc3d4d2025-04-14 11:46:47 +000089 l.Info("OS image config downloaded")
90
Lorenz Brunaadeb792023-03-27 15:53:56 +020091 nodeParamsRaw, err := proto.Marshal(req.NodeParameters)
92 if err != nil {
93 return fmt.Errorf("failed marshaling: %w", err)
94 }
95
Lorenz Brunad131882023-06-28 16:42:20 +020096 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äre19d2792025-06-23 12:37:58 +0000101 installParams := metropolisInstall.Params{
102 PartitionSize: metropolisInstall.PartitionSizeInfo{
Lorenz Brun35fcf032023-06-29 04:15:58 +0200103 ESP: 384,
Lorenz Brunaadeb792023-03-27 15:53:56 +0200104 System: 4096,
105 Data: 128,
106 },
Jan Schärdaf9e952025-06-23 13:28:16 +0000107 OSImage: osImage,
Jan Schärc1b6df42025-03-20 08:52:18 +0000108 ABLoader: structfs.Bytes(abloader),
109 NodeParameters: structfs.Bytes(nodeParamsRaw),
Lorenz Brunad131882023-06-28 16:42:20 +0200110 Output: rootDev,
Lorenz Brunaadeb792023-03-27 15:53:56 +0200111 }
112
Jan Schäre19d2792025-06-23 12:37:58 +0000113 be, err := metropolisInstall.Write(&installParams)
Lorenz Brunaadeb792023-03-27 15:53:56 +0200114 if err != nil {
115 return err
116 }
Lorenz Brunca1cff02023-06-26 17:52:44 +0200117 bootEntryIdx, err := efivarfs.AddBootEntry(be)
Lorenz Brunaadeb792023-03-27 15:53:56 +0200118 if err != nil {
119 return fmt.Errorf("error creating EFI boot entry: %w", err)
120 }
Lorenz Brun9933ef02023-07-06 18:28:29 +0200121 if err := efivarfs.SetBootOrder(efivarfs.BootOrder{uint16(bootEntryIdx)}); err != nil {
Lorenz Brunaadeb792023-03-27 15:53:56 +0200122 return fmt.Errorf("error setting EFI boot order: %w", err)
123 }
124 l.Info("Metropolis installation completed")
125 return nil
126}