blob: abac980b891c6f81e4c2a6d627aa5fd5914437d2 [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"
27 "os/signal"
Jan Schära9b060b2024-08-07 10:42:29 +020028 "strconv"
29 "strings"
30 "time"
Lorenz Brun52f7f292020-06-24 16:42:02 +020031
Jan Schär5abcc7a2024-08-26 13:59:11 +020032 "github.com/bazelbuild/rules_go/go/runfiles"
33
Jan Schära9b060b2024-08-07 10:42:29 +020034 "source.monogon.dev/metropolis/cli/flagdefs"
Serge Bazanski1f8cad72023-03-20 16:58:10 +010035 metroctl "source.monogon.dev/metropolis/cli/metroctl/core"
Jan Schära9b060b2024-08-07 10:42:29 +020036 "source.monogon.dev/metropolis/node"
37 cpb "source.monogon.dev/metropolis/proto/common"
Tim Windelschmidt9f21f532024-05-07 15:14:20 +020038 mlaunch "source.monogon.dev/metropolis/test/launch"
Lorenz Brun52f7f292020-06-24 16:42:02 +020039)
40
Jan Schär5abcc7a2024-08-26 13:59:11 +020041var (
42 // These are filled by bazel at linking time with the canonical path of
43 // their corresponding file. Inside the init function we resolve it
44 // with the rules_go runfiles package to the real path.
45 xMetroctlPath string
46)
47
48func init() {
49 var err error
50 for _, path := range []*string{
51 &xMetroctlPath,
52 } {
53 *path, err = runfiles.Rlocation(*path)
54 if err != nil {
55 panic(err)
56 }
57 }
58}
59
Jan Schära9b060b2024-08-07 10:42:29 +020060const maxNodes = 256
61
62func nodeSetFlag(p *[]int, name string, usage string) {
63 flag.Func(name, usage, func(val string) error {
64 for _, part := range strings.Split(val, ",") {
65 part = strings.TrimSpace(part)
66 if part == "" {
67 continue
68 }
69 startStr, endStr, ok := strings.Cut(part, "-")
70 if !ok {
71 endStr = startStr
72 }
73 start, err := strconv.Atoi(startStr)
74 if err != nil {
75 return err
76 }
77 end, err := strconv.Atoi(endStr)
78 if err != nil {
79 return err
80 }
81 if end >= maxNodes {
82 return fmt.Errorf("node index %v out of range, there can be at most %v nodes", end, maxNodes)
83 }
84 if end < start {
85 return fmt.Errorf("invalid range %q, end is smaller than start", part)
86 }
87 for i := start; i <= end; i++ {
88 *p = append(*p, i)
89 }
90 }
91 return nil
Serge Bazanski075465c2021-11-16 15:38:49 +010092 })
Jan Schära9b060b2024-08-07 10:42:29 +020093}
94
Jan Schär07003572024-08-26 10:42:16 +020095func memoryMiBFlag(p *int, name string, usage string) {
Jan Schära9b060b2024-08-07 10:42:29 +020096 flag.Func(name, usage, func(val string) error {
97 multiplier := 1
98 switch {
99 case strings.HasSuffix(val, "M"):
100 case strings.HasSuffix(val, "G"):
101 multiplier = 1024
102 default:
103 return errors.New("must have suffix M for MiB or G for GiB")
104 }
105 intVal, err := strconv.Atoi(val[:len(val)-1])
106 if err != nil {
107 return err
108 }
109 *p = multiplier * intVal
110 return nil
111 })
112}
113
Jan Schär07003572024-08-26 10:42:16 +0200114func diskBytesFlag(p *uint64, name string, usage string) {
115 flag.Func(name, usage, func(val string) error {
116 var multiplier uint64
117 switch {
118 case strings.HasSuffix(val, "M"):
119 multiplier = 1024 * 1024
120 case strings.HasSuffix(val, "G"):
121 multiplier = 1024 * 1024 * 1024
122 case strings.HasSuffix(val, "T"):
123 multiplier = 1024 * 1024 * 1024 * 1024
124 default:
125 return errors.New("must have suffix M for MiB, G for GiB or T for TiB")
126 }
127 intVal, err := strconv.ParseUint(val[:len(val)-1], 10, 64)
128 if err != nil {
129 return err
130 }
131 *p = multiplier * intVal
132 return nil
133 })
134}
135
Jan Schära9b060b2024-08-07 10:42:29 +0200136func main() {
137 clusterConfig := cpb.ClusterConfiguration{}
138 opts := mlaunch.ClusterOptions{
139 NodeLogsToFiles: true,
140 InitialClusterConfiguration: &clusterConfig,
141 }
142 var consensusMemberList, kubernetesControllerList, kubernetesWorkerList []int
143
144 flag.IntVar(&opts.NumNodes, "num-nodes", 3, "Number of cluster nodes")
145 flagdefs.TPMModeVar(flag.CommandLine, &clusterConfig.TpmMode, "tpm-mode", cpb.ClusterConfiguration_TPM_MODE_REQUIRED, "TPM mode to set on cluster")
146 flagdefs.StorageSecurityPolicyVar(flag.CommandLine, &clusterConfig.StorageSecurityPolicy, "storage-security", cpb.ClusterConfiguration_STORAGE_SECURITY_POLICY_NEEDS_INSECURE, "Storage security policy to set on cluster")
147 flag.IntVar(&opts.Node.CPUs, "cpu", 1, "Number of virtual CPUs of each node")
148 flag.IntVar(&opts.Node.ThreadsPerCPU, "threads-per-cpu", 1, "Number of threads per CPU")
Jan Schär07003572024-08-26 10:42:16 +0200149 memoryMiBFlag(&opts.Node.MemoryMiB, "ram", "RAM size of each node, with suffix M for MiB or G for GiB")
150 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 +0200151 nodeSetFlag(&consensusMemberList, "consensus-member", "List of nodes which get the Consensus Member role. Example: 0,3-5")
152 nodeSetFlag(&kubernetesControllerList, "kubernetes-controller", "List of nodes which get the Kubernetes Controller role. Example: 0,3-5")
153 nodeSetFlag(&kubernetesWorkerList, "kubernetes-worker", "List of nodes which get the Kubernetes Worker role. Example: 0,3-5")
154 flag.Parse()
155
156 if opts.NumNodes >= maxNodes {
157 log.Fatalf("num-nodes (%v) is too large, there can be at most %v nodes", opts.NumNodes, maxNodes)
158 }
159 for _, list := range [][]int{consensusMemberList, kubernetesControllerList, kubernetesWorkerList} {
160 for i := len(list) - 1; i >= 0; i-- {
161 if list[i] >= opts.NumNodes {
162 log.Fatalf("Node index %v out of range, can be at most %v", list[i], opts.NumNodes-1)
163 }
164 }
165 }
166
167 ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
168 cl, err := mlaunch.LaunchCluster(ctx, opts)
Serge Bazanski075465c2021-11-16 15:38:49 +0100169 if err != nil {
170 log.Fatalf("LaunchCluster: %v", err)
171 }
Serge Bazanski1f8cad72023-03-20 16:58:10 +0100172
Jan Schära9b060b2024-08-07 10:42:29 +0200173 for _, node := range consensusMemberList {
174 cl.MakeConsensusMember(ctx, cl.NodeIDs[node])
175 }
176 for _, node := range kubernetesControllerList {
177 cl.MakeKubernetesController(ctx, cl.NodeIDs[node])
178 }
179 for _, node := range kubernetesWorkerList {
180 cl.MakeKubernetesWorker(ctx, cl.NodeIDs[node])
181 }
182
Serge Bazanski1f8cad72023-03-20 16:58:10 +0100183 wpath, err := cl.MakeMetroctlWrapper()
184 if err != nil {
185 log.Fatalf("MakeWrapper: %v", err)
186 }
187
Jan Schära9b060b2024-08-07 10:42:29 +0200188 apiserver := cl.Nodes[cl.NodeIDs[0]].ManagementAddress
189 // Wait for the API server to start listening.
190 for {
191 conn, err := cl.DialNode(ctx, net.JoinHostPort(apiserver, node.KubernetesAPIWrappedPort.PortString()))
192 if err == nil {
193 conn.Close()
194 break
195 }
196 time.Sleep(100 * time.Millisecond)
Serge Bazanski1f8cad72023-03-20 16:58:10 +0100197 }
198
Serge Bazanski6a328e62024-02-12 17:37:50 +0100199 configName := "launch-cluster"
Jan Schär5abcc7a2024-08-26 13:59:11 +0200200 if err := metroctl.InstallKubeletConfig(ctx, xMetroctlPath, cl.ConnectOptions(), configName, apiserver); err != nil {
Serge Bazanski1f8cad72023-03-20 16:58:10 +0100201 log.Fatalf("InstallKubeletConfig: %v", err)
202 }
203
Serge Bazanski075465c2021-11-16 15:38:49 +0100204 log.Printf("Launch: Cluster running!")
Serge Bazanski1f8cad72023-03-20 16:58:10 +0100205 log.Printf(" To access cluster use: metroctl %s", cl.MetroctlFlags())
206 log.Printf(" Or use this handy wrapper: %s", wpath)
207 log.Printf(" To access Kubernetes, use kubectl --context=%s", configName)
Serge Bazanski075465c2021-11-16 15:38:49 +0100208
209 <-ctx.Done()
210 cl.Close()
Lorenz Brun52f7f292020-06-24 16:42:02 +0200211}