blob: e985a64e0ccb60d58fb0e67c5d1eefd36baf3f9d [file] [log] [blame]
Serge Bazanski1f8cad72023-03-20 16:58:10 +01001package cluster
2
3import (
Serge Bazanski7eeef0f2024-02-05 14:40:15 +01004 "context"
5 "crypto/x509"
Serge Bazanski1f8cad72023-03-20 16:58:10 +01006 "fmt"
7 "net"
8 "os"
9 "path"
10 "sort"
11
Tim Windelschmidt2a1d1b22024-02-06 07:07:42 +010012 "github.com/bazelbuild/rules_go/go/runfiles"
Serge Bazanski1f8cad72023-03-20 16:58:10 +010013 "github.com/kballard/go-shellquote"
14
15 metroctl "source.monogon.dev/metropolis/cli/metroctl/core"
Serge Bazanski1f8cad72023-03-20 16:58:10 +010016)
17
Tim Windelschmidt68cf38a2024-03-07 15:52:28 +010018const metroctlRunfile = "_main/metropolis/cli/metroctl/metroctl_/metroctl"
Serge Bazanski1f8cad72023-03-20 16:58:10 +010019
20// MetroctlRunfilePath returns the absolute path to the metroctl binary available
21// if the built target depends on //metropolis/cli/metroctl. Otherwise, an error
22// is returned.
23func MetroctlRunfilePath() (string, error) {
Tim Windelschmidt2a1d1b22024-02-06 07:07:42 +010024 path, err := runfiles.Rlocation(metroctlRunfile)
Serge Bazanski1f8cad72023-03-20 16:58:10 +010025 if err != nil {
26 return "", fmt.Errorf("//metropolis/cli/metroctl not found in runfiles, did you include it as a data dependency? error: %w", err)
27 }
28 return path, nil
29}
30
Serge Bazanski7eeef0f2024-02-05 14:40:15 +010031type acceptall struct{}
32
33func (a *acceptall) Ask(ctx context.Context, _ *metroctl.ConnectOptions, _ *x509.Certificate) (bool, error) {
34 return true, nil
35}
36
Serge Bazanski1f8cad72023-03-20 16:58:10 +010037// ConnectOptions returns metroctl.ConnectOptions that describe connectivity to
38// the launched cluster.
39func (c *Cluster) ConnectOptions() *metroctl.ConnectOptions {
40 // Use all metropolis nodes as endpoints. That's fine, metroctl's resolver will
41 // figure out what to actually use.
42 var endpoints []string
43 for _, n := range c.Nodes {
44 endpoints = append(endpoints, n.ManagementAddress)
45 }
46 sort.Strings(endpoints)
47 return &metroctl.ConnectOptions{
48 ConfigPath: c.metroctlDir,
49 ProxyServer: net.JoinHostPort("127.0.0.1", fmt.Sprintf("%d", c.Ports[SOCKSPort])),
50 Endpoints: endpoints,
Serge Bazanski7eeef0f2024-02-05 14:40:15 +010051 TOFU: &acceptall{},
Serge Bazanski1f8cad72023-03-20 16:58:10 +010052 }
53}
54
55// MetroctlFlags return stringified flags to pass to a metroctl binary to connect
56// to the launched cluster.
57func (c *Cluster) MetroctlFlags() string {
58 return shellquote.Join(c.ConnectOptions().ToFlags()...)
59}
60
61// MakeMetroctlWrapper builds and returns the path to a shell script which calls
62// metroctl (from //metropolis/cli/metroctl, which must be included as a data
63// dependency of the built target) with all the required flags to connect to the
64// launched cluster.
65func (c *Cluster) MakeMetroctlWrapper() (string, error) {
66 mpath, err := MetroctlRunfilePath()
67 if err != nil {
68 return "", err
69 }
70 wpath := path.Join(c.metroctlDir, "metroctl.sh")
71
72 // Don't create wrapper if it already exists.
73 if _, err := os.Stat(wpath); err == nil {
74 return wpath, nil
75 }
76
77 wrapper := fmt.Sprintf("#!/usr/bin/env bash\nexec %s %s \"$@\"", mpath, c.MetroctlFlags())
78 if err := os.WriteFile(wpath, []byte(wrapper), 0555); err != nil {
79 return "", fmt.Errorf("could not write wrapper: %w", err)
80 }
81 return wpath, nil
82}