blob: 2439c9531fb4d339394f8b749c0bd4fafab460e6 [file] [log] [blame]
// Copyright The Monogon Project Authors.
// SPDX-License-Identifier: Apache-2.0
package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"regexp"
"strings"
"github.com/spf13/cobra"
"google.golang.org/protobuf/types/known/fieldmaskpb"
apb "source.monogon.dev/metropolis/proto/api"
cpb "source.monogon.dev/metropolis/proto/common"
)
type configurableClusterKey struct {
key string
description string
set func(value []string) (*apb.ConfigureClusterRequest, error)
get func(c *cpb.ClusterConfiguration) (string, error)
}
var configurableClusterKeys = []configurableClusterKey{
{
key: "kubernetes.node_labels_to_synchronize",
description: "list of label regexes to sync from Metropolis to Kubernetes nodes",
set: func(value []string) (*apb.ConfigureClusterRequest, error) {
res := &apb.ConfigureClusterRequest{
NewConfig: &cpb.ClusterConfiguration{
Kubernetes: &cpb.ClusterConfiguration_Kubernetes{},
},
UpdateMask: &fieldmaskpb.FieldMask{
Paths: []string{"kubernetes.node_labels_to_synchronize"},
},
}
for _, v := range value {
_, err := regexp.Compile(v)
if err != nil {
return nil, fmt.Errorf("%q is not a valid regexp: %w", v, err)
}
res.NewConfig.Kubernetes.NodeLabelsToSynchronize = append(res.NewConfig.Kubernetes.NodeLabelsToSynchronize, &cpb.ClusterConfiguration_Kubernetes_NodeLabelsToSynchronize{
Regexp: v,
})
}
return res, nil
},
get: func(c *cpb.ClusterConfiguration) (string, error) {
var res []string
if kc := c.Kubernetes; kc != nil {
for _, r := range kc.NodeLabelsToSynchronize {
res = append(res, fmt.Sprintf("%q", r.Regexp))
}
}
return strings.Join(res, ", "), nil
},
},
}
var clusterConfigureCommand = &cobra.Command{
Use: "configure <set/get> <field>",
Short: "Gets/sets values in the cluster configuration structure",
Long: `Gets/sets values in the cluster configuration structure.
The cluster is configured through a ClusterConfiguration structure, of which
a subset of fields can be modified. To set a field's value, use:
cluster configure set <field> <value>
To get a field's current value, use the:
cluster configure get <field>
Available configuration fields:
`,
Args: PrintUsageOnWrongArgs(cobra.MinimumNArgs(2)),
RunE: func(cmd *cobra.Command, args []string) error {
mode := strings.ToLower(args[0])
isSet := false
switch mode {
case "set":
isSet = true
case "get":
default:
return fmt.Errorf("invalid mode %q: must be set or get", mode)
}
var key *configurableClusterKey
for _, k := range configurableClusterKeys {
if k.key == args[1] {
key = &k
break
}
}
if key == nil {
return fmt.Errorf("unknown field %q, see help for list of supported fields", args[1])
}
ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
cc, err := newAuthenticatedClient(ctx)
if err != nil {
return fmt.Errorf("while creating client: %w", err)
}
mgmt := apb.NewManagementClient(cc)
if isSet {
req, err := key.set(args[2:])
if err != nil {
return err
}
res, err := mgmt.ConfigureCluster(ctx, req)
if err != nil {
return fmt.Errorf("could not mutate config: %w", err)
}
newValue, err := key.get(res.ResultingConfig)
if err != nil {
return fmt.Errorf("could not extract value from new config: %w", err)
}
log.Printf("New value: %s", newValue)
} else {
if len(args[2:]) > 0 {
return fmt.Errorf("get <field> takes no extra arguments")
}
ci, err := mgmt.GetClusterInfo(ctx, &apb.GetClusterInfoRequest{})
if err != nil {
return fmt.Errorf("could not get cluster information: %w", err)
}
newValue, err := key.get(ci.ClusterConfiguration)
if err != nil {
return fmt.Errorf("could not extract value from new config: %w", err)
}
log.Printf("Value: %s", newValue)
}
return nil
},
}
func init() {
for _, key := range configurableClusterKeys {
clusterConfigureCommand.Long += fmt.Sprintf(" - %s: %s", key.key, key.description)
}
clusterCmd.AddCommand(clusterConfigureCommand)
}