blob: 73483c1e95a077be8d33eedc5ac4ee4bab1f4916 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
Serge Bazanskib701df92024-10-31 14:15:33 +00004package main
5
6import (
7 "context"
8 "fmt"
9 "log"
10 "os"
11 "os/signal"
12 "regexp"
13 "strings"
14
15 "github.com/spf13/cobra"
16 "google.golang.org/protobuf/types/known/fieldmaskpb"
17
18 apb "source.monogon.dev/metropolis/proto/api"
19 cpb "source.monogon.dev/metropolis/proto/common"
20)
21
22type configurableClusterKey struct {
23 key string
24 description string
25 set func(value []string) (*apb.ConfigureClusterRequest, error)
26 get func(c *cpb.ClusterConfiguration) (string, error)
27}
28
29var configurableClusterKeys = []configurableClusterKey{
30 {
31 key: "kubernetes.node_labels_to_synchronize",
32 description: "list of label regexes to sync from Metropolis to Kubernetes nodes",
33 set: func(value []string) (*apb.ConfigureClusterRequest, error) {
34 res := &apb.ConfigureClusterRequest{
35 NewConfig: &cpb.ClusterConfiguration{
36 Kubernetes: &cpb.ClusterConfiguration_Kubernetes{},
37 },
38 UpdateMask: &fieldmaskpb.FieldMask{
39 Paths: []string{"kubernetes.node_labels_to_synchronize"},
40 },
41 }
42 for _, v := range value {
43 _, err := regexp.Compile(v)
44 if err != nil {
45 return nil, fmt.Errorf("%q is not a valid regexp: %w", v, err)
46 }
47 res.NewConfig.Kubernetes.NodeLabelsToSynchronize = append(res.NewConfig.Kubernetes.NodeLabelsToSynchronize, &cpb.ClusterConfiguration_Kubernetes_NodeLabelsToSynchronize{
48 Regexp: v,
49 })
50 }
51 return res, nil
52 },
53 get: func(c *cpb.ClusterConfiguration) (string, error) {
54 var res []string
55 if kc := c.Kubernetes; kc != nil {
56 for _, r := range kc.NodeLabelsToSynchronize {
57 res = append(res, fmt.Sprintf("%q", r.Regexp))
58 }
59 }
60 return strings.Join(res, ", "), nil
61 },
62 },
63}
64
65var clusterConfigureCommand = &cobra.Command{
66 Use: "configure <set/get> <field>",
67 Short: "Gets/sets values in the cluster configuration structure",
68 Long: `Gets/sets values in the cluster configuration structure.
69
70The cluster is configured through a ClusterConfiguration structure, of which
71a subset of fields can be modified. To set a field's value, use:
72
73 cluster configure set <field> <value>
74
75To get a field's current value, use the:
76
77 cluster configure get <field>
78
79Available configuration fields:
80
81`,
82 Args: PrintUsageOnWrongArgs(cobra.MinimumNArgs(2)),
83 RunE: func(cmd *cobra.Command, args []string) error {
84 mode := strings.ToLower(args[0])
85 isSet := false
86 switch mode {
87 case "set":
88 isSet = true
89 case "get":
90 default:
91 return fmt.Errorf("invalid mode %q: must be set or get", mode)
92 }
93
94 var key *configurableClusterKey
95 for _, k := range configurableClusterKeys {
96 if k.key == args[1] {
97 key = &k
98 break
99 }
100 }
101 if key == nil {
102 return fmt.Errorf("unknown field %q, see help for list of supported fields", args[1])
103 }
104
105 ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
106 cc, err := dialAuthenticated(ctx)
107 if err != nil {
108 return fmt.Errorf("while dialing node: %w", err)
109 }
110 mgmt := apb.NewManagementClient(cc)
111
112 if isSet {
113 req, err := key.set(args[2:])
114 if err != nil {
115 return err
116 }
117 res, err := mgmt.ConfigureCluster(ctx, req)
118 if err != nil {
119 return fmt.Errorf("could not mutate config: %w", err)
120 }
121 newValue, err := key.get(res.ResultingConfig)
122 if err != nil {
123 return fmt.Errorf("could not extract value from new config: %w", err)
124 }
125 log.Printf("New value: %s", newValue)
126 } else {
127 if len(args[2:]) > 0 {
128 return fmt.Errorf("get <field> takes no extra arguments")
129 }
130 ci, err := mgmt.GetClusterInfo(ctx, &apb.GetClusterInfoRequest{})
131 if err != nil {
132 return fmt.Errorf("could not get cluster information: %w", err)
133 }
134 newValue, err := key.get(ci.ClusterConfiguration)
135 if err != nil {
136 return fmt.Errorf("could not extract value from new config: %w", err)
137 }
138 log.Printf("Value: %s", newValue)
139 }
140 return nil
141 },
142}
143
144func init() {
145 for _, key := range configurableClusterKeys {
146 clusterConfigureCommand.Long += fmt.Sprintf(" - %s: %s", key.key, key.description)
147 }
148 clusterCmd.AddCommand(clusterConfigureCommand)
149}