blob: adb7b8f3569233571f496f8eb6de37373e012696 [file] [log] [blame]
Mateusz Zalegac437dc42022-07-07 13:01:43 +02001package main
2
3import (
4 "context"
5 "fmt"
6 "io"
7 "log"
8
9 "github.com/spf13/cobra"
10
11 clicontext "source.monogon.dev/metropolis/cli/pkg/context"
12 "source.monogon.dev/metropolis/node/core/identity"
13 "source.monogon.dev/metropolis/proto/api"
14)
15
16var approveCmd = &cobra.Command{
17 Short: "Approves a candidate node, if specified; lists nodes pending approval otherwise.",
18 Use: "approve [node-id]",
19 Args: cobra.MaximumNArgs(1), // One positional argument: node ID
20 Run: doApprove,
21}
22
23func init() {
24 rootCmd.AddCommand(approveCmd)
25}
26
27// getNewNodes returns all nodes pending approval within the cluster.
28func getNewNodes(ctx context.Context, mgmt api.ManagementClient) ([]*api.Node, error) {
29 resN, err := mgmt.GetNodes(ctx, &api.GetNodesRequest{
30 Filter: "node.state == NODE_STATE_NEW",
31 })
32 if err != nil {
33 return nil, err
34 }
35
36 var nodes []*api.Node
37 for {
38 node, err := resN.Recv()
39 if err == io.EOF {
40 break
41 }
42 if err != nil {
43 return nil, err
44 }
45 nodes = append(nodes, node)
46 }
47 return nodes, nil
48}
49
50// nodeById returns the node matching id, if it exists within nodes.
51func nodeById(nodes []*api.Node, id string) *api.Node {
52 for _, n := range nodes {
53 if identity.NodeID(n.Pubkey) == id {
54 return n
55 }
56 }
57 return nil
58}
59
60func doApprove(cmd *cobra.Command, args []string) {
61 // Collect credentials, validate command parameters, and try dialing the
62 // cluster.
63 ocert, opkey, err := getCredentials()
64 if err == noCredentialsError {
65 log.Fatalf("You have to take ownership of the cluster first: %v", err)
66 }
67 if len(flags.clusterEndpoints) == 0 {
68 log.Fatal("Please provide at least one cluster endpoint using the --endpoint parameter.")
69 }
70 ctx := clicontext.WithInterrupt(context.Background())
Mateusz Zalegaf7774962022-07-08 12:26:55 +020071 cc, err := dialCluster(ctx, opkey, ocert, flags.proxyAddr, flags.clusterEndpoints)
Mateusz Zalegac437dc42022-07-07 13:01:43 +020072 if err != nil {
73 log.Fatalf("While dialing the cluster: %v", err)
74 }
75 mgmt := api.NewManagementClient(cc)
76
77 // Get a list of all nodes pending approval by calling Management.GetNodes.
78 // We need this list regardless of whether we're actually approving nodes, or
79 // just listing them.
80 nodes, err := getNewNodes(ctx, mgmt)
81 if err != nil {
82 log.Fatalf("While fetching a list of nodes pending approval: %v", err)
83 }
84
85 if len(args) == 0 {
86 // If no id was given, just list the nodes pending approval.
87 if len(nodes) != 0 {
88 for _, n := range nodes {
89 fmt.Print(identity.NodeID(n.Pubkey))
90 }
91 } else {
92 log.Print("There are no nodes pending approval at this time.")
93 }
94 } else {
95 // Otherwise, try to approve the node matching the id.
96 tgtNodeId := args[0]
97
98 n := nodeById(nodes, tgtNodeId)
99 if n == nil {
100 log.Fatalf("Couldn't find a new node matching id %s", tgtNodeId)
101 }
102 _, err := mgmt.ApproveNode(ctx, &api.ApproveNodeRequest{
103 Pubkey: n.Pubkey,
104 })
105 if err != nil {
106 log.Fatalf("While approving node %s: %v", tgtNodeId, err)
107 }
108 log.Printf("Approved node %s.", tgtNodeId)
109 }
110}