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