blob: ccfb41cc8fc11db03d6958e68e1225210070e7d6 [file] [log] [blame]
Lorenz Brun6e8f69c2019-11-18 10:44:24 +01001// 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 kubernetes
18
19import (
Lorenz Brun878f5f92020-05-12 16:15:39 +020020 "context"
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010021 "errors"
Serge Bazanskidbfc6382020-06-19 20:35:43 +020022 "fmt"
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010023 "net"
Serge Bazanskidbfc6382020-06-19 20:35:43 +020024 "os"
Lorenz Brunb15abad2020-04-16 11:17:12 +020025 "time"
26
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +020027 "go.etcd.io/etcd/clientv3"
28 "go.uber.org/zap"
Lorenz Brun878f5f92020-05-12 16:15:39 +020029 "google.golang.org/grpc/codes"
30 "google.golang.org/grpc/status"
Lorenz Brunf042e6f2020-06-24 16:46:09 +020031 "k8s.io/client-go/informers"
Lorenz Brunb15abad2020-04-16 11:17:12 +020032 "k8s.io/client-go/kubernetes"
Lorenz Brunf042e6f2020-06-24 16:46:09 +020033 "k8s.io/client-go/tools/clientcmd"
Lorenz Brun878f5f92020-05-12 16:15:39 +020034
35 schema "git.monogon.dev/source/nexantic.git/core/generated/api"
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +020036 "git.monogon.dev/source/nexantic.git/core/internal/common/supervisor"
Hendrik Hofstadt8efe51e2020-02-28 12:53:41 +010037 "git.monogon.dev/source/nexantic.git/core/internal/consensus"
Lorenz Brunf042e6f2020-06-24 16:46:09 +020038 "git.monogon.dev/source/nexantic.git/core/internal/kubernetes/clusternet"
Serge Bazanskidbfc6382020-06-19 20:35:43 +020039 "git.monogon.dev/source/nexantic.git/core/internal/kubernetes/pki"
Serge Bazanskie6030f62020-06-03 17:52:59 +020040 "git.monogon.dev/source/nexantic.git/core/internal/kubernetes/reconciler"
Lorenz Brun0db90ba2020-04-06 14:04:52 +020041 "git.monogon.dev/source/nexantic.git/core/internal/storage"
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +020042 "git.monogon.dev/source/nexantic.git/core/pkg/logbuffer"
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010043)
44
45type Config struct {
46 AdvertiseAddress net.IP
47 ServiceIPRange net.IPNet
48 ClusterNet net.IPNet
49}
50
51type Service struct {
Serge Bazanskidbfc6382020-06-19 20:35:43 +020052 consensusService *consensus.Service
53 storageService *storage.Manager
54 logger *zap.Logger
55
Lorenz Brun878f5f92020-05-12 16:15:39 +020056 apiserverLogs *logbuffer.LogBuffer
57 controllerManagerLogs *logbuffer.LogBuffer
58 schedulerLogs *logbuffer.LogBuffer
59 kubeletLogs *logbuffer.LogBuffer
Serge Bazanskidbfc6382020-06-19 20:35:43 +020060
61 kpki *pki.KubernetesPKI
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010062}
63
Lorenz Brun0db90ba2020-04-06 14:04:52 +020064func New(logger *zap.Logger, consensusService *consensus.Service, storageService *storage.Manager) *Service {
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010065 s := &Service{
Serge Bazanskidbfc6382020-06-19 20:35:43 +020066 consensusService: consensusService,
67 storageService: storageService,
68 logger: logger,
69
Lorenz Brun878f5f92020-05-12 16:15:39 +020070 apiserverLogs: logbuffer.New(5000, 16384),
71 controllerManagerLogs: logbuffer.New(5000, 16384),
72 schedulerLogs: logbuffer.New(5000, 16384),
73 kubeletLogs: logbuffer.New(5000, 16384),
Serge Bazanskidbfc6382020-06-19 20:35:43 +020074
75 kpki: pki.NewKubernetes(logger.Named("pki")),
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010076 }
Serge Bazanskidbfc6382020-06-19 20:35:43 +020077
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010078 return s
79}
80
81func (s *Service) getKV() clientv3.KV {
82 return s.consensusService.GetStore("kubernetes", "")
83}
84
85func (s *Service) NewCluster() error {
Serge Bazanskidbfc6382020-06-19 20:35:43 +020086 // TODO(q3k): this needs to be passed by the caller.
87 ctx := context.TODO()
88 return s.kpki.EnsureAll(ctx, s.getKV())
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010089}
90
Lorenz Brun878f5f92020-05-12 16:15:39 +020091// GetComponentLogs grabs logs from various Kubernetes binaries
92func (s *Service) GetComponentLogs(component string, n int) ([]string, error) {
93 switch component {
94 case "apiserver":
95 return s.apiserverLogs.ReadLinesTruncated(n, "..."), nil
96 case "controller-manager":
97 return s.controllerManagerLogs.ReadLinesTruncated(n, "..."), nil
98 case "scheduler":
99 return s.schedulerLogs.ReadLinesTruncated(n, "..."), nil
100 case "kubelet":
101 return s.kubeletLogs.ReadLinesTruncated(n, "..."), nil
102 default:
103 return []string{}, errors.New("component not available")
104 }
105}
106
107// GetDebugKubeconfig issues a kubeconfig for an arbitrary given identity. Useful for debugging and testing.
108func (s *Service) GetDebugKubeconfig(ctx context.Context, request *schema.GetDebugKubeconfigRequest) (*schema.GetDebugKubeconfigResponse, error) {
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200109 if !s.consensusService.IsReady() {
110 return nil, status.Error(codes.Unavailable, "Consensus not ready yet")
111 }
Serge Bazanskidbfc6382020-06-19 20:35:43 +0200112 ca := s.kpki.Certificates[pki.IdCA]
113 debugKubeconfig, err := pki.New(ca, "", pki.Client(request.Id, request.Groups)).Kubeconfig(ctx, s.getKV())
Lorenz Brun878f5f92020-05-12 16:15:39 +0200114 if err != nil {
115 return nil, status.Errorf(codes.Unavailable, "Failed to generate kubeconfig: %v", err)
116 }
117 return &schema.GetDebugKubeconfigResponse{DebugKubeconfig: string(debugKubeconfig)}, nil
118}
119
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +0200120func (s *Service) Start() error {
121 // TODO(lorenz): This creates a new supervision tree, it should instead attach to the root one. But for that SmalltownNode needs
122 // to be ported to supervisor.
123 supervisor.New(context.TODO(), s.logger, s.Run())
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100124 return nil
125}
126
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +0200127func (s *Service) Run() supervisor.Runnable {
128 return func(ctx context.Context) error {
129 config := Config{
130 AdvertiseAddress: net.IP{10, 0, 2, 15}, // Depends on networking
131 ServiceIPRange: net.IPNet{ // TODO: Decide if configurable / final value
132 IP: net.IP{192, 168, 188, 0},
133 Mask: net.IPMask{0xff, 0xff, 0xff, 0x00}, // /24, but Go stores as a literal mask
134 },
135 ClusterNet: net.IPNet{
136 IP: net.IP{192, 168, 188, 0},
137 Mask: net.IPMask{0xff, 0xff, 0xfd, 0x00}, // /22
138 },
139 }
140 consensusKV := s.getKV()
Serge Bazanskidbfc6382020-06-19 20:35:43 +0200141 apiserverConfig, err := getPKIApiserverConfig(ctx, consensusKV, s.kpki)
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +0200142 if err != nil {
Serge Bazanskidbfc6382020-06-19 20:35:43 +0200143 return fmt.Errorf("could not generate apiserver pki config: %w", err)
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +0200144 }
145 apiserverConfig.advertiseAddress = config.AdvertiseAddress
146 apiserverConfig.serviceIPRange = config.ServiceIPRange
Serge Bazanskidbfc6382020-06-19 20:35:43 +0200147 controllerManagerConfig, err := getPKIControllerManagerConfig(ctx, consensusKV, s.kpki)
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +0200148 if err != nil {
Serge Bazanskidbfc6382020-06-19 20:35:43 +0200149 return fmt.Errorf("could not generate controller manager pki config: %w", err)
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +0200150 }
151 controllerManagerConfig.clusterNet = config.ClusterNet
Serge Bazanskidbfc6382020-06-19 20:35:43 +0200152 schedulerConfig, err := getPKISchedulerConfig(ctx, consensusKV, s.kpki)
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +0200153 if err != nil {
Serge Bazanskidbfc6382020-06-19 20:35:43 +0200154 return fmt.Errorf("could not generate scheduler pki config: %w", err)
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +0200155 }
156
Serge Bazanskidbfc6382020-06-19 20:35:43 +0200157 masterKubeconfig, err := s.kpki.Kubeconfig(ctx, pki.Master, consensusKV)
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +0200158 if err != nil {
Serge Bazanskidbfc6382020-06-19 20:35:43 +0200159 return fmt.Errorf("could not generate master kubeconfig: %w", err)
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +0200160 }
161
Lorenz Brunb15abad2020-04-16 11:17:12 +0200162 rawClientConfig, err := clientcmd.NewClientConfigFromBytes(masterKubeconfig)
163 if err != nil {
Serge Bazanskidbfc6382020-06-19 20:35:43 +0200164 return fmt.Errorf("could not generate kubernetes client config: %w", err)
Lorenz Brunb15abad2020-04-16 11:17:12 +0200165 }
166
167 clientConfig, err := rawClientConfig.ClientConfig()
168 clientSet, err := kubernetes.NewForConfig(clientConfig)
169 if err != nil {
Serge Bazanskidbfc6382020-06-19 20:35:43 +0200170 return fmt.Errorf("could not generate kubernetes client: %w", err)
Lorenz Brunb15abad2020-04-16 11:17:12 +0200171 }
172
173 informerFactory := informers.NewSharedInformerFactory(clientSet, 5*time.Minute)
174
Serge Bazanskidbfc6382020-06-19 20:35:43 +0200175 hostname, err := os.Hostname()
176 if err != nil {
177 return fmt.Errorf("failed to get hostname: %w", err)
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +0200178 }
Serge Bazanskidbfc6382020-06-19 20:35:43 +0200179 err = createKubeletConfig(ctx, consensusKV, s.kpki, hostname)
180 if err != nil {
181 return fmt.Errorf("could not created kubelet config: %w", err)
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +0200182 }
Serge Bazanskidbfc6382020-06-19 20:35:43 +0200183
Lorenz Brunf042e6f2020-06-24 16:46:09 +0200184 key, err := clusternet.EnsureOnDiskKey()
185 if err != nil {
186 return fmt.Errorf("failed to ensure cluster key: %w", err)
187 }
188
Serge Bazanskidbfc6382020-06-19 20:35:43 +0200189 for _, sub := range []struct {
190 name string
191 runnable supervisor.Runnable
192 }{
193 {"apiserver", runAPIServer(*apiserverConfig, s.apiserverLogs)},
194 {"controller-manager", runControllerManager(*controllerManagerConfig, s.controllerManagerLogs)},
195 {"scheduler", runScheduler(*schedulerConfig, s.schedulerLogs)},
196 {"kubelet", runKubelet(&KubeletSpec{}, s.kubeletLogs)},
197 {"reconciler", reconciler.Run(clientSet)},
198 {"csi-plugin", runCSIPlugin(s.storageService)},
199 {"pv-provisioner", runCSIProvisioner(s.storageService, clientSet, informerFactory)},
Lorenz Brunf042e6f2020-06-24 16:46:09 +0200200 {"clusternet", clusternet.Run(informerFactory, clusterNet, clientSet, key)},
Serge Bazanskidbfc6382020-06-19 20:35:43 +0200201 } {
202 err := supervisor.Run(ctx, sub.name, sub.runnable)
203 if err != nil {
204 return fmt.Errorf("could not run sub-service %q: %w", sub.name, err)
205 }
Lorenz Brunb15abad2020-04-16 11:17:12 +0200206 }
207
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +0200208 supervisor.Signal(ctx, supervisor.SignalHealthy)
209 supervisor.Signal(ctx, supervisor.SignalDone)
210 return nil
211 }
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100212}