blob: 1af760755cf03ed0cf9e2370d322652bc27db266 [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"
Serge Bazanskidbfc6382020-06-19 20:35:43 +020021 "fmt"
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010022 "net"
Lorenz Brunb15abad2020-04-16 11:17:12 +020023 "time"
24
Lorenz Brun878f5f92020-05-12 16:15:39 +020025 "google.golang.org/grpc/codes"
26 "google.golang.org/grpc/status"
Lorenz Brunf042e6f2020-06-24 16:46:09 +020027 "k8s.io/client-go/informers"
Lorenz Brunb15abad2020-04-16 11:17:12 +020028 "k8s.io/client-go/kubernetes"
Lorenz Brunf042e6f2020-06-24 16:46:09 +020029 "k8s.io/client-go/tools/clientcmd"
Lorenz Brun878f5f92020-05-12 16:15:39 +020030
Lorenz Brun1de8b182021-12-21 17:15:18 +010031 "source.monogon.dev/metropolis/node/core/identity"
Serge Bazanski31370b02021-01-07 16:31:14 +010032 "source.monogon.dev/metropolis/node/core/localstorage"
Serge Bazanskid8af5bf2021-03-16 13:38:29 +010033 "source.monogon.dev/metropolis/node/core/network"
Serge Bazanski31370b02021-01-07 16:31:14 +010034 "source.monogon.dev/metropolis/node/core/network/dns"
35 "source.monogon.dev/metropolis/node/kubernetes/clusternet"
36 "source.monogon.dev/metropolis/node/kubernetes/nfproxy"
37 "source.monogon.dev/metropolis/node/kubernetes/pki"
Lorenz Brun4e090352021-03-17 17:44:41 +010038 "source.monogon.dev/metropolis/node/kubernetes/plugins/kvmdevice"
Serge Bazanski31370b02021-01-07 16:31:14 +010039 "source.monogon.dev/metropolis/node/kubernetes/reconciler"
40 "source.monogon.dev/metropolis/pkg/supervisor"
41 apb "source.monogon.dev/metropolis/proto/api"
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010042)
43
44type Config struct {
Serge Bazanskid8af5bf2021-03-16 13:38:29 +010045 ServiceIPRange net.IPNet
46 ClusterNet net.IPNet
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020047
Serge Bazanskid8af5bf2021-03-16 13:38:29 +010048 KPKI *pki.PKI
49 Root *localstorage.Root
50 Network *network.Service
Lorenz Brun1de8b182021-12-21 17:15:18 +010051 Node *identity.Node
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010052}
53
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020054type Service struct {
Serge Bazanski967be212020-11-02 11:26:59 +010055 c Config
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020056}
Serge Bazanskidbfc6382020-06-19 20:35:43 +020057
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020058func New(c Config) *Service {
59 s := &Service{
60 c: c,
61 }
62 return s
63}
64
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020065func (s *Service) Run(ctx context.Context) error {
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020066 controllerManagerConfig, err := getPKIControllerManagerConfig(ctx, s.c.KPKI)
67 if err != nil {
68 return fmt.Errorf("could not generate controller manager pki config: %w", err)
69 }
70 controllerManagerConfig.clusterNet = s.c.ClusterNet
71 schedulerConfig, err := getPKISchedulerConfig(ctx, s.c.KPKI)
72 if err != nil {
73 return fmt.Errorf("could not generate scheduler pki config: %w", err)
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010074 }
Serge Bazanskidbfc6382020-06-19 20:35:43 +020075
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020076 masterKubeconfig, err := s.c.KPKI.Kubeconfig(ctx, pki.Master)
77 if err != nil {
78 return fmt.Errorf("could not generate master kubeconfig: %w", err)
79 }
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010080
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020081 rawClientConfig, err := clientcmd.NewClientConfigFromBytes(masterKubeconfig)
82 if err != nil {
83 return fmt.Errorf("could not generate kubernetes client config: %w", err)
84 }
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010085
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020086 clientConfig, err := rawClientConfig.ClientConfig()
87 clientSet, err := kubernetes.NewForConfig(clientConfig)
88 if err != nil {
89 return fmt.Errorf("could not generate kubernetes client: %w", err)
90 }
91
92 informerFactory := informers.NewSharedInformerFactory(clientSet, 5*time.Minute)
93
Serge Bazanskid8af5bf2021-03-16 13:38:29 +010094 // Sub-runnable which starts all parts of Kubernetes that depend on the
95 // machine's external IP address. If it changes, the runnable will exit.
96 // TODO(q3k): test this
97 supervisor.Run(ctx, "networked", func(ctx context.Context) error {
98 networkWatch := s.c.Network.Watch()
99 defer networkWatch.Close()
Lorenz Brun339582b2020-07-29 18:13:35 +0200100
Serge Bazanskid8af5bf2021-03-16 13:38:29 +0100101 var status *network.Status
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200102
Serge Bazanskid8af5bf2021-03-16 13:38:29 +0100103 supervisor.Logger(ctx).Info("Waiting for node networking...")
104 for status == nil || status.ExternalAddress == nil {
105 status, err = networkWatch.Get(ctx)
106 if err != nil {
107 return fmt.Errorf("failed to get network status: %w", err)
108 }
109 }
110 address := status.ExternalAddress
111 supervisor.Logger(ctx).Info("Node has active networking, starting apiserver/kubelet")
112
113 apiserver := &apiserverService{
114 KPKI: s.c.KPKI,
115 AdvertiseAddress: address,
116 ServiceIPRange: s.c.ServiceIPRange,
117 EphemeralConsensusDirectory: &s.c.Root.Ephemeral.Consensus,
118 }
119
120 kubelet := kubeletService{
Lorenz Brun1de8b182021-12-21 17:15:18 +0100121 NodeName: s.c.Node.ID(),
Serge Bazanskid8af5bf2021-03-16 13:38:29 +0100122 ClusterDNS: []net.IP{address},
123 KubeletDirectory: &s.c.Root.Data.Kubernetes.Kubelet,
124 EphemeralDirectory: &s.c.Root.Ephemeral,
125 KPKI: s.c.KPKI,
126 }
127
128 err := supervisor.RunGroup(ctx, map[string]supervisor.Runnable{
129 "apiserver": apiserver.Run,
130 "kubelet": kubelet.Run,
131 })
132 if err != nil {
133 return fmt.Errorf("when starting apiserver/kubelet: %w", err)
134 }
135
136 supervisor.Signal(ctx, supervisor.SignalHealthy)
137
138 for status.ExternalAddress.Equal(address) {
139 status, err = networkWatch.Get(ctx)
140 if err != nil {
141 return fmt.Errorf("when watching for network changes: %w", err)
142 }
143 }
144 return fmt.Errorf("network configuration changed (%s -> %s)", address.String(), status.ExternalAddress.String())
145 })
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200146
147 csiPlugin := csiPluginServer{
148 KubeletDirectory: &s.c.Root.Data.Kubernetes.Kubelet,
149 VolumesDirectory: &s.c.Root.Data.Volumes,
150 }
151
152 csiProvisioner := csiProvisionerServer{
Lorenz Brun1de8b182021-12-21 17:15:18 +0100153 NodeName: s.c.Node.ID(),
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200154 Kubernetes: clientSet,
155 InformerFactory: informerFactory,
156 VolumesDirectory: &s.c.Root.Data.Volumes,
157 }
158
159 clusternet := clusternet.Service{
Lorenz Brun1de8b182021-12-21 17:15:18 +0100160 NodeName: s.c.Node.ID(),
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200161 Kubernetes: clientSet,
162 ClusterNet: s.c.ClusterNet,
163 InformerFactory: informerFactory,
164 DataDirectory: &s.c.Root.Data.Kubernetes.ClusterNetworking,
165 }
166
Lorenz Brunb682ba52020-07-08 14:51:36 +0200167 nfproxy := nfproxy.Service{
168 ClusterCIDR: s.c.ClusterNet,
169 ClientSet: clientSet,
170 }
171
Lorenz Brun4e090352021-03-17 17:44:41 +0100172 kvmDevicePlugin := kvmdevice.Plugin{
173 KubeletDirectory: &s.c.Root.Data.Kubernetes.Kubelet,
174 }
175
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200176 for _, sub := range []struct {
177 name string
178 runnable supervisor.Runnable
179 }{
Serge Bazanski967be212020-11-02 11:26:59 +0100180 {"controller-manager", runControllerManager(*controllerManagerConfig)},
181 {"scheduler", runScheduler(*schedulerConfig)},
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200182 {"reconciler", reconciler.Run(clientSet)},
183 {"csi-plugin", csiPlugin.Run},
184 {"csi-provisioner", csiProvisioner.Run},
185 {"clusternet", clusternet.Run},
Lorenz Brunb682ba52020-07-08 14:51:36 +0200186 {"nfproxy", nfproxy.Run},
Lorenz Brun4e090352021-03-17 17:44:41 +0100187 {"kvmdeviceplugin", kvmDevicePlugin.Run},
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200188 } {
189 err := supervisor.Run(ctx, sub.name, sub.runnable)
190 if err != nil {
191 return fmt.Errorf("could not run sub-service %q: %w", sub.name, err)
192 }
193 }
194
Lorenz Brunfa5c2fc2020-09-28 13:32:12 +0200195 supervisor.Logger(ctx).Info("Registering K8s CoreDNS")
196 clusterDNSDirective := dns.NewKubernetesDirective("cluster.local", masterKubeconfig)
Serge Bazanskid8af5bf2021-03-16 13:38:29 +0100197 s.c.Network.ConfigureDNS(clusterDNSDirective)
Lorenz Brunfa5c2fc2020-09-28 13:32:12 +0200198
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200199 supervisor.Signal(ctx, supervisor.SignalHealthy)
Lorenz Brunfa5c2fc2020-09-28 13:32:12 +0200200 <-ctx.Done()
Serge Bazanskid8af5bf2021-03-16 13:38:29 +0100201 s.c.Network.ConfigureDNS(dns.CancelDirective(clusterDNSDirective))
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200202 return nil
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100203}
204
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200205// GetDebugKubeconfig issues a kubeconfig for an arbitrary given identity.
206// Useful for debugging and testing.
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200207func (s *Service) GetDebugKubeconfig(ctx context.Context, request *apb.GetDebugKubeconfigRequest) (*apb.GetDebugKubeconfigResponse, error) {
Serge Bazanski9411f7c2021-03-10 13:12:53 +0100208 client, err := s.c.KPKI.VolatileClient(ctx, request.Id, request.Groups)
209 if err != nil {
210 return nil, status.Errorf(codes.Unavailable, "Failed to get volatile client certificate: %v", err)
211 }
212 kubeconfig, err := pki.Kubeconfig(ctx, s.c.KPKI.KV, client)
Lorenz Brun878f5f92020-05-12 16:15:39 +0200213 if err != nil {
214 return nil, status.Errorf(codes.Unavailable, "Failed to generate kubeconfig: %v", err)
215 }
Serge Bazanski9411f7c2021-03-10 13:12:53 +0100216 return &apb.GetDebugKubeconfigResponse{DebugKubeconfig: string(kubeconfig)}, nil
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100217}