blob: 9a996b2d0afc44c9c431aa704bd4cb229506d283 [file] [log] [blame]
Lorenz Brune6573102021-11-02 14:15:37 +01001package main
2
3import (
Serge Bazanski97783222021-12-14 16:04:26 +01004 "bytes"
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +02005 "context"
Lorenz Brune6573102021-11-02 14:15:37 +01006 "crypto/ed25519"
Lorenz Brun7a510192022-07-04 15:31:38 +00007 _ "embed"
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +02008 "fmt"
Lorenz Brune6573102021-11-02 14:15:37 +01009 "os"
Tim Windelschmidtb765f242024-05-08 01:40:02 +020010 "os/signal"
Lorenz Brune6573102021-11-02 14:15:37 +010011
Tim Windelschmidt2a1d1b22024-02-06 07:07:42 +010012 "github.com/bazelbuild/rules_go/go/runfiles"
Lorenz Brune6573102021-11-02 14:15:37 +010013 "github.com/spf13/cobra"
Timon Stampflib9701c32024-12-15 17:50:01 +010014 "google.golang.org/protobuf/encoding/prototext"
Lorenz Brune6573102021-11-02 14:15:37 +010015
Tim Windelschmidt2a1d1b22024-02-06 07:07:42 +010016 "source.monogon.dev/metropolis/proto/api"
17 cpb "source.monogon.dev/metropolis/proto/common"
18
Jan Schära9b060b2024-08-07 10:42:29 +020019 "source.monogon.dev/metropolis/cli/flagdefs"
Lorenz Brune6573102021-11-02 14:15:37 +010020 "source.monogon.dev/metropolis/cli/metroctl/core"
Jan Schär39f4f5c2024-10-29 09:41:50 +010021 common "source.monogon.dev/metropolis/node"
Tim Windelschmidt9f21f532024-05-07 15:14:20 +020022 "source.monogon.dev/osbase/blkio"
23 "source.monogon.dev/osbase/fat32"
Lorenz Brune6573102021-11-02 14:15:37 +010024)
25
26var installCmd = &cobra.Command{
Lorenz Brun7a510192022-07-04 15:31:38 +000027 Short: "Contains subcommands to install Metropolis via different media.",
Lorenz Brune6573102021-11-02 14:15:37 +010028 Use: "install",
29}
30
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020031// bootstrap is a flag controlling node parameters included in the installer
32// image. If set, the installed node will bootstrap a new cluster. Otherwise,
33// it will try to connect to the cluster which endpoints were provided with
34// the --endpoints flag.
Tim Windelschmidt7006caf2024-02-27 16:49:39 +010035var bootstrap = installCmd.PersistentFlags().Bool("bootstrap", false, "Create a bootstrap installer image.")
Jan Schära9b060b2024-08-07 10:42:29 +020036var bootstrapTPMMode = flagdefs.TPMModePflag(installCmd.PersistentFlags(), "bootstrap-tpm-mode", cpb.ClusterConfiguration_TPM_MODE_REQUIRED, "TPM mode to set on cluster")
37var bootstrapStorageSecurityPolicy = flagdefs.StorageSecurityPolicyPflag(installCmd.PersistentFlags(), "bootstrap-storage-security", cpb.ClusterConfiguration_STORAGE_SECURITY_POLICY_NEEDS_ENCRYPTION_AND_AUTHENTICATION, "Storage security policy to set on cluster")
Tim Windelschmidt7006caf2024-02-27 16:49:39 +010038var bundlePath = installCmd.PersistentFlags().StringP("bundle", "b", "", "Path to the Metropolis bundle to be installed")
Timon Stampflib9701c32024-12-15 17:50:01 +010039var nodeParamPath = installCmd.PersistentFlags().String("node-params", "", "Path to the metropolis.proto.api.NodeParameters prototext file (advanced usage only)")
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020040
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020041func makeNodeParams() (*api.NodeParameters, error) {
Tim Windelschmidtb765f242024-05-08 01:40:02 +020042 ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020043
Mateusz Zalega8234c162022-07-08 17:05:50 +020044 if err := os.MkdirAll(flags.configPath, 0700); err != nil && !os.IsExist(err) {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020045 return nil, fmt.Errorf("failed to create config directory: %w", err)
Lorenz Brune6573102021-11-02 14:15:37 +010046 }
Lorenz Brune6573102021-11-02 14:15:37 +010047
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020048 var params *api.NodeParameters
Timon Stampflib9701c32024-12-15 17:50:01 +010049 if *nodeParamPath != "" {
50 nodeParamsRaw, err := os.ReadFile(*nodeParamPath)
51 if err != nil {
52 return nil, fmt.Errorf("failed to read node-params file: %w", err)
53 }
54 if err := prototext.Unmarshal(nodeParamsRaw, params); err != nil {
55 return nil, fmt.Errorf("failed to parse node-params: %w", err)
56 }
57 } else {
58 params = &api.NodeParameters{}
59 }
60
Tim Windelschmidt7006caf2024-02-27 16:49:39 +010061 if *bootstrap {
Jan Schär39f4f5c2024-10-29 09:41:50 +010062 if flags.cluster == "" {
63 return nil, fmt.Errorf("when bootstrapping a cluster, the --cluster parameter is required")
64 }
65 if err := common.ValidateClusterDomain(flags.cluster); err != nil {
66 return nil, fmt.Errorf("invalid cluster domain: %w", err)
67 }
68
Tim Windelschmidt7006caf2024-02-27 16:49:39 +010069 // TODO(lorenz): Have a key management story for this
Serge Bazanskicf23ebc2023-03-14 17:02:04 +010070 priv, err := core.GetOrMakeOwnerKey(flags.configPath)
71 if err != nil {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020072 return nil, fmt.Errorf("failed to generate or get owner key: %w", err)
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020073 }
Serge Bazanskicf23ebc2023-03-14 17:02:04 +010074 pub := priv.Public().(ed25519.PublicKey)
Timon Stampflib9701c32024-12-15 17:50:01 +010075 params.Cluster = &api.NodeParameters_ClusterBootstrap_{
76 ClusterBootstrap: &api.NodeParameters_ClusterBootstrap{
77 OwnerPublicKey: pub,
78 InitialClusterConfiguration: &cpb.ClusterConfiguration{
79 ClusterDomain: flags.cluster,
80 StorageSecurityPolicy: *bootstrapStorageSecurityPolicy,
81 TpmMode: *bootstrapTPMMode,
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020082 },
Lorenz Brune6573102021-11-02 14:15:37 +010083 },
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020084 }
85 } else {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020086 cc, err := dialAuthenticated(ctx)
87 if err != nil {
88 return nil, fmt.Errorf("while dialing node: %w", err)
89 }
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020090 mgmt := api.NewManagementClient(cc)
91 resT, err := mgmt.GetRegisterTicket(ctx, &api.GetRegisterTicketRequest{})
92 if err != nil {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020093 return nil, fmt.Errorf("while receiving register ticket: %w", err)
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020094 }
95 resI, err := mgmt.GetClusterInfo(ctx, &api.GetClusterInfoRequest{})
96 if err != nil {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020097 return nil, fmt.Errorf("while receiving cluster directory: %w", err)
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020098 }
99
Timon Stampflib9701c32024-12-15 17:50:01 +0100100 params.Cluster = &api.NodeParameters_ClusterRegister_{
101 ClusterRegister: &api.NodeParameters_ClusterRegister{
102 RegisterTicket: resT.Ticket,
103 ClusterDirectory: resI.ClusterDirectory,
104 CaCertificate: resI.CaCertificate,
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +0200105 },
106 }
Lorenz Brune6573102021-11-02 14:15:37 +0100107 }
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200108 return params, nil
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100109}
Lorenz Brune6573102021-11-02 14:15:37 +0100110
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200111func external(name, datafilePath string, flag *string) (fat32.SizedReader, error) {
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100112 if flag == nil || *flag == "" {
113 rPath, err := runfiles.Rlocation(datafilePath)
114 if err != nil {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200115 return nil, fmt.Errorf("no %s specified", name)
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100116 }
117 df, err := os.ReadFile(rPath)
118 if err != nil {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200119 return nil, fmt.Errorf("can't read file: %w", err)
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100120 }
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200121 return bytes.NewReader(df), nil
Lorenz Brune6573102021-11-02 14:15:37 +0100122 }
123
Tim Windelschmidt9ded9942024-04-08 21:30:17 +0200124 f, err := blkio.NewFileReader(*flag)
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100125 if err != nil {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200126 return nil, fmt.Errorf("failed to open specified %s: %w", name, err)
Lorenz Brune6573102021-11-02 14:15:37 +0100127 }
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100128
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200129 return f, nil
Lorenz Brune6573102021-11-02 14:15:37 +0100130}
131
132func init() {
133 rootCmd.AddCommand(installCmd)
Lorenz Brune6573102021-11-02 14:15:37 +0100134}