blob: 3aeb3198ef3f56be2a397ed42b854f667612c4cb [file] [log] [blame]
Lorenz Brun52f7f292020-06-24 16:42:02 +02001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package main
18
19import (
Serge Bazanski075465c2021-11-16 15:38:49 +010020 "context"
Jan Schära9b060b2024-08-07 10:42:29 +020021 "errors"
22 "flag"
23 "fmt"
Lorenz Brun52f7f292020-06-24 16:42:02 +020024 "log"
Jan Schära9b060b2024-08-07 10:42:29 +020025 "net"
Tim Windelschmidtb765f242024-05-08 01:40:02 +020026 "os"
Tim Windelschmidt82e6af72024-07-23 00:05:42 +000027 "os/exec"
Tim Windelschmidtb765f242024-05-08 01:40:02 +020028 "os/signal"
Jan Schära9b060b2024-08-07 10:42:29 +020029 "strconv"
30 "strings"
31 "time"
Lorenz Brun52f7f292020-06-24 16:42:02 +020032
Jan Schära9b060b2024-08-07 10:42:29 +020033 "source.monogon.dev/metropolis/cli/flagdefs"
Serge Bazanski1f8cad72023-03-20 16:58:10 +010034 metroctl "source.monogon.dev/metropolis/cli/metroctl/core"
Jan Schära9b060b2024-08-07 10:42:29 +020035 "source.monogon.dev/metropolis/node"
36 cpb "source.monogon.dev/metropolis/proto/common"
Tim Windelschmidt9f21f532024-05-07 15:14:20 +020037 mlaunch "source.monogon.dev/metropolis/test/launch"
Lorenz Brun52f7f292020-06-24 16:42:02 +020038)
39
Jan Schära9b060b2024-08-07 10:42:29 +020040const maxNodes = 256
41
42func nodeSetFlag(p *[]int, name string, usage string) {
43 flag.Func(name, usage, func(val string) error {
44 for _, part := range strings.Split(val, ",") {
45 part = strings.TrimSpace(part)
46 if part == "" {
47 continue
48 }
49 startStr, endStr, ok := strings.Cut(part, "-")
50 if !ok {
51 endStr = startStr
52 }
53 start, err := strconv.Atoi(startStr)
54 if err != nil {
55 return err
56 }
57 end, err := strconv.Atoi(endStr)
58 if err != nil {
59 return err
60 }
61 if end >= maxNodes {
62 return fmt.Errorf("node index %v out of range, there can be at most %v nodes", end, maxNodes)
63 }
64 if end < start {
65 return fmt.Errorf("invalid range %q, end is smaller than start", part)
66 }
67 for i := start; i <= end; i++ {
68 *p = append(*p, i)
69 }
70 }
71 return nil
Serge Bazanski075465c2021-11-16 15:38:49 +010072 })
Jan Schära9b060b2024-08-07 10:42:29 +020073}
74
Jan Schär07003572024-08-26 10:42:16 +020075func memoryMiBFlag(p *int, name string, usage string) {
Jan Schära9b060b2024-08-07 10:42:29 +020076 flag.Func(name, usage, func(val string) error {
77 multiplier := 1
78 switch {
79 case strings.HasSuffix(val, "M"):
80 case strings.HasSuffix(val, "G"):
81 multiplier = 1024
82 default:
83 return errors.New("must have suffix M for MiB or G for GiB")
84 }
85 intVal, err := strconv.Atoi(val[:len(val)-1])
86 if err != nil {
87 return err
88 }
89 *p = multiplier * intVal
90 return nil
91 })
92}
93
Jan Schär07003572024-08-26 10:42:16 +020094func diskBytesFlag(p *uint64, name string, usage string) {
95 flag.Func(name, usage, func(val string) error {
96 var multiplier uint64
97 switch {
98 case strings.HasSuffix(val, "M"):
99 multiplier = 1024 * 1024
100 case strings.HasSuffix(val, "G"):
101 multiplier = 1024 * 1024 * 1024
102 case strings.HasSuffix(val, "T"):
103 multiplier = 1024 * 1024 * 1024 * 1024
104 default:
105 return errors.New("must have suffix M for MiB, G for GiB or T for TiB")
106 }
107 intVal, err := strconv.ParseUint(val[:len(val)-1], 10, 64)
108 if err != nil {
109 return err
110 }
111 *p = multiplier * intVal
112 return nil
113 })
114}
115
Jan Schära9b060b2024-08-07 10:42:29 +0200116func main() {
117 clusterConfig := cpb.ClusterConfiguration{}
118 opts := mlaunch.ClusterOptions{
119 NodeLogsToFiles: true,
120 InitialClusterConfiguration: &clusterConfig,
121 }
122 var consensusMemberList, kubernetesControllerList, kubernetesWorkerList []int
123
124 flag.IntVar(&opts.NumNodes, "num-nodes", 3, "Number of cluster nodes")
125 flagdefs.TPMModeVar(flag.CommandLine, &clusterConfig.TpmMode, "tpm-mode", cpb.ClusterConfiguration_TPM_MODE_REQUIRED, "TPM mode to set on cluster")
126 flagdefs.StorageSecurityPolicyVar(flag.CommandLine, &clusterConfig.StorageSecurityPolicy, "storage-security", cpb.ClusterConfiguration_STORAGE_SECURITY_POLICY_NEEDS_INSECURE, "Storage security policy to set on cluster")
127 flag.IntVar(&opts.Node.CPUs, "cpu", 1, "Number of virtual CPUs of each node")
128 flag.IntVar(&opts.Node.ThreadsPerCPU, "threads-per-cpu", 1, "Number of threads per CPU")
Jan Schär07003572024-08-26 10:42:16 +0200129 memoryMiBFlag(&opts.Node.MemoryMiB, "ram", "RAM size of each node, with suffix M for MiB or G for GiB")
130 diskBytesFlag(&opts.Node.DiskBytes, "disk", "Disk size of each node, with suffix M for MiB, G for GiB or T for TiB")
Jan Schära9b060b2024-08-07 10:42:29 +0200131 nodeSetFlag(&consensusMemberList, "consensus-member", "List of nodes which get the Consensus Member role. Example: 0,3-5")
132 nodeSetFlag(&kubernetesControllerList, "kubernetes-controller", "List of nodes which get the Kubernetes Controller role. Example: 0,3-5")
133 nodeSetFlag(&kubernetesWorkerList, "kubernetes-worker", "List of nodes which get the Kubernetes Worker role. Example: 0,3-5")
134 flag.Parse()
135
136 if opts.NumNodes >= maxNodes {
137 log.Fatalf("num-nodes (%v) is too large, there can be at most %v nodes", opts.NumNodes, maxNodes)
138 }
139 for _, list := range [][]int{consensusMemberList, kubernetesControllerList, kubernetesWorkerList} {
140 for i := len(list) - 1; i >= 0; i-- {
141 if list[i] >= opts.NumNodes {
142 log.Fatalf("Node index %v out of range, can be at most %v", list[i], opts.NumNodes-1)
143 }
144 }
145 }
146
147 ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
148 cl, err := mlaunch.LaunchCluster(ctx, opts)
Serge Bazanski075465c2021-11-16 15:38:49 +0100149 if err != nil {
150 log.Fatalf("LaunchCluster: %v", err)
151 }
Serge Bazanski1f8cad72023-03-20 16:58:10 +0100152
Jan Schära9b060b2024-08-07 10:42:29 +0200153 for _, node := range consensusMemberList {
154 cl.MakeConsensusMember(ctx, cl.NodeIDs[node])
155 }
156 for _, node := range kubernetesControllerList {
157 cl.MakeKubernetesController(ctx, cl.NodeIDs[node])
158 }
159 for _, node := range kubernetesWorkerList {
160 cl.MakeKubernetesWorker(ctx, cl.NodeIDs[node])
161 }
162
Serge Bazanski1f8cad72023-03-20 16:58:10 +0100163 wpath, err := cl.MakeMetroctlWrapper()
164 if err != nil {
165 log.Fatalf("MakeWrapper: %v", err)
166 }
167
Jan Schära9b060b2024-08-07 10:42:29 +0200168 apiserver := cl.Nodes[cl.NodeIDs[0]].ManagementAddress
169 // Wait for the API server to start listening.
170 for {
171 conn, err := cl.DialNode(ctx, net.JoinHostPort(apiserver, node.KubernetesAPIWrappedPort.PortString()))
172 if err == nil {
173 conn.Close()
174 break
175 }
176 time.Sleep(100 * time.Millisecond)
Serge Bazanski1f8cad72023-03-20 16:58:10 +0100177 }
178
Tim Windelschmidt82e6af72024-07-23 00:05:42 +0000179 // If the user has metroctl in their path, use the metroctl from path as
180 // a credential plugin. Otherwise use the path to the currently-running
181 // metroctl.
182 metroctlPath := "metroctl"
183 if _, err := exec.LookPath("metroctl"); err != nil {
184 metroctlPath, err = os.Executable()
185 if err != nil {
186 log.Fatalf("Failed to create kubectl entry as metroctl is neither in PATH nor can its absolute path be determined: %v", err)
187 }
188 }
189
Serge Bazanski6a328e62024-02-12 17:37:50 +0100190 configName := "launch-cluster"
Jan Schära9b060b2024-08-07 10:42:29 +0200191 if err := metroctl.InstallKubeletConfig(ctx, metroctlPath, cl.ConnectOptions(), configName, apiserver); err != nil {
Serge Bazanski1f8cad72023-03-20 16:58:10 +0100192 log.Fatalf("InstallKubeletConfig: %v", err)
193 }
194
Serge Bazanski075465c2021-11-16 15:38:49 +0100195 log.Printf("Launch: Cluster running!")
Serge Bazanski1f8cad72023-03-20 16:58:10 +0100196 log.Printf(" To access cluster use: metroctl %s", cl.MetroctlFlags())
197 log.Printf(" Or use this handy wrapper: %s", wpath)
198 log.Printf(" To access Kubernetes, use kubectl --context=%s", configName)
Serge Bazanski075465c2021-11-16 15:38:49 +0100199
200 <-ctx.Done()
201 cl.Close()
Lorenz Brun52f7f292020-06-24 16:42:02 +0200202}