1
16
17 package app
18
19 import (
20 "context"
21 goflag "flag"
22 "fmt"
23 "time"
24
25 "github.com/spf13/cobra"
26 "github.com/spf13/pflag"
27 oteltrace "go.opentelemetry.io/otel/trace"
28 internalapi "k8s.io/cri-api/pkg/apis"
29 "k8s.io/klog/v2"
30
31 v1 "k8s.io/api/core/v1"
32 "k8s.io/apimachinery/pkg/api/resource"
33 "k8s.io/apimachinery/pkg/types"
34 "k8s.io/apimachinery/pkg/util/sets"
35 clientset "k8s.io/client-go/kubernetes"
36 restclient "k8s.io/client-go/rest"
37 "k8s.io/client-go/tools/clientcmd"
38 "k8s.io/client-go/tools/events"
39 cliflag "k8s.io/component-base/cli/flag"
40 _ "k8s.io/component-base/metrics/prometheus/restclient"
41 _ "k8s.io/component-base/metrics/prometheus/version"
42 "k8s.io/component-base/version"
43 "k8s.io/component-base/version/verflag"
44 "k8s.io/kubernetes/pkg/api/legacyscheme"
45 "k8s.io/kubernetes/pkg/cluster/ports"
46 cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing"
47 "k8s.io/kubernetes/pkg/kubelet/certificate/bootstrap"
48 "k8s.io/kubernetes/pkg/kubelet/cm"
49 "k8s.io/kubernetes/pkg/kubelet/cri/remote"
50 fakeremote "k8s.io/kubernetes/pkg/kubelet/cri/remote/fake"
51 "k8s.io/kubernetes/pkg/kubemark"
52 kubemarkproxy "k8s.io/kubernetes/pkg/proxy/kubemark"
53 utilflag "k8s.io/kubernetes/pkg/util/flag"
54 )
55
56 type hollowNodeConfig struct {
57 KubeconfigPath string
58 BootstrapKubeconfigPath string
59 CertDirectory string
60 KubeletPort int
61 KubeletReadOnlyPort int
62 Morph string
63 NodeName string
64 ServerPort int
65 ContentType string
66 NodeLabels map[string]string
67 RegisterWithTaints []v1.Taint
68 MaxPods int
69 ExtendedResources map[string]string
70 UseHostImageService bool
71
72
73 UseRealProxier bool
74 ProxierSyncPeriod time.Duration
75 ProxierMinSyncPeriod time.Duration
76 }
77
78 const (
79 maxPods = 110
80 podsPerCore = 0
81 )
82
83
84
85 var knownMorphs = sets.NewString("kubelet", "proxy")
86
87 func (c *hollowNodeConfig) addFlags(fs *pflag.FlagSet) {
88 fs.StringVar(&c.KubeconfigPath, "kubeconfig", "/kubeconfig/kubeconfig", "Path to kubeconfig file.")
89 fs.StringVar(&c.BootstrapKubeconfigPath, "bootstrap-kubeconfig", "", "Path to bootstrap kubeconfig file.")
90 fs.StringVar(&c.CertDirectory, "cert-dir", "/etc/srv/", "Path to cert directory for bootstraping.")
91 fs.IntVar(&c.KubeletPort, "kubelet-port", ports.KubeletPort, "Port on which HollowKubelet should be listening.")
92 fs.IntVar(&c.KubeletReadOnlyPort, "kubelet-read-only-port", ports.KubeletReadOnlyPort, "Read-only port on which Kubelet is listening.")
93 fs.StringVar(&c.NodeName, "name", "fake-node", "Name of this Hollow Node.")
94 fs.IntVar(&c.ServerPort, "api-server-port", 443, "Port on which API server is listening.")
95 fs.StringVar(&c.Morph, "morph", "", fmt.Sprintf("Specifies into which Hollow component this binary should morph. Allowed values: %v", knownMorphs.List()))
96 fs.StringVar(&c.ContentType, "kube-api-content-type", "application/vnd.kubernetes.protobuf", "ContentType of requests sent to apiserver.")
97 bindableNodeLabels := cliflag.ConfigurationMap(c.NodeLabels)
98 fs.Var(&bindableNodeLabels, "node-labels", "Additional node labels")
99 fs.Var(utilflag.RegisterWithTaintsVar{Value: &c.RegisterWithTaints}, "register-with-taints", "Register the node with the given list of taints (comma separated \"<key>=<value>:<effect>\"). No-op if register-node is false.")
100 fs.IntVar(&c.MaxPods, "max-pods", maxPods, "Number of pods that can run on this Kubelet.")
101 bindableExtendedResources := cliflag.ConfigurationMap(c.ExtendedResources)
102 fs.Var(&bindableExtendedResources, "extended-resources", "Register the node with extended resources (comma separated \"<name>=<quantity>\")")
103 fs.BoolVar(&c.UseHostImageService, "use-host-image-service", true, "Set to true if the hollow-kubelet should use the host image service. If set to false the fake image service will be used")
104
105 fs.BoolVar(&c.UseRealProxier, "use-real-proxier", true, "Has no effect.")
106 _ = fs.MarkDeprecated("use-real-proxier", "This flag is deprecated and will be removed in a future release.")
107 fs.DurationVar(&c.ProxierSyncPeriod, "proxier-sync-period", 30*time.Second, "Has no effect.")
108 _ = fs.MarkDeprecated("proxier-sync-period", "This flag is deprecated and will be removed in a future release.")
109 fs.DurationVar(&c.ProxierMinSyncPeriod, "proxier-min-sync-period", 0, "Has no effect.")
110 _ = fs.MarkDeprecated("proxier-min-sync-period", "This flag is deprecated and will be removed in a future release.")
111 }
112
113 func (c *hollowNodeConfig) createClientConfigFromFile() (*restclient.Config, error) {
114 clientConfig, err := clientcmd.LoadFromFile(c.KubeconfigPath)
115 if err != nil {
116 return nil, fmt.Errorf("error while loading kubeconfig from file %v: %v", c.KubeconfigPath, err)
117 }
118 config, err := clientcmd.NewDefaultClientConfig(*clientConfig, &clientcmd.ConfigOverrides{}).ClientConfig()
119 if err != nil {
120 return nil, fmt.Errorf("error while creating kubeconfig: %v", err)
121 }
122 config.ContentType = c.ContentType
123 config.QPS = 10
124 config.Burst = 20
125 return config, nil
126 }
127
128 func (c *hollowNodeConfig) bootstrapClientConfig() error {
129 if c.BootstrapKubeconfigPath != "" {
130 return bootstrap.LoadClientCert(context.TODO(), c.KubeconfigPath, c.BootstrapKubeconfigPath, c.CertDirectory, types.NodeName(c.NodeName))
131 }
132 return nil
133 }
134
135 func (c *hollowNodeConfig) createHollowKubeletOptions() *kubemark.HollowKubeletOptions {
136 return &kubemark.HollowKubeletOptions{
137 NodeName: c.NodeName,
138 KubeletPort: c.KubeletPort,
139 KubeletReadOnlyPort: c.KubeletReadOnlyPort,
140 MaxPods: c.MaxPods,
141 PodsPerCore: podsPerCore,
142 NodeLabels: c.NodeLabels,
143 RegisterWithTaints: c.RegisterWithTaints,
144 }
145 }
146
147
148 func NewHollowNodeCommand() *cobra.Command {
149 s := &hollowNodeConfig{
150 NodeLabels: make(map[string]string),
151 ExtendedResources: make(map[string]string),
152 }
153
154 cmd := &cobra.Command{
155 Use: "kubemark",
156 Long: "kubemark",
157 RunE: func(cmd *cobra.Command, args []string) error {
158 verflag.PrintAndExitIfRequested()
159 cliflag.PrintFlags(cmd.Flags())
160 return run(s)
161 },
162 Args: func(cmd *cobra.Command, args []string) error {
163 for _, arg := range args {
164 if len(arg) > 0 {
165 return fmt.Errorf("%q does not take any arguments, got %q", cmd.CommandPath(), args)
166 }
167 }
168 return nil
169 },
170 }
171
172 fs := cmd.Flags()
173 fs.AddGoFlagSet(goflag.CommandLine)
174 s.addFlags(fs)
175
176 return cmd
177 }
178
179 func run(config *hollowNodeConfig) error {
180
181 klog.Infof("Version: %+v", version.Get())
182
183 if !knownMorphs.Has(config.Morph) {
184 return fmt.Errorf("Unknown morph: %v. allowed values: %v", config.Morph, knownMorphs.List())
185 }
186
187
188 err := config.bootstrapClientConfig()
189 if err != nil {
190 return fmt.Errorf("Failed to bootstrap, error: %w. Exiting", err)
191 }
192 clientConfig, err := config.createClientConfigFromFile()
193 if err != nil {
194 return fmt.Errorf("Failed to create a ClientConfig, error: %w. Exiting", err)
195 }
196
197 if config.Morph == "kubelet" {
198 clientConfig.UserAgent = "hollow-kubelet"
199 client, err := clientset.NewForConfig(clientConfig)
200 if err != nil {
201 return fmt.Errorf("Failed to create a ClientSet, error: %w. Exiting", err)
202 }
203
204 f, c := kubemark.GetHollowKubeletConfig(config.createHollowKubeletOptions())
205
206 heartbeatClientConfig := *clientConfig
207 heartbeatClientConfig.Timeout = c.NodeStatusUpdateFrequency.Duration
208
209 leaseTimeout := time.Duration(c.NodeLeaseDurationSeconds) * time.Second
210 if heartbeatClientConfig.Timeout > leaseTimeout {
211 heartbeatClientConfig.Timeout = leaseTimeout
212 }
213
214 heartbeatClientConfig.QPS = float32(-1)
215 heartbeatClient, err := clientset.NewForConfig(&heartbeatClientConfig)
216 if err != nil {
217 return fmt.Errorf("Failed to create a ClientSet, error: %w. Exiting", err)
218 }
219
220 cadvisorInterface := &cadvisortest.Fake{
221 NodeName: config.NodeName,
222 }
223
224 var containerManager cm.ContainerManager
225 if config.ExtendedResources != nil {
226 extendedResources := v1.ResourceList{}
227 for k, v := range config.ExtendedResources {
228 extendedResources[v1.ResourceName(k)] = resource.MustParse(v)
229 }
230
231 containerManager = cm.NewStubContainerManagerWithDevicePluginResource(extendedResources)
232 } else {
233 containerManager = cm.NewStubContainerManager()
234 }
235
236 endpoint, err := fakeremote.GenerateEndpoint()
237 if err != nil {
238 return fmt.Errorf("Failed to generate fake endpoint, error: %w", err)
239 }
240 fakeRemoteRuntime := fakeremote.NewFakeRemoteRuntime()
241 if err = fakeRemoteRuntime.Start(endpoint); err != nil {
242 return fmt.Errorf("Failed to start fake runtime, error: %w", err)
243 }
244 defer fakeRemoteRuntime.Stop()
245 runtimeService, err := remote.NewRemoteRuntimeService(endpoint, 15*time.Second, oteltrace.NewNoopTracerProvider())
246 if err != nil {
247 return fmt.Errorf("Failed to init runtime service, error: %w", err)
248 }
249
250 var imageService internalapi.ImageManagerService = fakeRemoteRuntime.ImageService
251 if config.UseHostImageService {
252 imageService, err = remote.NewRemoteImageService(c.ImageServiceEndpoint, 15*time.Second, oteltrace.NewNoopTracerProvider())
253 if err != nil {
254 return fmt.Errorf("Failed to init image service, error: %w", err)
255 }
256 }
257
258 hollowKubelet := kubemark.NewHollowKubelet(
259 f, c,
260 client,
261 heartbeatClient,
262 cadvisorInterface,
263 imageService,
264 runtimeService,
265 containerManager,
266 )
267 hollowKubelet.Run()
268 }
269
270 if config.Morph == "proxy" {
271 clientConfig.UserAgent = "hollow-proxy"
272
273 client, err := clientset.NewForConfig(clientConfig)
274 if err != nil {
275 return fmt.Errorf("Failed to create API Server client, error: %w", err)
276 }
277 eventBroadcaster := events.NewBroadcaster(&events.EventSinkImpl{Interface: client.EventsV1()})
278 recorder := eventBroadcaster.NewRecorder(legacyscheme.Scheme, "kube-proxy")
279
280 hollowProxy := kubemarkproxy.NewHollowProxy(
281 config.NodeName,
282 client,
283 client.CoreV1(),
284 eventBroadcaster,
285 recorder,
286 )
287 return hollowProxy.Run()
288 }
289
290 return nil
291 }
292
View as plain text