Serge Bazanski | 1f8cad7 | 2023-03-20 16:58:10 +0100 | [diff] [blame] | 1 | package cluster |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | "net" |
| 6 | "os" |
| 7 | "path" |
| 8 | "sort" |
| 9 | |
| 10 | "github.com/kballard/go-shellquote" |
| 11 | |
| 12 | metroctl "source.monogon.dev/metropolis/cli/metroctl/core" |
| 13 | "source.monogon.dev/metropolis/cli/pkg/datafile" |
| 14 | ) |
| 15 | |
| 16 | const metroctlRunfile = "metropolis/cli/metroctl/metroctl_/metroctl" |
| 17 | |
| 18 | // MetroctlRunfilePath returns the absolute path to the metroctl binary available |
| 19 | // if the built target depends on //metropolis/cli/metroctl. Otherwise, an error |
| 20 | // is returned. |
| 21 | func MetroctlRunfilePath() (string, error) { |
| 22 | path, err := datafile.ResolveRunfile(metroctlRunfile) |
| 23 | if err != nil { |
| 24 | return "", fmt.Errorf("//metropolis/cli/metroctl not found in runfiles, did you include it as a data dependency? error: %w", err) |
| 25 | } |
| 26 | return path, nil |
| 27 | } |
| 28 | |
| 29 | // ConnectOptions returns metroctl.ConnectOptions that describe connectivity to |
| 30 | // the launched cluster. |
| 31 | func (c *Cluster) ConnectOptions() *metroctl.ConnectOptions { |
| 32 | // Use all metropolis nodes as endpoints. That's fine, metroctl's resolver will |
| 33 | // figure out what to actually use. |
| 34 | var endpoints []string |
| 35 | for _, n := range c.Nodes { |
| 36 | endpoints = append(endpoints, n.ManagementAddress) |
| 37 | } |
| 38 | sort.Strings(endpoints) |
| 39 | return &metroctl.ConnectOptions{ |
| 40 | ConfigPath: c.metroctlDir, |
| 41 | ProxyServer: net.JoinHostPort("127.0.0.1", fmt.Sprintf("%d", c.Ports[SOCKSPort])), |
| 42 | Endpoints: endpoints, |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | // MetroctlFlags return stringified flags to pass to a metroctl binary to connect |
| 47 | // to the launched cluster. |
| 48 | func (c *Cluster) MetroctlFlags() string { |
| 49 | return shellquote.Join(c.ConnectOptions().ToFlags()...) |
| 50 | } |
| 51 | |
| 52 | // MakeMetroctlWrapper builds and returns the path to a shell script which calls |
| 53 | // metroctl (from //metropolis/cli/metroctl, which must be included as a data |
| 54 | // dependency of the built target) with all the required flags to connect to the |
| 55 | // launched cluster. |
| 56 | func (c *Cluster) MakeMetroctlWrapper() (string, error) { |
| 57 | mpath, err := MetroctlRunfilePath() |
| 58 | if err != nil { |
| 59 | return "", err |
| 60 | } |
| 61 | wpath := path.Join(c.metroctlDir, "metroctl.sh") |
| 62 | |
| 63 | // Don't create wrapper if it already exists. |
| 64 | if _, err := os.Stat(wpath); err == nil { |
| 65 | return wpath, nil |
| 66 | } |
| 67 | |
| 68 | wrapper := fmt.Sprintf("#!/usr/bin/env bash\nexec %s %s \"$@\"", mpath, c.MetroctlFlags()) |
| 69 | if err := os.WriteFile(wpath, []byte(wrapper), 0555); err != nil { |
| 70 | return "", fmt.Errorf("could not write wrapper: %w", err) |
| 71 | } |
| 72 | return wpath, nil |
| 73 | } |