blob: cceebd1dc5a6f84f54da5f2b3a3d1651eef7f13f [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
75func sizeFlagMiB(p *int, name string, usage string) {
76 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
94func main() {
95 clusterConfig := cpb.ClusterConfiguration{}
96 opts := mlaunch.ClusterOptions{
97 NodeLogsToFiles: true,
98 InitialClusterConfiguration: &clusterConfig,
99 }
100 var consensusMemberList, kubernetesControllerList, kubernetesWorkerList []int
101
102 flag.IntVar(&opts.NumNodes, "num-nodes", 3, "Number of cluster nodes")
103 flagdefs.TPMModeVar(flag.CommandLine, &clusterConfig.TpmMode, "tpm-mode", cpb.ClusterConfiguration_TPM_MODE_REQUIRED, "TPM mode to set on cluster")
104 flagdefs.StorageSecurityPolicyVar(flag.CommandLine, &clusterConfig.StorageSecurityPolicy, "storage-security", cpb.ClusterConfiguration_STORAGE_SECURITY_POLICY_NEEDS_INSECURE, "Storage security policy to set on cluster")
105 flag.IntVar(&opts.Node.CPUs, "cpu", 1, "Number of virtual CPUs of each node")
106 flag.IntVar(&opts.Node.ThreadsPerCPU, "threads-per-cpu", 1, "Number of threads per CPU")
107 sizeFlagMiB(&opts.Node.MemoryMiB, "ram", "RAM size of each node, with suffix M for MiB or G for GiB")
108 nodeSetFlag(&consensusMemberList, "consensus-member", "List of nodes which get the Consensus Member role. Example: 0,3-5")
109 nodeSetFlag(&kubernetesControllerList, "kubernetes-controller", "List of nodes which get the Kubernetes Controller role. Example: 0,3-5")
110 nodeSetFlag(&kubernetesWorkerList, "kubernetes-worker", "List of nodes which get the Kubernetes Worker role. Example: 0,3-5")
111 flag.Parse()
112
113 if opts.NumNodes >= maxNodes {
114 log.Fatalf("num-nodes (%v) is too large, there can be at most %v nodes", opts.NumNodes, maxNodes)
115 }
116 for _, list := range [][]int{consensusMemberList, kubernetesControllerList, kubernetesWorkerList} {
117 for i := len(list) - 1; i >= 0; i-- {
118 if list[i] >= opts.NumNodes {
119 log.Fatalf("Node index %v out of range, can be at most %v", list[i], opts.NumNodes-1)
120 }
121 }
122 }
123
124 ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
125 cl, err := mlaunch.LaunchCluster(ctx, opts)
Serge Bazanski075465c2021-11-16 15:38:49 +0100126 if err != nil {
127 log.Fatalf("LaunchCluster: %v", err)
128 }
Serge Bazanski1f8cad72023-03-20 16:58:10 +0100129
Jan Schära9b060b2024-08-07 10:42:29 +0200130 for _, node := range consensusMemberList {
131 cl.MakeConsensusMember(ctx, cl.NodeIDs[node])
132 }
133 for _, node := range kubernetesControllerList {
134 cl.MakeKubernetesController(ctx, cl.NodeIDs[node])
135 }
136 for _, node := range kubernetesWorkerList {
137 cl.MakeKubernetesWorker(ctx, cl.NodeIDs[node])
138 }
139
Serge Bazanski1f8cad72023-03-20 16:58:10 +0100140 wpath, err := cl.MakeMetroctlWrapper()
141 if err != nil {
142 log.Fatalf("MakeWrapper: %v", err)
143 }
144
Jan Schära9b060b2024-08-07 10:42:29 +0200145 apiserver := cl.Nodes[cl.NodeIDs[0]].ManagementAddress
146 // Wait for the API server to start listening.
147 for {
148 conn, err := cl.DialNode(ctx, net.JoinHostPort(apiserver, node.KubernetesAPIWrappedPort.PortString()))
149 if err == nil {
150 conn.Close()
151 break
152 }
153 time.Sleep(100 * time.Millisecond)
Serge Bazanski1f8cad72023-03-20 16:58:10 +0100154 }
155
Tim Windelschmidt82e6af72024-07-23 00:05:42 +0000156 // If the user has metroctl in their path, use the metroctl from path as
157 // a credential plugin. Otherwise use the path to the currently-running
158 // metroctl.
159 metroctlPath := "metroctl"
160 if _, err := exec.LookPath("metroctl"); err != nil {
161 metroctlPath, err = os.Executable()
162 if err != nil {
163 log.Fatalf("Failed to create kubectl entry as metroctl is neither in PATH nor can its absolute path be determined: %v", err)
164 }
165 }
166
Serge Bazanski6a328e62024-02-12 17:37:50 +0100167 configName := "launch-cluster"
Jan Schära9b060b2024-08-07 10:42:29 +0200168 if err := metroctl.InstallKubeletConfig(ctx, metroctlPath, cl.ConnectOptions(), configName, apiserver); err != nil {
Serge Bazanski1f8cad72023-03-20 16:58:10 +0100169 log.Fatalf("InstallKubeletConfig: %v", err)
170 }
171
Serge Bazanski075465c2021-11-16 15:38:49 +0100172 log.Printf("Launch: Cluster running!")
Serge Bazanski1f8cad72023-03-20 16:58:10 +0100173 log.Printf(" To access cluster use: metroctl %s", cl.MetroctlFlags())
174 log.Printf(" Or use this handy wrapper: %s", wpath)
175 log.Printf(" To access Kubernetes, use kubectl --context=%s", configName)
Serge Bazanski075465c2021-11-16 15:38:49 +0100176
177 <-ctx.Done()
178 cl.Close()
Lorenz Brun52f7f292020-06-24 16:42:02 +0200179}