blob: 55c20bf9c403cafc01e00a62357aa6c8eb1fbd1a [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 Brun339582b2020-07-29 18:13:35 +020036 "git.monogon.dev/source/nexantic.git/core/internal/kubernetes/dns"
Lorenz Brunb682ba52020-07-08 14:51:36 +020037 "git.monogon.dev/source/nexantic.git/core/internal/kubernetes/nfproxy"
Serge Bazanskidbfc6382020-06-19 20:35:43 +020038 "git.monogon.dev/source/nexantic.git/core/internal/kubernetes/pki"
Serge Bazanskie6030f62020-06-03 17:52:59 +020039 "git.monogon.dev/source/nexantic.git/core/internal/kubernetes/reconciler"
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020040 "git.monogon.dev/source/nexantic.git/core/internal/localstorage"
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
50 KPKI *pki.KubernetesPKI
51 Root *localstorage.Root
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010052}
53
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020054type state struct {
Lorenz Brun878f5f92020-05-12 16:15:39 +020055 apiserverLogs *logbuffer.LogBuffer
56 controllerManagerLogs *logbuffer.LogBuffer
57 schedulerLogs *logbuffer.LogBuffer
58 kubeletLogs *logbuffer.LogBuffer
Lorenz Brun339582b2020-07-29 18:13:35 +020059 corednsLogs *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),
Lorenz Brun339582b2020-07-29 18:13:35 +020087 corednsLogs: logbuffer.New(5000, 16384),
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020088 }
89 s.stateMu.Lock()
90 s.state = st
91 s.stateMu.Unlock()
Serge Bazanskidbfc6382020-06-19 20:35:43 +020092
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020093 controllerManagerConfig, err := getPKIControllerManagerConfig(ctx, s.c.KPKI)
94 if err != nil {
95 return fmt.Errorf("could not generate controller manager pki config: %w", err)
96 }
97 controllerManagerConfig.clusterNet = s.c.ClusterNet
98 schedulerConfig, err := getPKISchedulerConfig(ctx, s.c.KPKI)
99 if err != nil {
100 return fmt.Errorf("could not generate scheduler pki config: %w", err)
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100101 }
Serge Bazanskidbfc6382020-06-19 20:35:43 +0200102
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200103 masterKubeconfig, err := s.c.KPKI.Kubeconfig(ctx, pki.Master)
104 if err != nil {
105 return fmt.Errorf("could not generate master kubeconfig: %w", err)
106 }
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100107
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200108 rawClientConfig, err := clientcmd.NewClientConfigFromBytes(masterKubeconfig)
109 if err != nil {
110 return fmt.Errorf("could not generate kubernetes client config: %w", err)
111 }
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100112
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200113 clientConfig, err := rawClientConfig.ClientConfig()
114 clientSet, err := kubernetes.NewForConfig(clientConfig)
115 if err != nil {
116 return fmt.Errorf("could not generate kubernetes client: %w", err)
117 }
118
119 informerFactory := informers.NewSharedInformerFactory(clientSet, 5*time.Minute)
120
121 hostname, err := os.Hostname()
122 if err != nil {
123 return fmt.Errorf("failed to get hostname: %w", err)
124 }
125
Lorenz Brun339582b2020-07-29 18:13:35 +0200126 dnsHostIP := s.c.AdvertiseAddress // TODO: Which IP to use
127
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200128 apiserver := &apiserverService{
129 KPKI: s.c.KPKI,
130 AdvertiseAddress: s.c.AdvertiseAddress,
131 ServiceIPRange: s.c.ServiceIPRange,
132 Output: st.apiserverLogs,
133 EphemeralConsensusDirectory: &s.c.Root.Ephemeral.Consensus,
134 }
135
136 kubelet := kubeletService{
137 NodeName: hostname,
Lorenz Brun339582b2020-07-29 18:13:35 +0200138 ClusterDNS: []net.IP{dnsHostIP},
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200139 KubeletDirectory: &s.c.Root.Data.Kubernetes.Kubelet,
140 EphemeralDirectory: &s.c.Root.Ephemeral,
141 Output: st.kubeletLogs,
142 KPKI: s.c.KPKI,
143 }
144
145 csiPlugin := csiPluginServer{
146 KubeletDirectory: &s.c.Root.Data.Kubernetes.Kubelet,
147 VolumesDirectory: &s.c.Root.Data.Volumes,
148 }
149
150 csiProvisioner := csiProvisionerServer{
151 NodeName: hostname,
152 Kubernetes: clientSet,
153 InformerFactory: informerFactory,
154 VolumesDirectory: &s.c.Root.Data.Volumes,
155 }
156
157 clusternet := clusternet.Service{
158 NodeName: hostname,
159 Kubernetes: clientSet,
160 ClusterNet: s.c.ClusterNet,
161 InformerFactory: informerFactory,
162 DataDirectory: &s.c.Root.Data.Kubernetes.ClusterNetworking,
163 }
164
Lorenz Brunb682ba52020-07-08 14:51:36 +0200165 nfproxy := nfproxy.Service{
166 ClusterCIDR: s.c.ClusterNet,
167 ClientSet: clientSet,
168 }
169
Lorenz Brun339582b2020-07-29 18:13:35 +0200170 dns := dns.Service{
171 Kubeconfig: masterKubeconfig,
172 Output: s.state.corednsLogs,
173 ClusterDomain: "cluster.local", // Hardcode this here until we make this configurable
174 }
175
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200176 for _, sub := range []struct {
177 name string
178 runnable supervisor.Runnable
179 }{
180 {"apiserver", apiserver.Run},
181 {"controller-manager", runControllerManager(*controllerManagerConfig, st.controllerManagerLogs)},
182 {"scheduler", runScheduler(*schedulerConfig, st.schedulerLogs)},
183 {"kubelet", kubelet.Run},
184 {"reconciler", reconciler.Run(clientSet)},
185 {"csi-plugin", csiPlugin.Run},
186 {"csi-provisioner", csiProvisioner.Run},
187 {"clusternet", clusternet.Run},
Lorenz Brunb682ba52020-07-08 14:51:36 +0200188 {"nfproxy", nfproxy.Run},
Lorenz Brun339582b2020-07-29 18:13:35 +0200189 {"dns", dns.Run},
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200190 } {
191 err := supervisor.Run(ctx, sub.name, sub.runnable)
192 if err != nil {
193 return fmt.Errorf("could not run sub-service %q: %w", sub.name, err)
194 }
195 }
196
197 supervisor.Signal(ctx, supervisor.SignalHealthy)
198 supervisor.Signal(ctx, supervisor.SignalDone)
199 return nil
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100200}
201
Lorenz Brun878f5f92020-05-12 16:15:39 +0200202// GetComponentLogs grabs logs from various Kubernetes binaries
203func (s *Service) GetComponentLogs(component string, n int) ([]string, error) {
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200204 s.stateMu.Lock()
205 defer s.stateMu.Unlock()
206 if s.state == nil {
207 return nil, errors.New("kubernetes not started yet")
208 }
209
Lorenz Brun878f5f92020-05-12 16:15:39 +0200210 switch component {
211 case "apiserver":
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200212 return s.state.apiserverLogs.ReadLinesTruncated(n, "..."), nil
Lorenz Brun878f5f92020-05-12 16:15:39 +0200213 case "controller-manager":
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200214 return s.state.controllerManagerLogs.ReadLinesTruncated(n, "..."), nil
Lorenz Brun878f5f92020-05-12 16:15:39 +0200215 case "scheduler":
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200216 return s.state.schedulerLogs.ReadLinesTruncated(n, "..."), nil
Lorenz Brun878f5f92020-05-12 16:15:39 +0200217 case "kubelet":
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200218 return s.state.kubeletLogs.ReadLinesTruncated(n, "..."), nil
Lorenz Brun339582b2020-07-29 18:13:35 +0200219 case "coredns":
220 return s.state.corednsLogs.ReadLinesTruncated(n, "..."), nil
Lorenz Brun878f5f92020-05-12 16:15:39 +0200221 default:
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200222 return nil, errors.New("component not available")
Lorenz Brun878f5f92020-05-12 16:15:39 +0200223 }
224}
225
226// GetDebugKubeconfig issues a kubeconfig for an arbitrary given identity. Useful for debugging and testing.
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200227func (s *Service) GetDebugKubeconfig(ctx context.Context, request *apb.GetDebugKubeconfigRequest) (*apb.GetDebugKubeconfigResponse, error) {
228 ca := s.c.KPKI.Certificates[pki.IdCA]
229 debugKubeconfig, err := pki.New(ca, "", pki.Client(request.Id, request.Groups)).Kubeconfig(ctx, s.c.KPKI.KV)
Lorenz Brun878f5f92020-05-12 16:15:39 +0200230 if err != nil {
231 return nil, status.Errorf(codes.Unavailable, "Failed to generate kubeconfig: %v", err)
232 }
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200233 return &apb.GetDebugKubeconfigResponse{DebugKubeconfig: string(debugKubeconfig)}, nil
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100234}