blob: a22b6b9a3ea553ce51707db4756c38bde6b74627 [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 Brun8e3b8fc2020-05-19 14:29:40 +020040 "git.monogon.dev/source/nexantic.git/core/pkg/logbuffer"
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020041 apb "git.monogon.dev/source/nexantic.git/core/proto/api"
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010042)
43
44type Config struct {
45 AdvertiseAddress net.IP
46 ServiceIPRange net.IPNet
47 ClusterNet net.IPNet
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020048
49 KPKI *pki.KubernetesPKI
50 Root *localstorage.Root
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010051}
52
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020053type state struct {
Lorenz Brun878f5f92020-05-12 16:15:39 +020054 apiserverLogs *logbuffer.LogBuffer
55 controllerManagerLogs *logbuffer.LogBuffer
56 schedulerLogs *logbuffer.LogBuffer
57 kubeletLogs *logbuffer.LogBuffer
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010058}
59
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020060type Service struct {
61 c Config
62 stateMu sync.Mutex
63 state *state
64}
Serge Bazanskidbfc6382020-06-19 20:35:43 +020065
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020066func New(c Config) *Service {
67 s := &Service{
68 c: c,
69 }
70 return s
71}
72
73func (s *Service) getState() *state {
74 s.stateMu.Lock()
75 defer s.stateMu.Unlock()
76 return s.state
77}
78
79func (s *Service) Run(ctx context.Context) error {
80 st := &state{
Lorenz Brun878f5f92020-05-12 16:15:39 +020081 apiserverLogs: logbuffer.New(5000, 16384),
82 controllerManagerLogs: logbuffer.New(5000, 16384),
83 schedulerLogs: logbuffer.New(5000, 16384),
84 kubeletLogs: logbuffer.New(5000, 16384),
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020085 }
86 s.stateMu.Lock()
87 s.state = st
88 s.stateMu.Unlock()
Serge Bazanskidbfc6382020-06-19 20:35:43 +020089
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020090 controllerManagerConfig, err := getPKIControllerManagerConfig(ctx, s.c.KPKI)
91 if err != nil {
92 return fmt.Errorf("could not generate controller manager pki config: %w", err)
93 }
94 controllerManagerConfig.clusterNet = s.c.ClusterNet
95 schedulerConfig, err := getPKISchedulerConfig(ctx, s.c.KPKI)
96 if err != nil {
97 return fmt.Errorf("could not generate scheduler pki config: %w", err)
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010098 }
Serge Bazanskidbfc6382020-06-19 20:35:43 +020099
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200100 masterKubeconfig, err := s.c.KPKI.Kubeconfig(ctx, pki.Master)
101 if err != nil {
102 return fmt.Errorf("could not generate master kubeconfig: %w", err)
103 }
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100104
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200105 rawClientConfig, err := clientcmd.NewClientConfigFromBytes(masterKubeconfig)
106 if err != nil {
107 return fmt.Errorf("could not generate kubernetes client config: %w", err)
108 }
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100109
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200110 clientConfig, err := rawClientConfig.ClientConfig()
111 clientSet, err := kubernetes.NewForConfig(clientConfig)
112 if err != nil {
113 return fmt.Errorf("could not generate kubernetes client: %w", err)
114 }
115
116 informerFactory := informers.NewSharedInformerFactory(clientSet, 5*time.Minute)
117
118 hostname, err := os.Hostname()
119 if err != nil {
120 return fmt.Errorf("failed to get hostname: %w", err)
121 }
122
123 apiserver := &apiserverService{
124 KPKI: s.c.KPKI,
125 AdvertiseAddress: s.c.AdvertiseAddress,
126 ServiceIPRange: s.c.ServiceIPRange,
127 Output: st.apiserverLogs,
128 EphemeralConsensusDirectory: &s.c.Root.Ephemeral.Consensus,
129 }
130
131 kubelet := kubeletService{
132 NodeName: hostname,
133 ClusterDNS: nil,
134 KubeletDirectory: &s.c.Root.Data.Kubernetes.Kubelet,
135 EphemeralDirectory: &s.c.Root.Ephemeral,
136 Output: st.kubeletLogs,
137 KPKI: s.c.KPKI,
138 }
139
140 csiPlugin := csiPluginServer{
141 KubeletDirectory: &s.c.Root.Data.Kubernetes.Kubelet,
142 VolumesDirectory: &s.c.Root.Data.Volumes,
143 }
144
145 csiProvisioner := csiProvisionerServer{
146 NodeName: hostname,
147 Kubernetes: clientSet,
148 InformerFactory: informerFactory,
149 VolumesDirectory: &s.c.Root.Data.Volumes,
150 }
151
152 clusternet := clusternet.Service{
153 NodeName: hostname,
154 Kubernetes: clientSet,
155 ClusterNet: s.c.ClusterNet,
156 InformerFactory: informerFactory,
157 DataDirectory: &s.c.Root.Data.Kubernetes.ClusterNetworking,
158 }
159
Lorenz Brunb682ba52020-07-08 14:51:36 +0200160 nfproxy := nfproxy.Service{
161 ClusterCIDR: s.c.ClusterNet,
162 ClientSet: clientSet,
163 }
164
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200165 for _, sub := range []struct {
166 name string
167 runnable supervisor.Runnable
168 }{
169 {"apiserver", apiserver.Run},
170 {"controller-manager", runControllerManager(*controllerManagerConfig, st.controllerManagerLogs)},
171 {"scheduler", runScheduler(*schedulerConfig, st.schedulerLogs)},
172 {"kubelet", kubelet.Run},
173 {"reconciler", reconciler.Run(clientSet)},
174 {"csi-plugin", csiPlugin.Run},
175 {"csi-provisioner", csiProvisioner.Run},
176 {"clusternet", clusternet.Run},
Lorenz Brunb682ba52020-07-08 14:51:36 +0200177 {"nfproxy", nfproxy.Run},
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200178 } {
179 err := supervisor.Run(ctx, sub.name, sub.runnable)
180 if err != nil {
181 return fmt.Errorf("could not run sub-service %q: %w", sub.name, err)
182 }
183 }
184
185 supervisor.Signal(ctx, supervisor.SignalHealthy)
186 supervisor.Signal(ctx, supervisor.SignalDone)
187 return nil
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100188}
189
Lorenz Brun878f5f92020-05-12 16:15:39 +0200190// GetComponentLogs grabs logs from various Kubernetes binaries
191func (s *Service) GetComponentLogs(component string, n int) ([]string, error) {
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200192 s.stateMu.Lock()
193 defer s.stateMu.Unlock()
194 if s.state == nil {
195 return nil, errors.New("kubernetes not started yet")
196 }
197
Lorenz Brun878f5f92020-05-12 16:15:39 +0200198 switch component {
199 case "apiserver":
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200200 return s.state.apiserverLogs.ReadLinesTruncated(n, "..."), nil
Lorenz Brun878f5f92020-05-12 16:15:39 +0200201 case "controller-manager":
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200202 return s.state.controllerManagerLogs.ReadLinesTruncated(n, "..."), nil
Lorenz Brun878f5f92020-05-12 16:15:39 +0200203 case "scheduler":
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200204 return s.state.schedulerLogs.ReadLinesTruncated(n, "..."), nil
Lorenz Brun878f5f92020-05-12 16:15:39 +0200205 case "kubelet":
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200206 return s.state.kubeletLogs.ReadLinesTruncated(n, "..."), nil
Lorenz Brun878f5f92020-05-12 16:15:39 +0200207 default:
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200208 return nil, errors.New("component not available")
Lorenz Brun878f5f92020-05-12 16:15:39 +0200209 }
210}
211
212// GetDebugKubeconfig issues a kubeconfig for an arbitrary given identity. Useful for debugging and testing.
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200213func (s *Service) GetDebugKubeconfig(ctx context.Context, request *apb.GetDebugKubeconfigRequest) (*apb.GetDebugKubeconfigResponse, error) {
214 ca := s.c.KPKI.Certificates[pki.IdCA]
215 debugKubeconfig, err := pki.New(ca, "", pki.Client(request.Id, request.Groups)).Kubeconfig(ctx, s.c.KPKI.KV)
Lorenz Brun878f5f92020-05-12 16:15:39 +0200216 if err != nil {
217 return nil, status.Errorf(codes.Unavailable, "Failed to generate kubeconfig: %v", err)
218 }
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200219 return &apb.GetDebugKubeconfigResponse{DebugKubeconfig: string(debugKubeconfig)}, nil
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100220}