blob: 82e7cca327c993dc3885cbe1b55311a77c933df7 [file] [log] [blame]
Serge Bazanski0d937772021-06-17 15:54:40 +02001// package roleserve implements the roleserver/“Role Server”.
2//
Serge Bazanski6dff6d62022-01-28 18:15:14 +01003// The Role Server runs on every node and is responsible for running all of the
4// node's role dependant services, like the control plane (Consensus/etcd and
5// Curator) and Kubernetes. It watches the node roles as assigned by the
6// cluster's curator, updates the status of the node within the curator, and
7// spawns on-demand services.
Serge Bazanski0d937772021-06-17 15:54:40 +02008//
Serge Bazanski0d937772021-06-17 15:54:40 +02009//
Serge Bazanski6dff6d62022-01-28 18:15:14 +010010// .-----------. .--------. Watches .------------.
11// | Cluster |--------->| Role |<----------| Node Roles |
12// | Enrolment | Provides | Server | Updates '------------'
13// '-----------' Data | |----. .-------------.
14// '--------' '----->| Node Status |
15// Spawns | | Spawns '-------------'
16// .-----' '-----.
17// V V
18// .-----------. .------------.
19// | Consensus | | Kubernetes |
20// | & Curator | | |
21// '-----------' '------------'
22//
23// The internal state of the Role Server (eg. status of services, input from
24// Cluster Enrolment, current node roles as retrieved from the cluster) is
25// stored as in-memory Event Value variables, with some of them being exposed
26// externally for other services to consume (ie. ones that wish to depend on
27// some information managed by the Role Server but which do not need to be
28// spawned on demand by the Role Server). These Event Values and code which acts
29// upon them form a reactive/dataflow-driven model which drives the Role Server
30// logic forward.
31//
32// The Role Server also has to handle the complex bootstrap problem involved in
33// simultaneously accessing the control plane (for node roles and other cluster
34// data) while maintaining (possibly the only one in the cluster) control plane
35// instance. The state of of resolution of this bootstrap problem is maintained
36// within ClusterMembership, which contains critical information about the
37// control plane, like the information required to connect to a Curator (local
38// or remote). It is updated both by external processes (ie. data from the
39// Cluster Enrolment) as well as logic responsible for spawning the control
40// plane.
41//
Serge Bazanski0d937772021-06-17 15:54:40 +020042package roleserve
43
44import (
45 "context"
Serge Bazanski6dff6d62022-01-28 18:15:14 +010046 "crypto/ed25519"
Serge Bazanski0d937772021-06-17 15:54:40 +020047
Lorenz Brun1de8b182021-12-21 17:15:18 +010048 "source.monogon.dev/metropolis/node/core/identity"
Serge Bazanski0d937772021-06-17 15:54:40 +020049 "source.monogon.dev/metropolis/node/core/localstorage"
50 "source.monogon.dev/metropolis/node/core/network"
Serge Bazanski0d937772021-06-17 15:54:40 +020051 "source.monogon.dev/metropolis/pkg/supervisor"
Serge Bazanski6dff6d62022-01-28 18:15:14 +010052 cpb "source.monogon.dev/metropolis/proto/common"
Serge Bazanski0d937772021-06-17 15:54:40 +020053)
54
55// Config is the configuration of the role server.
56type Config struct {
Serge Bazanski0d937772021-06-17 15:54:40 +020057 // StorageRoot is a handle to access all of the Node's storage. This is needed
58 // as the roleserver spawns complex workloads like Kubernetes which need access
59 // to a broad range of storage.
60 StorageRoot *localstorage.Root
61
62 // Network is a handle to the network service, used by workloads.
63 Network *network.Service
Serge Bazanski0d937772021-06-17 15:54:40 +020064}
65
66// Service is the roleserver/“Role Server” service. See the package-level
67// documentation for more details.
68type Service struct {
69 Config
70
Serge Bazanski6dff6d62022-01-28 18:15:14 +010071 ClusterMembership ClusterMembershipValue
72 KubernetesStatus KubernetesStatusValue
73 bootstrapData bootstrapDataValue
74 localRoles localRolesValue
Serge Bazanski0d937772021-06-17 15:54:40 +020075
Serge Bazanski6dff6d62022-01-28 18:15:14 +010076 controlPlane *workerControlPlane
77 statusPush *workerStatusPush
Mateusz Zalega32b19292022-05-17 13:26:55 +020078 heartbeat *workerHeartbeat
Serge Bazanski6dff6d62022-01-28 18:15:14 +010079 kubernetes *workerKubernetes
80 rolefetch *workerRoleFetch
Serge Bazanski0d937772021-06-17 15:54:40 +020081}
82
83// New creates a Role Server services from a Config.
84func New(c Config) *Service {
Serge Bazanski6dff6d62022-01-28 18:15:14 +010085 s := &Service{
Serge Bazanski0d937772021-06-17 15:54:40 +020086 Config: c,
Serge Bazanski0d937772021-06-17 15:54:40 +020087 }
Serge Bazanski6dff6d62022-01-28 18:15:14 +010088
89 s.controlPlane = &workerControlPlane{
90 storageRoot: s.StorageRoot,
91
92 bootstrapData: &s.bootstrapData,
93 clusterMembership: &s.ClusterMembership,
94 localRoles: &s.localRoles,
95 }
96
97 s.statusPush = &workerStatusPush{
98 network: s.Network,
99
100 clusterMembership: &s.ClusterMembership,
101 }
102
Mateusz Zalega32b19292022-05-17 13:26:55 +0200103 s.heartbeat = &workerHeartbeat{
104 network: s.Network,
105
106 clusterMembership: &s.ClusterMembership,
107 }
108
Serge Bazanski6dff6d62022-01-28 18:15:14 +0100109 s.kubernetes = &workerKubernetes{
110 network: s.Network,
111 storageRoot: s.StorageRoot,
112
113 localRoles: &s.localRoles,
114 clusterMembership: &s.ClusterMembership,
115
116 kubernetesStatus: &s.KubernetesStatus,
117 }
118
119 s.rolefetch = &workerRoleFetch{
120 clusterMembership: &s.ClusterMembership,
121
122 localRoles: &s.localRoles,
123 }
124
125 return s
Serge Bazanski0d937772021-06-17 15:54:40 +0200126}
127
Mateusz Zalega2930e992022-04-25 12:52:35 +0200128func (s *Service) ProvideBootstrapData(privkey ed25519.PrivateKey, iok, cuk, nuk, jkey []byte) {
Serge Bazanski6dff6d62022-01-28 18:15:14 +0100129 s.ClusterMembership.set(&ClusterMembership{
130 pubkey: privkey.Public().(ed25519.PublicKey),
131 })
132 s.bootstrapData.set(&bootstrapData{
Mateusz Zalega2930e992022-04-25 12:52:35 +0200133 nodePrivateKey: privkey,
134 initialOwnerKey: iok,
135 clusterUnlockKey: cuk,
136 nodeUnlockKey: nuk,
137 nodePrivateJoinKey: jkey,
Serge Bazanski6dff6d62022-01-28 18:15:14 +0100138 })
Serge Bazanski0d937772021-06-17 15:54:40 +0200139}
140
Serge Bazanski6dff6d62022-01-28 18:15:14 +0100141func (s *Service) ProvideRegisterData(credentials identity.NodeCredentials, directory *cpb.ClusterDirectory) {
142 s.ClusterMembership.set(&ClusterMembership{
143 remoteCurators: directory,
144 credentials: &credentials,
145 pubkey: credentials.PublicKey(),
146 })
Serge Bazanski0d937772021-06-17 15:54:40 +0200147}
148
Mateusz Zalega2930e992022-04-25 12:52:35 +0200149func (s *Service) ProvideJoinData(credentials identity.NodeCredentials, directory *cpb.ClusterDirectory) {
150 s.ClusterMembership.set(&ClusterMembership{
151 remoteCurators: directory,
152 credentials: &credentials,
153 pubkey: credentials.PublicKey(),
154 })
155}
156
Serge Bazanski0d937772021-06-17 15:54:40 +0200157// Run the Role Server service, which uses intermediary workload launchers to
158// start/stop subordinate services as the Node's roles change.
159func (s *Service) Run(ctx context.Context) error {
Serge Bazanski6dff6d62022-01-28 18:15:14 +0100160 supervisor.Run(ctx, "controlplane", s.controlPlane.run)
161 supervisor.Run(ctx, "kubernetes", s.kubernetes.run)
162 supervisor.Run(ctx, "statuspush", s.statusPush.run)
Mateusz Zalega32b19292022-05-17 13:26:55 +0200163 supervisor.Run(ctx, "heartbeat", s.heartbeat.run)
Serge Bazanski6dff6d62022-01-28 18:15:14 +0100164 supervisor.Run(ctx, "rolefetch", s.rolefetch.run)
Serge Bazanski0d937772021-06-17 15:54:40 +0200165 supervisor.Signal(ctx, supervisor.SignalHealthy)
166
Serge Bazanski6dff6d62022-01-28 18:15:14 +0100167 <-ctx.Done()
168 return ctx.Err()
Serge Bazanski0d937772021-06-17 15:54:40 +0200169}