blob: b5731c69c6f0260f187375ebe6f54f33935f9d6f [file] [log] [blame]
// Copyright The Monogon Project Authors.
// SPDX-License-Identifier: Apache-2.0
package main
import (
"context"
"crypto/x509"
"os"
"path/filepath"
"github.com/adrg/xdg"
"github.com/spf13/cobra"
"source.monogon.dev/go/logging"
"source.monogon.dev/metropolis/cli/metroctl/core"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "metroctl",
Short: "metroctl controls Metropolis nodes and clusters.",
SilenceUsage: true,
SilenceErrors: true,
}
type metroctlFlags struct {
// cluster is the domain name identifying the target cluster.
cluster string
// clusterEndpoints is a list of the targeted cluster's endpoints, used by
// commands that perform RPC on it.
clusterEndpoints []string
// proxyAddr is a SOCKS5 proxy address the cluster will be accessed through.
proxyAddr string
// configPath overrides the default XDG config path
configPath string
// verbose, if set, will make this utility log additional runtime
// information.
verbose bool
// format refers to how the output data, except logs, is formatted.
format string
// filter specifies a CEL filter used to narrow down the set of output
// objects.
filter string
// output is an optional output file path the resulting data will be saved
// at. If unspecified, the data will be written to stdout.
output string
// acceptAnyCA will persist the first encountered (while connecting) CA
// certificate of the cluster as the trusted CA certificate for this cluster.
// This is unsafe and should only be used for testing.
acceptAnyCA bool
// columns is a comma-separated list of column names which selects which columns
// will be output to the user. An empty string means all columns will be
// displayed.
columns string
}
var flags metroctlFlags
func init() {
rootCmd.PersistentFlags().StringVar(&flags.cluster, "cluster", "", "Cluster domain")
rootCmd.PersistentFlags().StringSliceVar(&flags.clusterEndpoints, "endpoints", nil, "A list of the target cluster's endpoints.")
rootCmd.PersistentFlags().StringVar(&flags.proxyAddr, "proxy", "", "SOCKS5 proxy address")
rootCmd.PersistentFlags().StringVar(&flags.configPath, "config", filepath.Join(xdg.ConfigHome, "metroctl"), "An alternative cluster config path")
rootCmd.PersistentFlags().BoolVar(&flags.verbose, "verbose", false, "Log additional runtime information")
rootCmd.PersistentFlags().StringVar(&flags.format, "format", "plaintext", "Data output format")
rootCmd.PersistentFlags().StringVar(&flags.filter, "filter", "", "The object filter applied to the output data")
rootCmd.PersistentFlags().StringVar(&flags.columns, "columns", "", "Comma-separated list of column names to show. If not set, all columns will be shown")
rootCmd.PersistentFlags().StringVarP(&flags.output, "output", "o", "", "Redirects output to the specified file")
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.")
rootCmd.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error {
cmd.PrintErr(cmd.UsageString())
return err
})
}
func PrintUsageOnWrongArgs(pArgs cobra.PositionalArgs) cobra.PositionalArgs {
return func(cmd *cobra.Command, args []string) error {
err := pArgs(cmd, args)
if err != nil {
cmd.PrintErr(cmd.UsageString())
}
return err
}
}
func main() {
cobra.CheckErr(rootCmd.Execute())
}
type acceptall struct{}
func (a *acceptall) Ask(ctx context.Context, _ *core.ConnectOptions, _ *x509.Certificate) (bool, error) {
return true, nil
}
// connectOptions returns core.ConnectOptions as defined by the metroctl flags
// currently set.
func connectOptions() *core.ConnectOptions {
var tofu core.CertificateTOFU
if flags.acceptAnyCA {
tofu = &acceptall{}
}
logger := logging.NewWriterBackend(os.Stderr)
if !flags.verbose {
logger.MinimumSeverity = logging.WARNING
}
return &core.ConnectOptions{
ConfigPath: flags.configPath,
ProxyServer: flags.proxyAddr,
Endpoints: flags.clusterEndpoints,
ResolverLogger: logger,
TOFU: tofu,
}
}