-
Notifications
You must be signed in to change notification settings - Fork 90
/
kubelet.go
159 lines (130 loc) · 4.39 KB
/
kubelet.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package cluster
import (
"bytes"
"context"
"encoding/base64"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"github.com/pkg/errors"
)
const (
kubeletConfig = `apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
evictionHard:
memory.available: "200Mi"
featureGates:
KubeletInUserNamespace: true
`
kubeletKubeconfigFilename = "kubelet-kubeconfig.yaml"
kubeletConfigFilename = "config.yaml"
)
// startKubelet will (spwan) a kubelet process that's basically unsupervised
// we need to think about resiliency and supervision for this kubelet before this can be reliable
// we do this instead of a systemd service for kubelet because we don't want to ask for sudo/root
func startKubelet(ctx context.Context, dataDir string) error {
// make a kubelet directory under datadir
kubeletDir := filepath.Join(dataDir, "kubelet")
if _, err := os.Stat(kubeletDir); os.IsNotExist(err) {
if err := os.MkdirAll(kubeletDir, 0755); err != nil {
return errors.Wrap(err, "mkdir kubelet")
}
}
// TODO check the version of kubelet in case there was an old version
// write the kubelet config file
if err := writeKubeletConfig(kubeletDir); err != nil {
return errors.Wrap(err, "write kubelet config")
}
// write the kubelet kubeconfig file
if err := writeKubeletKubeconfig(dataDir, kubeletDir); err != nil {
return errors.Wrap(err, "write kubelet kubeconfig")
}
// spawn the kubelet process
if err := spawnKubelet(dataDir, kubeletDir); err != nil {
return errors.Wrap(err, "spawn kubelet")
}
return nil
}
func spawnKubelet(dataDir string, kubeletDir string) error {
go func() {
args := []string{
fmt.Sprintf("--kubeconfig=%s", filepath.Join(kubeletDir, kubeletKubeconfigFilename)),
fmt.Sprintf("--config=%s", filepath.Join(kubeletDir, kubeletConfigFilename)),
"--container-runtime=remote",
fmt.Sprintf("--container-runtime-endpoint=unix://%s/containerd/containerd.sock", dataDir),
fmt.Sprintf("--root-dir=%s", kubeletDir),
fmt.Sprintf("--cert-dir=%s", filepath.Join(kubeletDir, "pki")),
}
cmd := exec.Command(filepath.Join(BinRoot(dataDir), "kubelet"), args...)
cmd.Env = os.Environ() // TODO
// TODO stream the output of stdout and stderr to files
// TODO stream the output of stdout and stderr to files
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
fmt.Printf("%s\n", stderr.String())
panic(err)
}
fmt.Printf("%s\n", stdout.String())
}()
return nil
}
// writeKubeletKubeconfig will write a file named kubelet-kubeconfig.yaml
// to the kubelet dir. this is the kubeconfig that the kubelet will use to
// connect to the api server. it's not using a bootstrap token yet, this is
// using a static token that we JIT provision when writing this file
func writeKubeletKubeconfig(dataDir string, kubeletDir string) error {
b, err := getKubeletKubeconfig(dataDir)
if err != nil {
return errors.Wrap(err, "get kubelet bootstrap config")
}
if err := ioutil.WriteFile(filepath.Join(kubeletDir, kubeletKubeconfigFilename), b, 0644); err != nil {
return errors.Wrap(err, "write kubelet kubeconfig")
}
return nil
}
// getKubeletKubeconfig will return the kubeconfig used by the kubelet
func getKubeletKubeconfig(dataDir string) ([]byte, error) {
certFile, err := caCertFilePath(dataDir)
if err != nil {
return nil, errors.Wrap(err, "get cert file path")
}
data, err := ioutil.ReadFile(certFile)
if err != nil {
return nil, errors.Wrap(err, "read cert file")
}
encodedCert := base64.StdEncoding.EncodeToString(data)
bootstrapToken := "NOT_VALID" // TODO
b := fmt.Sprintf(`apiVersion: v1
clusters:
- name: kubernetes
cluster:
certificate-authority-data: %s
server: "https://localhost:8443"
contexts:
- name: tls-bootstrap-token-user@kubernetes
context:
cluster: kubernetes
user: tls-bootstrap-token-user
current-context: tls-bootstrap-token-user@kubernetes
kind: Config
preferences: {}
users:
- name: tls-bootstrap-token-user
user:
token: %s`, encodedCert, bootstrapToken)
return []byte(b), nil
}
// writeKubeletConfig writes the current kubelet config to a file to pass in
// when spawning the kubelet process.
func writeKubeletConfig(kubeletDir string) error {
if err := ioutil.WriteFile(filepath.Join(kubeletDir, kubeletConfigFilename), []byte(kubeletConfig), 0644); err != nil {
return errors.Wrap(err, "write kubelet config")
}
return nil
}