blob: 481c81869d5977aefa7ebe8fcc1e138b07f1e77c [file] [log] [blame]
Serge Bazanskia959cbd2021-06-17 15:56:51 +02001package cluster
2
3import (
4 "errors"
5 "fmt"
6
7 "source.monogon.dev/metropolis/node/core/consensus/client"
Serge Bazanski3379a5d2021-09-09 12:56:40 +02008 "source.monogon.dev/metropolis/node/core/identity"
Serge Bazanskia959cbd2021-06-17 15:56:51 +02009 cpb "source.monogon.dev/metropolis/proto/common"
10)
11
12var (
13 ErrNoLocalConsensus = errors.New("this node does not have direct access to etcd")
14)
15
16// Status is returned to Cluster clients (ie., node code) on Manager.Watch/.Get.
17type Status struct {
18 // State is the current state of the cluster, as seen by the node.
19 State cpb.ClusterState
20
21 // hasLocalConsensus is true if the local node is running a local consensus
22 // (etcd) server.
Serge Bazanskie78a0892021-10-07 17:03:49 +020023 HasLocalConsensus bool
Serge Bazanskia959cbd2021-06-17 15:56:51 +020024 // consensusClient is an etcd client to the local consensus server if the node
25 // has such a server and the cluster state is HOME or SPLIT.
26 consensusClient client.Namespaced
27
28 // Credentials used for the node to authenticate to the Curator and other
29 // cluster services.
Serge Bazanski3379a5d2021-09-09 12:56:40 +020030 Credentials *identity.NodeCredentials
Serge Bazanskia959cbd2021-06-17 15:56:51 +020031}
32
33// ConsensusUser is the to-level user of an etcd client in Metropolis node
34// code. These need to be defined ahead of time in an Go 'enum', and different
35// ConsensusUsers should not be shared by different codepaths.
36type ConsensusUser string
37
38const (
39 ConsensusUserKubernetesPKI ConsensusUser = "kubernetes-pki"
40 ConsensusUserCurator ConsensusUser = "curator"
41)
42
43// ConsensusClient returns an etcd/consensus client for a given ConsensusUser.
44// The node must be running a local consensus/etcd server.
45func (s *Status) ConsensusClient(user ConsensusUser) (client.Namespaced, error) {
Serge Bazanskie78a0892021-10-07 17:03:49 +020046 if !s.HasLocalConsensus {
Serge Bazanskia959cbd2021-06-17 15:56:51 +020047 return nil, ErrNoLocalConsensus
48 }
49
50 // Ensure that we already are connected to etcd and are in a state in which we
51 // should be handing out cluster connectivity.
52 if s.consensusClient == nil {
53 return nil, fmt.Errorf("not connected")
54 }
55 switch s.State {
56 case cpb.ClusterState_CLUSTER_STATE_HOME:
57 case cpb.ClusterState_CLUSTER_STATE_SPLIT:
58 // The consensus client is resistant to being split off, and will serve
59 // as soon as the split is resolved.
60 default:
61 return nil, fmt.Errorf("refusing connection with cluster state %v", s.State)
62 }
63
64 // Ensure only defined 'applications' are used to prevent programmer error and
65 // casting to ConsensusUser from an arbitrary string.
66 switch user {
67 case ConsensusUserKubernetesPKI:
68 case ConsensusUserCurator:
69 default:
70 return nil, fmt.Errorf("unknown ConsensusUser %q", user)
71 }
72 client, err := s.consensusClient.Sub(string(user))
73 if err != nil {
74 return nil, fmt.Errorf("retrieving subclient failed: %w", err)
75 }
76 return client, nil
77}