blob: e4391f5bfc817ee25b94e9dda12c8879b4c67422 [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"
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020025 "sync"
Lorenz Brunb15abad2020-04-16 11:17:12 +020026 "time"
27
Lorenz Brun878f5f92020-05-12 16:15:39 +020028 "google.golang.org/grpc/codes"
29 "google.golang.org/grpc/status"
Lorenz Brunf042e6f2020-06-24 16:46:09 +020030 "k8s.io/client-go/informers"
Lorenz Brunb15abad2020-04-16 11:17:12 +020031 "k8s.io/client-go/kubernetes"
Lorenz Brunf042e6f2020-06-24 16:46:09 +020032 "k8s.io/client-go/tools/clientcmd"
Lorenz Brun878f5f92020-05-12 16:15:39 +020033
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +020034 "git.monogon.dev/source/nexantic.git/core/internal/common/supervisor"
Lorenz Brunf042e6f2020-06-24 16:46:09 +020035 "git.monogon.dev/source/nexantic.git/core/internal/kubernetes/clusternet"
Lorenz Brunb682ba52020-07-08 14:51:36 +020036 "git.monogon.dev/source/nexantic.git/core/internal/kubernetes/nfproxy"
Serge Bazanskidbfc6382020-06-19 20:35:43 +020037 "git.monogon.dev/source/nexantic.git/core/internal/kubernetes/pki"
Serge Bazanskie6030f62020-06-03 17:52:59 +020038 "git.monogon.dev/source/nexantic.git/core/internal/kubernetes/reconciler"
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020039 "git.monogon.dev/source/nexantic.git/core/internal/localstorage"
Lorenz Brunfa5c2fc2020-09-28 13:32:12 +020040 "git.monogon.dev/source/nexantic.git/core/internal/network/dns"
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +020041 "git.monogon.dev/source/nexantic.git/core/pkg/logbuffer"
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020042 apb "git.monogon.dev/source/nexantic.git/core/proto/api"
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010043)
44
45type Config struct {
46 AdvertiseAddress net.IP
47 ServiceIPRange net.IPNet
48 ClusterNet net.IPNet
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020049
Lorenz Brunfa5c2fc2020-09-28 13:32:12 +020050 KPKI *pki.KubernetesPKI
51 Root *localstorage.Root
52 CorednsRegistrationChan chan *dns.ExtraDirective
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010053}
54
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020055type state struct {
Lorenz Brun878f5f92020-05-12 16:15:39 +020056 apiserverLogs *logbuffer.LogBuffer
57 controllerManagerLogs *logbuffer.LogBuffer
58 schedulerLogs *logbuffer.LogBuffer
59 kubeletLogs *logbuffer.LogBuffer
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010060}
61
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020062type Service struct {
63 c Config
64 stateMu sync.Mutex
65 state *state
66}
Serge Bazanskidbfc6382020-06-19 20:35:43 +020067
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020068func New(c Config) *Service {
69 s := &Service{
70 c: c,
71 }
72 return s
73}
74
75func (s *Service) getState() *state {
76 s.stateMu.Lock()
77 defer s.stateMu.Unlock()
78 return s.state
79}
80
81func (s *Service) Run(ctx context.Context) error {
82 st := &state{
Lorenz Brun878f5f92020-05-12 16:15:39 +020083 apiserverLogs: logbuffer.New(5000, 16384),
84 controllerManagerLogs: logbuffer.New(5000, 16384),
85 schedulerLogs: logbuffer.New(5000, 16384),
86 kubeletLogs: logbuffer.New(5000, 16384),
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020087 }
88 s.stateMu.Lock()
89 s.state = st
90 s.stateMu.Unlock()
Serge Bazanskidbfc6382020-06-19 20:35:43 +020091
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020092 controllerManagerConfig, err := getPKIControllerManagerConfig(ctx, s.c.KPKI)
93 if err != nil {
94 return fmt.Errorf("could not generate controller manager pki config: %w", err)
95 }
96 controllerManagerConfig.clusterNet = s.c.ClusterNet
97 schedulerConfig, err := getPKISchedulerConfig(ctx, s.c.KPKI)
98 if err != nil {
99 return fmt.Errorf("could not generate scheduler pki config: %w", err)
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100100 }
Serge Bazanskidbfc6382020-06-19 20:35:43 +0200101
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200102 masterKubeconfig, err := s.c.KPKI.Kubeconfig(ctx, pki.Master)
103 if err != nil {
104 return fmt.Errorf("could not generate master kubeconfig: %w", err)
105 }
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100106
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200107 rawClientConfig, err := clientcmd.NewClientConfigFromBytes(masterKubeconfig)
108 if err != nil {
109 return fmt.Errorf("could not generate kubernetes client config: %w", err)
110 }
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100111
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200112 clientConfig, err := rawClientConfig.ClientConfig()
113 clientSet, err := kubernetes.NewForConfig(clientConfig)
114 if err != nil {
115 return fmt.Errorf("could not generate kubernetes client: %w", err)
116 }
117
118 informerFactory := informers.NewSharedInformerFactory(clientSet, 5*time.Minute)
119
120 hostname, err := os.Hostname()
121 if err != nil {
122 return fmt.Errorf("failed to get hostname: %w", err)
123 }
124
Lorenz Brun339582b2020-07-29 18:13:35 +0200125 dnsHostIP := s.c.AdvertiseAddress // TODO: Which IP to use
126
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200127 apiserver := &apiserverService{
128 KPKI: s.c.KPKI,
129 AdvertiseAddress: s.c.AdvertiseAddress,
130 ServiceIPRange: s.c.ServiceIPRange,
131 Output: st.apiserverLogs,
132 EphemeralConsensusDirectory: &s.c.Root.Ephemeral.Consensus,
133 }
134
135 kubelet := kubeletService{
136 NodeName: hostname,
Lorenz Brun339582b2020-07-29 18:13:35 +0200137 ClusterDNS: []net.IP{dnsHostIP},
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200138 KubeletDirectory: &s.c.Root.Data.Kubernetes.Kubelet,
139 EphemeralDirectory: &s.c.Root.Ephemeral,
140 Output: st.kubeletLogs,
141 KPKI: s.c.KPKI,
142 }
143
144 csiPlugin := csiPluginServer{
145 KubeletDirectory: &s.c.Root.Data.Kubernetes.Kubelet,
146 VolumesDirectory: &s.c.Root.Data.Volumes,
147 }
148
149 csiProvisioner := csiProvisionerServer{
150 NodeName: hostname,
151 Kubernetes: clientSet,
152 InformerFactory: informerFactory,
153 VolumesDirectory: &s.c.Root.Data.Volumes,
154 }
155
156 clusternet := clusternet.Service{
157 NodeName: hostname,
158 Kubernetes: clientSet,
159 ClusterNet: s.c.ClusterNet,
160 InformerFactory: informerFactory,
161 DataDirectory: &s.c.Root.Data.Kubernetes.ClusterNetworking,
162 }
163
Lorenz Brunb682ba52020-07-08 14:51:36 +0200164 nfproxy := nfproxy.Service{
165 ClusterCIDR: s.c.ClusterNet,
166 ClientSet: clientSet,
167 }
168
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200169 for _, sub := range []struct {
170 name string
171 runnable supervisor.Runnable
172 }{
173 {"apiserver", apiserver.Run},
174 {"controller-manager", runControllerManager(*controllerManagerConfig, st.controllerManagerLogs)},
175 {"scheduler", runScheduler(*schedulerConfig, st.schedulerLogs)},
176 {"kubelet", kubelet.Run},
177 {"reconciler", reconciler.Run(clientSet)},
178 {"csi-plugin", csiPlugin.Run},
179 {"csi-provisioner", csiProvisioner.Run},
180 {"clusternet", clusternet.Run},
Lorenz Brunb682ba52020-07-08 14:51:36 +0200181 {"nfproxy", nfproxy.Run},
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200182 } {
183 err := supervisor.Run(ctx, sub.name, sub.runnable)
184 if err != nil {
185 return fmt.Errorf("could not run sub-service %q: %w", sub.name, err)
186 }
187 }
188
Lorenz Brunfa5c2fc2020-09-28 13:32:12 +0200189 supervisor.Logger(ctx).Info("Registering K8s CoreDNS")
190 clusterDNSDirective := dns.NewKubernetesDirective("cluster.local", masterKubeconfig)
191 s.c.CorednsRegistrationChan <- clusterDNSDirective
192
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200193 supervisor.Signal(ctx, supervisor.SignalHealthy)
Lorenz Brunfa5c2fc2020-09-28 13:32:12 +0200194 <-ctx.Done()
195 s.c.CorednsRegistrationChan <- dns.CancelDirective(clusterDNSDirective)
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200196 return nil
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100197}
198
Lorenz Brun878f5f92020-05-12 16:15:39 +0200199// GetComponentLogs grabs logs from various Kubernetes binaries
200func (s *Service) GetComponentLogs(component string, n int) ([]string, error) {
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200201 s.stateMu.Lock()
202 defer s.stateMu.Unlock()
203 if s.state == nil {
204 return nil, errors.New("kubernetes not started yet")
205 }
206
Lorenz Brun878f5f92020-05-12 16:15:39 +0200207 switch component {
208 case "apiserver":
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200209 return s.state.apiserverLogs.ReadLinesTruncated(n, "..."), nil
Lorenz Brun878f5f92020-05-12 16:15:39 +0200210 case "controller-manager":
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200211 return s.state.controllerManagerLogs.ReadLinesTruncated(n, "..."), nil
Lorenz Brun878f5f92020-05-12 16:15:39 +0200212 case "scheduler":
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200213 return s.state.schedulerLogs.ReadLinesTruncated(n, "..."), nil
Lorenz Brun878f5f92020-05-12 16:15:39 +0200214 case "kubelet":
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200215 return s.state.kubeletLogs.ReadLinesTruncated(n, "..."), nil
Lorenz Brun878f5f92020-05-12 16:15:39 +0200216 default:
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200217 return nil, errors.New("component not available")
Lorenz Brun878f5f92020-05-12 16:15:39 +0200218 }
219}
220
221// GetDebugKubeconfig issues a kubeconfig for an arbitrary given identity. Useful for debugging and testing.
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200222func (s *Service) GetDebugKubeconfig(ctx context.Context, request *apb.GetDebugKubeconfigRequest) (*apb.GetDebugKubeconfigResponse, error) {
223 ca := s.c.KPKI.Certificates[pki.IdCA]
224 debugKubeconfig, err := pki.New(ca, "", pki.Client(request.Id, request.Groups)).Kubeconfig(ctx, s.c.KPKI.KV)
Lorenz Brun878f5f92020-05-12 16:15:39 +0200225 if err != nil {
226 return nil, status.Errorf(codes.Unavailable, "Failed to generate kubeconfig: %v", err)
227 }
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200228 return &apb.GetDebugKubeconfigResponse{DebugKubeconfig: string(debugKubeconfig)}, nil
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100229}