blob: 4041cb80861b4d2b4b7ef01dccb626841a5f1cf8 [file] [log] [blame]
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +02001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package node
18
19import (
Lorenz Brunaa6b7342019-12-12 02:55:02 +010020 "bytes"
Serge Bazanskicdb8c782020-02-17 12:34:02 +010021 "context"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010022 "crypto/ed25519"
23 "crypto/rand"
24 "crypto/sha512"
25 "crypto/tls"
26 "crypto/x509"
27 "crypto/x509/pkix"
Lorenz Brun878f5f92020-05-12 16:15:39 +020028 "encoding/hex"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010029 "errors"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020030 "flag"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010031 "fmt"
Lorenz Brun878f5f92020-05-12 16:15:39 +020032 "git.monogon.dev/source/nexantic.git/core/internal/containerd"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010033 "io/ioutil"
34 "math/big"
35 "net"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010036 "os"
Lorenz Brun878f5f92020-05-12 16:15:39 +020037 "strings"
Serge Bazanskicdb8c782020-02-17 12:34:02 +010038 "time"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010039
40 apipb "git.monogon.dev/source/nexantic.git/core/generated/api"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020041 "git.monogon.dev/source/nexantic.git/core/internal/api"
42 "git.monogon.dev/source/nexantic.git/core/internal/common"
43 "git.monogon.dev/source/nexantic.git/core/internal/consensus"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010044 "git.monogon.dev/source/nexantic.git/core/internal/integrity/tpm2"
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010045 "git.monogon.dev/source/nexantic.git/core/internal/kubernetes"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010046 "git.monogon.dev/source/nexantic.git/core/internal/network"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020047 "git.monogon.dev/source/nexantic.git/core/internal/storage"
Lorenz Brun878f5f92020-05-12 16:15:39 +020048 "golang.org/x/sys/unix"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020049
Serge Bazanskicdb8c782020-02-17 12:34:02 +010050 "github.com/cenkalti/backoff/v4"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010051 "github.com/gogo/protobuf/proto"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020052 "go.uber.org/zap"
Serge Bazanskicdb8c782020-02-17 12:34:02 +010053 "google.golang.org/grpc"
54 "google.golang.org/grpc/credentials"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020055)
56
Lorenz Brunaa6b7342019-12-12 02:55:02 +010057var (
58 // From RFC 5280 Section 4.1.2.5
59 unknownNotAfter = time.Unix(253402300799, 0)
60)
61
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020062type (
63 SmalltownNode struct {
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010064 Api *api.Server
65 Consensus *consensus.Service
66 Storage *storage.Manager
67 Kubernetes *kubernetes.Service
Lorenz Brun878f5f92020-05-12 16:15:39 +020068 Containerd *containerd.Service
Lorenz Brunaa6b7342019-12-12 02:55:02 +010069 Network *network.Service
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020070
Lorenz Brunaa6b7342019-12-12 02:55:02 +010071 logger *zap.Logger
72 state common.SmalltownState
73 hostname string
74 enrolmentConfig *apipb.EnrolmentConfig
Lorenz Brun878f5f92020-05-12 16:15:39 +020075
76 debugServer *grpc.Server
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020077 }
78)
79
Serge Bazanskicdb8c782020-02-17 12:34:02 +010080func NewSmalltownNode(logger *zap.Logger, ntwk *network.Service, strg *storage.Manager) (*SmalltownNode, error) {
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020081 flag.Parse()
82 logger.Info("Creating Smalltown node")
Serge Bazanskicdb8c782020-02-17 12:34:02 +010083 ctx := context.Background()
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020084
Leopold Schabel68c58752019-11-14 21:00:59 +010085 hostname, err := os.Hostname()
86 if err != nil {
87 panic(err)
88 }
89
Serge Bazanskicdb8c782020-02-17 12:34:02 +010090 // Wait for IP adddress...
91 ctxT, ctxTC := context.WithTimeout(ctx, time.Second*10)
92 defer ctxTC()
93 externalIP, err := ntwk.GetIP(ctxT, true)
Lorenz Brunaa6b7342019-12-12 02:55:02 +010094 if err != nil {
Serge Bazanskicdb8c782020-02-17 12:34:02 +010095 logger.Panic("Could not get IP address", zap.Error(err))
Lorenz Brunaa6b7342019-12-12 02:55:02 +010096 }
97
98 // Important to know if the GetIP above hangs
99 logger.Info("Node has IP", zap.String("ip", externalIP.String()))
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200100
101 consensusService, err := consensus.NewConsensusService(consensus.Config{
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100102 Name: hostname,
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100103 ListenHost: "0.0.0.0",
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100104 ExternalHost: externalIP.String(),
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200105 }, logger.With(zap.String("module", "consensus")))
106 if err != nil {
107 return nil, err
108 }
109
Lorenz Brun878f5f92020-05-12 16:15:39 +0200110 containerdService, err := containerd.New()
111 if err != nil {
112 return nil, err
113 }
114
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200115 s := &SmalltownNode{
Lorenz Brun878f5f92020-05-12 16:15:39 +0200116 Consensus: consensusService,
117 Containerd: containerdService,
118 Storage: strg,
119 Network: ntwk,
120 logger: logger,
121 hostname: hostname,
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200122 }
123
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100124 apiService, err := api.NewApiServer(&api.Config{}, logger.With(zap.String("module", "api")), s.Consensus)
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200125 if err != nil {
126 return nil, err
127 }
128
129 s.Api = apiService
130
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100131 s.Kubernetes = kubernetes.New(logger.With(zap.String("module", "kubernetes")), consensusService)
132
Lorenz Brun878f5f92020-05-12 16:15:39 +0200133 s.debugServer = grpc.NewServer()
134 apipb.RegisterNodeDebugServiceServer(s.debugServer, s)
135
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200136 logger.Info("Created SmalltownNode")
137
138 return s, nil
139}
140
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100141func (s *SmalltownNode) Start(ctx context.Context) error {
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200142 s.logger.Info("Starting Smalltown node")
143
Lorenz Brun878f5f92020-05-12 16:15:39 +0200144 s.startDebugSvc()
145
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100146 // TODO(lorenz): Abstracting enrolment sounds like a good idea, but ends up being painful
147 // because of things like storage access. I'm keeping it this way until the more complex
148 // enrolment procedures are fleshed out. This is also a bit panic()-happy, but there is really
149 // no good way out of an invalid enrolment configuration.
150 enrolmentPath, err := s.Storage.GetPathInPlace(storage.PlaceESP, "enrolment.pb")
151 if err != nil {
152 s.logger.Panic("ESP configuration partition not available", zap.Error(err))
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200153 }
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100154 enrolmentConfigRaw, err := ioutil.ReadFile(enrolmentPath)
155 if err == nil {
156 // We have an enrolment file, let's check its contents
157 var enrolmentConfig apipb.EnrolmentConfig
158 if err := proto.Unmarshal(enrolmentConfigRaw, &enrolmentConfig); err != nil {
159 s.logger.Panic("Invalid enrolment configuration provided", zap.Error(err))
160 }
161 s.enrolmentConfig = &enrolmentConfig
162 // The enrolment secret is only zeroed after
163 if len(enrolmentConfig.EnrolmentSecret) == 0 {
164 return s.startFull()
165 }
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100166 return s.startEnrolling(ctx)
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100167 } else if os.IsNotExist(err) {
168 // This is ok like this, once a new cluster has been set up the initial node also generates
169 // its own enrolment config
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100170 return s.startForSetup(ctx)
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100171 }
172 // Unknown error reading enrolment config (disk issues/invalid configuration format/...)
173 s.logger.Panic("Invalid enrolment configuration provided", zap.Error(err))
174 panic("Unreachable")
175}
176
Lorenz Brun878f5f92020-05-12 16:15:39 +0200177func (s *SmalltownNode) startDebugSvc() {
178 debugListenHost := fmt.Sprintf(":%v", common.DebugServicePort)
179 debugListener, err := net.Listen("tcp", debugListenHost)
180 if err != nil {
181 s.logger.Fatal("failed to listen", zap.Error(err))
182 }
183
184 go func() {
185 if err := s.debugServer.Serve(debugListener); err != nil {
186 s.logger.Fatal("failed to serve", zap.Error(err))
187 }
188 }()
189}
190
191func (s *SmalltownNode) initHostname() error {
192 if err := unix.Sethostname([]byte(s.hostname)); err != nil {
193 return err
194 }
195 if err := ioutil.WriteFile("/etc/hosts", []byte(fmt.Sprintf("%v %v", "127.0.0.1", s.hostname)), 0644); err != nil {
196 return err
197 }
198 return ioutil.WriteFile("/etc/machine-id", []byte(strings.TrimPrefix(s.hostname, "smalltown-")), 0644)
199}
200
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100201func (s *SmalltownNode) startEnrolling(ctx context.Context) error {
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100202 s.logger.Info("Initializing subsystems for enrolment")
203 s.state = common.StateEnrollMode
204
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100205 nodeInfo, nodeID, err := s.InitializeNode(ctx)
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100206 if err != nil {
207 return err
208 }
209
Lorenz Brun878f5f92020-05-12 16:15:39 +0200210 s.hostname = nodeID
211 if err := s.initHostname(); err != nil {
212 return err
213 }
214
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100215 // We only support TPM2 at the moment, any abstractions here would be premature
216 trustAgent := tpm2.TPM2Agent{}
217
218 initializeOp := func() error {
219 if err := trustAgent.Initialize(*nodeInfo, *s.enrolmentConfig); err != nil {
220 s.logger.Warn("Failed to initialize integrity backend", zap.Error(err))
221 return err
222 }
223 return nil
224 }
225
226 if err := backoff.Retry(initializeOp, getIntegrityBackoff()); err != nil {
227 panic("invariant violated: integrity initialization retry can never fail")
228 }
229
230 enrolmentPath, err := s.Storage.GetPathInPlace(storage.PlaceESP, "enrolment.pb")
231 if err != nil {
232 panic(err)
233 }
234
235 s.enrolmentConfig.EnrolmentSecret = []byte{}
236 s.enrolmentConfig.NodeId = nodeID
237
238 enrolmentConfigRaw, err := proto.Marshal(s.enrolmentConfig)
239 if err != nil {
240 panic(err)
241 }
242 if err := ioutil.WriteFile(enrolmentPath, enrolmentConfigRaw, 0600); err != nil {
243 return err
244 }
245 s.logger.Info("Node successfully enrolled")
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200246
247 return nil
248}
249
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100250func (s *SmalltownNode) startForSetup(ctx context.Context) error {
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100251 s.logger.Info("Setting up a new cluster")
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100252 initData, nodeID, err := s.InitializeNode(ctx)
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200253 if err != nil {
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200254 return err
255 }
Lorenz Brun878f5f92020-05-12 16:15:39 +0200256 s.hostname = nodeID
257 if err := s.initHostname(); err != nil {
258 return err
259 }
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200260
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100261 if err := s.initNodeAPI(); err != nil {
262 return err
263 }
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200264
Lorenz Brun878f5f92020-05-12 16:15:39 +0200265 // TODO: Use supervisor.Run for this
266 go s.Containerd.Run()(context.TODO())
267
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100268 dataPath, err := s.Storage.GetPathInPlace(storage.PlaceData, "etcd")
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200269 if err != nil {
270 return err
271 }
272
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100273 // Spin up etcd
274 config := s.Consensus.GetConfig()
275 config.NewCluster = true
276 config.Name = s.hostname
277 config.DataDir = dataPath
278 s.Consensus.SetConfig(config)
279
280 // Generate the cluster CA and store it to local storage.
281 if err := s.Consensus.PrecreateCA(); err != nil {
282 return err
283 }
284
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200285 err = s.Consensus.Start()
286 if err != nil {
287 return err
288 }
289
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100290 // Now that the cluster is up and running, we can persist the CA to the cluster.
291 if err := s.Consensus.InjectCA(); err != nil {
292 return err
293 }
294
295 if err := s.Api.BootstrapNewClusterHook(initData); err != nil {
296 return err
297 }
298
299 if err := s.Kubernetes.NewCluster(); err != nil {
300 return err
301 }
302
303 if err := s.Kubernetes.Start(); err != nil {
304 return err
305 }
306
307 if err := s.Api.Start(); err != nil {
308 s.logger.Error("Failed to start the API service", zap.Error(err))
309 return err
310 }
311
312 enrolmentPath, err := s.Storage.GetPathInPlace(storage.PlaceESP, "enrolment.pb")
313 if err != nil {
314 panic(err)
315 }
316
317 masterCert, err := s.Api.GetMasterCert()
318 if err != nil {
319 return err
320 }
321
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100322 ip, err := s.Network.GetIP(ctx, true)
323 if err != nil {
324 return fmt.Errorf("could not get node IP: %v", err)
325 }
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100326 enrolmentConfig := &apipb.EnrolmentConfig{
327 EnrolmentSecret: []byte{}, // First node is always already enrolled
328 MastersCert: masterCert,
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100329 MasterIps: [][]byte{[]byte(*ip)},
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100330 NodeId: nodeID,
331 }
332 enrolmentConfigRaw, err := proto.Marshal(enrolmentConfig)
333 if err != nil {
334 panic(err)
335 }
336 if err := ioutil.WriteFile(enrolmentPath, enrolmentConfigRaw, 0600); err != nil {
337 return err
338 }
339 masterCertFingerprint := sha512.Sum512_256(masterCert)
340 s.logger.Info("New Smalltown cluster successfully bootstrapped", zap.Binary("fingerprint", masterCertFingerprint[:]))
341
342 return nil
343}
344
345func (s *SmalltownNode) generateNodeID() ([]byte, string, error) {
346 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 127)
347 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
348 if err != nil {
349 return []byte{}, "", fmt.Errorf("Failed to generate serial number: %w", err)
350 }
351
352 pubKey, privKeyRaw, err := ed25519.GenerateKey(rand.Reader)
353 if err != nil {
354 return []byte{}, "", err
355 }
356 privkey, err := x509.MarshalPKCS8PrivateKey(privKeyRaw)
357 if err != nil {
358 return []byte{}, "", err
359 }
360
361 nodeKeyPath, err := s.Storage.GetPathInPlace(storage.PlaceData, "node-key.der")
362 if err != nil {
363 return []byte{}, "", err
364 }
365
366 if err := ioutil.WriteFile(nodeKeyPath, privkey, 0600); err != nil {
367 return []byte{}, "", fmt.Errorf("failed to write node key: %w", err)
368 }
369
Lorenz Brun878f5f92020-05-12 16:15:39 +0200370 name := "smalltown-" + hex.EncodeToString([]byte(pubKey[:16]))
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100371
372 // This has no SANs because it authenticates by public key, not by name
373 nodeCert := &x509.Certificate{
374 SerialNumber: serialNumber,
375 Subject: pkix.Name{
376 // We identify nodes by their ID public keys (not hashed since a strong hash is longer and serves no benefit)
377 CommonName: name,
378 },
379 IsCA: false,
380 BasicConstraintsValid: true,
381 NotBefore: time.Now(),
382 NotAfter: unknownNotAfter,
383 // Certificate is used both as server & client
384 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
385 }
386 cert, err := x509.CreateCertificate(rand.Reader, nodeCert, nodeCert, pubKey, privKeyRaw)
387 if err != nil {
388 return []byte{}, "", err
389 }
390
391 nodeCertPath, err := s.Storage.GetPathInPlace(storage.PlaceData, "node.der")
392 if err != nil {
393 return []byte{}, "", err
394 }
395
396 if err := ioutil.WriteFile(nodeCertPath, cert, 0600); err != nil {
397 return []byte{}, "", fmt.Errorf("failed to write node cert: %w", err)
398 }
399 return cert, name, nil
400}
401
402func (s *SmalltownNode) initNodeAPI() error {
403 certPath, err := s.Storage.GetPathInPlace(storage.PlaceData, "node.der")
404 if err != nil {
405 s.logger.Panic("Invariant violated: Data is available once this is called")
406 }
407 keyPath, err := s.Storage.GetPathInPlace(storage.PlaceData, "node-key.der")
408 if err != nil {
409 s.logger.Panic("Invariant violated: Data is available once this is called")
410 }
411
412 certRaw, err := ioutil.ReadFile(certPath)
413 if err != nil {
414 return err
415 }
416 privKeyRaw, err := ioutil.ReadFile(keyPath)
417 if err != nil {
418 return err
419 }
420
421 var nodeID tls.Certificate
422
423 cert, err := x509.ParseCertificate(certRaw)
424 if err != nil {
425 return err
426 }
427
428 privKey, err := x509.ParsePKCS8PrivateKey(privKeyRaw)
429 if err != nil {
430 return err
431 }
432
433 nodeID.Certificate = [][]byte{certRaw}
434 nodeID.PrivateKey = privKey
435 nodeID.Leaf = cert
436
437 secureTransport := &tls.Config{
438 Certificates: []tls.Certificate{nodeID},
439 ClientAuth: tls.RequireAndVerifyClientCert,
440 InsecureSkipVerify: true,
441 // Critical function, please review any changes with care
442 // TODO(lorenz): Actively check that this actually provides the security guarantees that we need
443 VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
444 for _, cert := range rawCerts {
445 // X.509 certificates in DER can be compared like this since DER has a unique representation
446 // for each certificate.
447 if bytes.Equal(cert, s.enrolmentConfig.MastersCert) {
448 return nil
449 }
450 }
451 return errors.New("failed to find authorized NMS certificate")
452 },
453 MinVersion: tls.VersionTLS13,
454 }
455 secureTransportCreds := credentials.NewTLS(secureTransport)
456
457 masterListenHost := fmt.Sprintf(":%d", common.NodeServicePort)
458 lis, err := net.Listen("tcp", masterListenHost)
459 if err != nil {
460 s.logger.Fatal("failed to listen", zap.Error(err))
461 }
462
463 nodeGRPCServer := grpc.NewServer(grpc.Creds(secureTransportCreds))
464 apipb.RegisterNodeServiceServer(nodeGRPCServer, s)
465 go func() {
466 if err := nodeGRPCServer.Serve(lis); err != nil {
467 panic(err) // Can only happen during initialization and is always fatal
468 }
469 }()
470 return nil
471}
472
473func getIntegrityBackoff() *backoff.ExponentialBackOff {
474 unlockBackoff := backoff.NewExponentialBackOff()
475 unlockBackoff.MaxElapsedTime = time.Duration(0)
476 unlockBackoff.InitialInterval = 5 * time.Second
477 unlockBackoff.MaxInterval = 5 * time.Minute
478 return unlockBackoff
479}
480
481func (s *SmalltownNode) startFull() error {
482 s.logger.Info("Initializing subsystems for production")
483 s.state = common.StateJoined
484
Lorenz Brun878f5f92020-05-12 16:15:39 +0200485 s.hostname = s.enrolmentConfig.NodeId
486 if err := s.initHostname(); err != nil {
487 return err
488 }
489
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100490 trustAgent := tpm2.TPM2Agent{}
491 unlockOp := func() error {
492 unlockKey, err := trustAgent.Unlock(*s.enrolmentConfig)
493 if err != nil {
494 s.logger.Warn("Failed to unlock", zap.Error(err))
495 return err
496 }
497 if err := s.Storage.MountData(unlockKey); err != nil {
498 s.logger.Panic("Failed to mount storage", zap.Error(err))
499 return err
500 }
501 return nil
502 }
503
504 if err := backoff.Retry(unlockOp, getIntegrityBackoff()); err != nil {
505 s.logger.Panic("Invariant violated: Unlock retry can never fail")
506 }
507
508 s.initNodeAPI()
509
Lorenz Brun878f5f92020-05-12 16:15:39 +0200510 // TODO: Use supervisor.Run for this
511 go s.Containerd.Run()(context.TODO())
512
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100513 err := s.Consensus.Start()
514 if err != nil {
515 return err
516 }
517
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200518 err = s.Api.Start()
519 if err != nil {
520 s.logger.Error("Failed to start the API service", zap.Error(err))
521 return err
522 }
523
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100524 err = s.Kubernetes.Start()
525 if err != nil {
526 s.logger.Error("Failed to start the Kubernetes Service", zap.Error(err))
527 }
528
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200529 return nil
530}
531
532func (s *SmalltownNode) Stop() error {
533 s.logger.Info("Stopping Smalltown node")
534 return nil
535}