blob: 23960669661c756f374abc8ec0156de4ee10cdde [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"
Serge Bazanskidbfc6382020-06-19 20:35:43 +020036 "git.monogon.dev/source/nexantic.git/core/internal/kubernetes/pki"
Serge Bazanskie6030f62020-06-03 17:52:59 +020037 "git.monogon.dev/source/nexantic.git/core/internal/kubernetes/reconciler"
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020038 "git.monogon.dev/source/nexantic.git/core/internal/localstorage"
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +020039 "git.monogon.dev/source/nexantic.git/core/pkg/logbuffer"
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020040 apb "git.monogon.dev/source/nexantic.git/core/proto/api"
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010041)
42
43type Config struct {
44 AdvertiseAddress net.IP
45 ServiceIPRange net.IPNet
46 ClusterNet net.IPNet
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020047
48 KPKI *pki.KubernetesPKI
49 Root *localstorage.Root
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010050}
51
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020052type state struct {
Lorenz Brun878f5f92020-05-12 16:15:39 +020053 apiserverLogs *logbuffer.LogBuffer
54 controllerManagerLogs *logbuffer.LogBuffer
55 schedulerLogs *logbuffer.LogBuffer
56 kubeletLogs *logbuffer.LogBuffer
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010057}
58
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020059type Service struct {
60 c Config
61 stateMu sync.Mutex
62 state *state
63}
Serge Bazanskidbfc6382020-06-19 20:35:43 +020064
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020065func New(c Config) *Service {
66 s := &Service{
67 c: c,
68 }
69 return s
70}
71
72func (s *Service) getState() *state {
73 s.stateMu.Lock()
74 defer s.stateMu.Unlock()
75 return s.state
76}
77
78func (s *Service) Run(ctx context.Context) error {
79 st := &state{
Lorenz Brun878f5f92020-05-12 16:15:39 +020080 apiserverLogs: logbuffer.New(5000, 16384),
81 controllerManagerLogs: logbuffer.New(5000, 16384),
82 schedulerLogs: logbuffer.New(5000, 16384),
83 kubeletLogs: logbuffer.New(5000, 16384),
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020084 }
85 s.stateMu.Lock()
86 s.state = st
87 s.stateMu.Unlock()
Serge Bazanskidbfc6382020-06-19 20:35:43 +020088
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020089 controllerManagerConfig, err := getPKIControllerManagerConfig(ctx, s.c.KPKI)
90 if err != nil {
91 return fmt.Errorf("could not generate controller manager pki config: %w", err)
92 }
93 controllerManagerConfig.clusterNet = s.c.ClusterNet
94 schedulerConfig, err := getPKISchedulerConfig(ctx, s.c.KPKI)
95 if err != nil {
96 return fmt.Errorf("could not generate scheduler pki config: %w", err)
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010097 }
Serge Bazanskidbfc6382020-06-19 20:35:43 +020098
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020099 masterKubeconfig, err := s.c.KPKI.Kubeconfig(ctx, pki.Master)
100 if err != nil {
101 return fmt.Errorf("could not generate master kubeconfig: %w", err)
102 }
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100103
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200104 rawClientConfig, err := clientcmd.NewClientConfigFromBytes(masterKubeconfig)
105 if err != nil {
106 return fmt.Errorf("could not generate kubernetes client config: %w", err)
107 }
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100108
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200109 clientConfig, err := rawClientConfig.ClientConfig()
110 clientSet, err := kubernetes.NewForConfig(clientConfig)
111 if err != nil {
112 return fmt.Errorf("could not generate kubernetes client: %w", err)
113 }
114
115 informerFactory := informers.NewSharedInformerFactory(clientSet, 5*time.Minute)
116
117 hostname, err := os.Hostname()
118 if err != nil {
119 return fmt.Errorf("failed to get hostname: %w", err)
120 }
121
122 apiserver := &apiserverService{
123 KPKI: s.c.KPKI,
124 AdvertiseAddress: s.c.AdvertiseAddress,
125 ServiceIPRange: s.c.ServiceIPRange,
126 Output: st.apiserverLogs,
127 EphemeralConsensusDirectory: &s.c.Root.Ephemeral.Consensus,
128 }
129
130 kubelet := kubeletService{
131 NodeName: hostname,
132 ClusterDNS: nil,
133 KubeletDirectory: &s.c.Root.Data.Kubernetes.Kubelet,
134 EphemeralDirectory: &s.c.Root.Ephemeral,
135 Output: st.kubeletLogs,
136 KPKI: s.c.KPKI,
137 }
138
139 csiPlugin := csiPluginServer{
140 KubeletDirectory: &s.c.Root.Data.Kubernetes.Kubelet,
141 VolumesDirectory: &s.c.Root.Data.Volumes,
142 }
143
144 csiProvisioner := csiProvisionerServer{
145 NodeName: hostname,
146 Kubernetes: clientSet,
147 InformerFactory: informerFactory,
148 VolumesDirectory: &s.c.Root.Data.Volumes,
149 }
150
151 clusternet := clusternet.Service{
152 NodeName: hostname,
153 Kubernetes: clientSet,
154 ClusterNet: s.c.ClusterNet,
155 InformerFactory: informerFactory,
156 DataDirectory: &s.c.Root.Data.Kubernetes.ClusterNetworking,
157 }
158
159 for _, sub := range []struct {
160 name string
161 runnable supervisor.Runnable
162 }{
163 {"apiserver", apiserver.Run},
164 {"controller-manager", runControllerManager(*controllerManagerConfig, st.controllerManagerLogs)},
165 {"scheduler", runScheduler(*schedulerConfig, st.schedulerLogs)},
166 {"kubelet", kubelet.Run},
167 {"reconciler", reconciler.Run(clientSet)},
168 {"csi-plugin", csiPlugin.Run},
169 {"csi-provisioner", csiProvisioner.Run},
170 {"clusternet", clusternet.Run},
171 } {
172 err := supervisor.Run(ctx, sub.name, sub.runnable)
173 if err != nil {
174 return fmt.Errorf("could not run sub-service %q: %w", sub.name, err)
175 }
176 }
177
178 supervisor.Signal(ctx, supervisor.SignalHealthy)
179 supervisor.Signal(ctx, supervisor.SignalDone)
180 return nil
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100181}
182
Lorenz Brun878f5f92020-05-12 16:15:39 +0200183// GetComponentLogs grabs logs from various Kubernetes binaries
184func (s *Service) GetComponentLogs(component string, n int) ([]string, error) {
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200185 s.stateMu.Lock()
186 defer s.stateMu.Unlock()
187 if s.state == nil {
188 return nil, errors.New("kubernetes not started yet")
189 }
190
Lorenz Brun878f5f92020-05-12 16:15:39 +0200191 switch component {
192 case "apiserver":
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200193 return s.state.apiserverLogs.ReadLinesTruncated(n, "..."), nil
Lorenz Brun878f5f92020-05-12 16:15:39 +0200194 case "controller-manager":
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200195 return s.state.controllerManagerLogs.ReadLinesTruncated(n, "..."), nil
Lorenz Brun878f5f92020-05-12 16:15:39 +0200196 case "scheduler":
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200197 return s.state.schedulerLogs.ReadLinesTruncated(n, "..."), nil
Lorenz Brun878f5f92020-05-12 16:15:39 +0200198 case "kubelet":
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200199 return s.state.kubeletLogs.ReadLinesTruncated(n, "..."), nil
Lorenz Brun878f5f92020-05-12 16:15:39 +0200200 default:
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200201 return nil, errors.New("component not available")
Lorenz Brun878f5f92020-05-12 16:15:39 +0200202 }
203}
204
205// GetDebugKubeconfig issues a kubeconfig for an arbitrary given identity. Useful for debugging and testing.
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200206func (s *Service) GetDebugKubeconfig(ctx context.Context, request *apb.GetDebugKubeconfigRequest) (*apb.GetDebugKubeconfigResponse, error) {
207 ca := s.c.KPKI.Certificates[pki.IdCA]
208 debugKubeconfig, err := pki.New(ca, "", pki.Client(request.Id, request.Groups)).Kubeconfig(ctx, s.c.KPKI.KV)
Lorenz Brun878f5f92020-05-12 16:15:39 +0200209 if err != nil {
210 return nil, status.Errorf(codes.Unavailable, "Failed to generate kubeconfig: %v", err)
211 }
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200212 return &apb.GetDebugKubeconfigResponse{DebugKubeconfig: string(debugKubeconfig)}, nil
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100213}