blob: 6d26d4c1db20610620e130ef8b6d6d5aa80543ce [file] [log] [blame]
Lorenz Brun6adf8842021-10-05 13:39:11 +02001package main
2
3import (
Serge Bazanski7eeef0f2024-02-05 14:40:15 +01004 "context"
5 "crypto/x509"
Serge Bazanskica8d9512024-09-12 14:20:57 +02006 "os"
Mateusz Zalega8234c162022-07-08 17:05:50 +02007 "path/filepath"
8
9 "github.com/adrg/xdg"
Lorenz Brun6adf8842021-10-05 13:39:11 +020010 "github.com/spf13/cobra"
Serge Bazanski1f8cad72023-03-20 16:58:10 +010011
Serge Bazanskica8d9512024-09-12 14:20:57 +020012 "source.monogon.dev/go/logging"
Serge Bazanski1f8cad72023-03-20 16:58:10 +010013 "source.monogon.dev/metropolis/cli/metroctl/core"
Lorenz Brun6adf8842021-10-05 13:39:11 +020014)
15
16// rootCmd represents the base command when called without any subcommands
17var rootCmd = &cobra.Command{
Tim Windelschmidtfc6e1cf2024-09-18 17:34:07 +020018 Use: "metroctl",
19 Short: "metroctl controls Metropolis nodes and clusters.",
20 SilenceUsage: true,
21 SilenceErrors: true,
Lorenz Brun6adf8842021-10-05 13:39:11 +020022}
23
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020024type metroctlFlags struct {
Jan Schär39f4f5c2024-10-29 09:41:50 +010025 // cluster is the domain name identifying the target cluster.
26 cluster string
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020027 // clusterEndpoints is a list of the targeted cluster's endpoints, used by
28 // commands that perform RPC on it.
29 clusterEndpoints []string
Mateusz Zalegaf7774962022-07-08 12:26:55 +020030 // proxyAddr is a SOCKS5 proxy address the cluster will be accessed through.
31 proxyAddr string
Mateusz Zalega8234c162022-07-08 17:05:50 +020032 // configPath overrides the default XDG config path
33 configPath string
Mateusz Zalegab2cac082022-07-14 14:55:43 +020034 // verbose, if set, will make this utility log additional runtime
35 // information.
36 verbose bool
Mateusz Zalegadb75e212022-08-04 17:31:34 +020037 // format refers to how the output data, except logs, is formatted.
38 format string
39 // filter specifies a CEL filter used to narrow down the set of output
40 // objects.
41 filter string
42 // output is an optional output file path the resulting data will be saved
43 // at. If unspecified, the data will be written to stdout.
44 output string
Serge Bazanski7eeef0f2024-02-05 14:40:15 +010045 // acceptAnyCA will persist the first encountered (while connecting) CA
46 // certificate of the cluster as the trusted CA certificate for this cluster.
47 // This is unsafe and should only be used for testing.
48 acceptAnyCA bool
Serge Bazanski98840342024-05-22 13:03:55 +020049 // columns is a comma-separated list of column names which selects which columns
50 // will be output to the user. An empty string means all columns will be
51 // displayed.
52 columns string
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020053}
54
55var flags metroctlFlags
56
57func init() {
Jan Schär39f4f5c2024-10-29 09:41:50 +010058 rootCmd.PersistentFlags().StringVar(&flags.cluster, "cluster", "", "Cluster domain")
Serge Bazanskicf33f682023-03-17 00:16:16 +010059 rootCmd.PersistentFlags().StringSliceVar(&flags.clusterEndpoints, "endpoints", nil, "A list of the target cluster's endpoints.")
Mateusz Zalegaf7774962022-07-08 12:26:55 +020060 rootCmd.PersistentFlags().StringVar(&flags.proxyAddr, "proxy", "", "SOCKS5 proxy address")
Mateusz Zalega8234c162022-07-08 17:05:50 +020061 rootCmd.PersistentFlags().StringVar(&flags.configPath, "config", filepath.Join(xdg.ConfigHome, "metroctl"), "An alternative cluster config path")
Mateusz Zalegab2cac082022-07-14 14:55:43 +020062 rootCmd.PersistentFlags().BoolVar(&flags.verbose, "verbose", false, "Log additional runtime information")
Serge Bazanskie012b722023-03-29 17:49:04 +020063 rootCmd.PersistentFlags().StringVar(&flags.format, "format", "plaintext", "Data output format")
Mateusz Zalegadb75e212022-08-04 17:31:34 +020064 rootCmd.PersistentFlags().StringVar(&flags.filter, "filter", "", "The object filter applied to the output data")
Serge Bazanski98840342024-05-22 13:03:55 +020065 rootCmd.PersistentFlags().StringVar(&flags.columns, "columns", "", "Comma-separated list of column names to show. If not set, all columns will be shown")
Mateusz Zalegadb75e212022-08-04 17:31:34 +020066 rootCmd.PersistentFlags().StringVarP(&flags.output, "output", "o", "", "Redirects output to the specified file")
Serge Bazanski7eeef0f2024-02-05 14:40:15 +010067 rootCmd.PersistentFlags().BoolVar(&flags.acceptAnyCA, "insecure-accept-and-persist-first-encountered-ca", false, "Accept the first encountered CA while connecting as the trusted CA for future metroctl connections with this config path. This is very insecure and should only be used for testing.")
Tim Windelschmidtfc6e1cf2024-09-18 17:34:07 +020068 rootCmd.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error {
69 cmd.PrintErr(cmd.UsageString())
70 return err
71 })
72}
73
74func PrintUsageOnWrongArgs(pArgs cobra.PositionalArgs) cobra.PositionalArgs {
75 return func(cmd *cobra.Command, args []string) error {
76 err := pArgs(cmd, args)
77 if err != nil {
78 cmd.PrintErr(cmd.UsageString())
79 }
80 return err
81 }
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020082}
83
Lorenz Brun6adf8842021-10-05 13:39:11 +020084func main() {
85 cobra.CheckErr(rootCmd.Execute())
86}
Serge Bazanski1f8cad72023-03-20 16:58:10 +010087
Serge Bazanski7eeef0f2024-02-05 14:40:15 +010088type acceptall struct{}
89
90func (a *acceptall) Ask(ctx context.Context, _ *core.ConnectOptions, _ *x509.Certificate) (bool, error) {
91 return true, nil
92}
93
Serge Bazanski1f8cad72023-03-20 16:58:10 +010094// connectOptions returns core.ConnectOptions as defined by the metroctl flags
95// currently set.
96func connectOptions() *core.ConnectOptions {
Serge Bazanski7eeef0f2024-02-05 14:40:15 +010097 var tofu core.CertificateTOFU
98 if flags.acceptAnyCA {
99 tofu = &acceptall{}
100 }
Serge Bazanskica8d9512024-09-12 14:20:57 +0200101 logger := logging.NewWriterBackend(os.Stderr)
102 if !flags.verbose {
103 logger.MinimumSeverity = logging.WARNING
104 }
Serge Bazanski1f8cad72023-03-20 16:58:10 +0100105 return &core.ConnectOptions{
Serge Bazanski925ec3d2024-02-05 14:38:20 +0100106 ConfigPath: flags.configPath,
107 ProxyServer: flags.proxyAddr,
108 Endpoints: flags.clusterEndpoints,
Serge Bazanskica8d9512024-09-12 14:20:57 +0200109 ResolverLogger: logger,
Serge Bazanski7eeef0f2024-02-05 14:40:15 +0100110 TOFU: tofu,
Serge Bazanski1f8cad72023-03-20 16:58:10 +0100111 }
112}