Skip to content

Commit db71941

Browse files
committed
make Kubelet bootstrap certificate signal aware
1 parent 86096ad commit db71941

File tree

4 files changed

+36
-25
lines changed

4 files changed

+36
-25
lines changed

cmd/kubelet/app/server.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -256,12 +256,12 @@ HTTP server: The kubelet can also listen for HTTP and respond to a simple API
256256
// add the kubelet config controller to kubeletDeps
257257
kubeletDeps.KubeletConfigController = kubeletConfigController
258258

259-
// set up stopCh here in order to be reused by kubelet and docker shim
260-
stopCh := genericapiserver.SetupSignalHandler()
259+
// set up signal context here in order to be reused by kubelet and docker shim
260+
ctx := genericapiserver.SetupSignalContext()
261261

262262
// run the kubelet
263263
klog.V(5).Infof("KubeletConfiguration: %#v", kubeletServer.KubeletConfiguration)
264-
if err := Run(kubeletServer, kubeletDeps, utilfeature.DefaultFeatureGate, stopCh); err != nil {
264+
if err := Run(ctx, kubeletServer, kubeletDeps, utilfeature.DefaultFeatureGate); err != nil {
265265
klog.Fatal(err)
266266
}
267267
},
@@ -403,7 +403,7 @@ func UnsecuredDependencies(s *options.KubeletServer, featureGate featuregate.Fea
403403
// The kubeDeps argument may be nil - if so, it is initialized from the settings on KubeletServer.
404404
// Otherwise, the caller is assumed to have set up the Dependencies object and a default one will
405405
// not be generated.
406-
func Run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, featureGate featuregate.FeatureGate, stopCh <-chan struct{}) error {
406+
func Run(ctx context.Context, s *options.KubeletServer, kubeDeps *kubelet.Dependencies, featureGate featuregate.FeatureGate) error {
407407
logOption := logs.NewOptions()
408408
logOption.LogFormat = s.Logging.Format
409409
logOption.Apply()
@@ -412,7 +412,7 @@ func Run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, featureGate f
412412
if err := initForOS(s.KubeletFlags.WindowsService); err != nil {
413413
return fmt.Errorf("failed OS init: %v", err)
414414
}
415-
if err := run(s, kubeDeps, featureGate, stopCh); err != nil {
415+
if err := run(ctx, s, kubeDeps, featureGate); err != nil {
416416
return fmt.Errorf("failed to run Kubelet: %v", err)
417417
}
418418
return nil
@@ -469,7 +469,7 @@ func makeEventRecorder(kubeDeps *kubelet.Dependencies, nodeName types.NodeName)
469469
}
470470
}
471471

472-
func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, featureGate featuregate.FeatureGate, stopCh <-chan struct{}) (err error) {
472+
func run(ctx context.Context, s *options.KubeletServer, kubeDeps *kubelet.Dependencies, featureGate featuregate.FeatureGate) (err error) {
473473
// Set global feature gates based on the value on the initial KubeletServer
474474
err = utilfeature.DefaultMutableFeatureGate.SetFromMap(s.KubeletConfiguration.FeatureGates)
475475
if err != nil {
@@ -552,7 +552,7 @@ func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, featureGate f
552552
klog.Warningf("standalone mode, no API client")
553553

554554
case kubeDeps.KubeClient == nil, kubeDeps.EventClient == nil, kubeDeps.HeartbeatClient == nil:
555-
clientConfig, closeAllConns, err := buildKubeletClientConfig(s, nodeName)
555+
clientConfig, closeAllConns, err := buildKubeletClientConfig(ctx, s, nodeName)
556556
if err != nil {
557557
return err
558558
}
@@ -597,7 +597,7 @@ func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, featureGate f
597597
return err
598598
}
599599
kubeDeps.Auth = auth
600-
runAuthenticatorCAReload(stopCh)
600+
runAuthenticatorCAReload(ctx.Done())
601601
}
602602

603603
var cgroupRoots []string
@@ -799,7 +799,7 @@ func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, featureGate f
799799
select {
800800
case <-done:
801801
break
802-
case <-stopCh:
802+
case <-ctx.Done():
803803
break
804804
}
805805

@@ -808,7 +808,7 @@ func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, featureGate f
808808

809809
// buildKubeletClientConfig constructs the appropriate client config for the kubelet depending on whether
810810
// bootstrapping is enabled or client certificate rotation is enabled.
811-
func buildKubeletClientConfig(s *options.KubeletServer, nodeName types.NodeName) (*restclient.Config, func(), error) {
811+
func buildKubeletClientConfig(ctx context.Context, s *options.KubeletServer, nodeName types.NodeName) (*restclient.Config, func(), error) {
812812
if s.RotateCertificates {
813813
// Rules for client rotation and the handling of kube config files:
814814
//
@@ -878,7 +878,7 @@ func buildKubeletClientConfig(s *options.KubeletServer, nodeName types.NodeName)
878878
}
879879

880880
if len(s.BootstrapKubeconfig) > 0 {
881-
if err := bootstrap.LoadClientCert(s.KubeConfig, s.BootstrapKubeconfig, s.CertDirectory, nodeName); err != nil {
881+
if err := bootstrap.LoadClientCert(ctx, s.KubeConfig, s.BootstrapKubeconfig, s.CertDirectory, nodeName); err != nil {
882882
return nil, nil, err
883883
}
884884
}

pkg/kubelet/certificate/bootstrap/bootstrap.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func LoadClientConfig(kubeconfigPath, bootstrapPath, certDir string) (certConfig
105105
// The kubeconfig at bootstrapPath is used to request a client certificate from the API server.
106106
// On success, a kubeconfig file referencing the generated key and obtained certificate is written to kubeconfigPath.
107107
// The certificate and key file are stored in certDir.
108-
func LoadClientCert(kubeconfigPath, bootstrapPath, certDir string, nodeName types.NodeName) error {
108+
func LoadClientCert(ctx context.Context, kubeconfigPath, bootstrapPath, certDir string, nodeName types.NodeName) error {
109109
// Short-circuit if the kubeconfig file exists and is valid.
110110
ok, err := isClientConfigStillValid(kubeconfigPath)
111111
if err != nil {
@@ -156,11 +156,11 @@ func LoadClientCert(kubeconfigPath, bootstrapPath, certDir string, nodeName type
156156
}
157157
}
158158

159-
if err := waitForServer(*bootstrapClientConfig, 1*time.Minute); err != nil {
159+
if err := waitForServer(ctx, *bootstrapClientConfig, 1*time.Minute); err != nil {
160160
klog.Warningf("Error waiting for apiserver to come up: %v", err)
161161
}
162162

163-
certData, err := requestNodeCertificate(bootstrapClient, keyData, nodeName)
163+
certData, err := requestNodeCertificate(ctx, bootstrapClient, keyData, nodeName)
164164
if err != nil {
165165
return err
166166
}
@@ -278,20 +278,20 @@ func verifyKeyData(data []byte) bool {
278278
return err == nil
279279
}
280280

281-
func waitForServer(cfg restclient.Config, deadline time.Duration) error {
281+
func waitForServer(ctx context.Context, cfg restclient.Config, deadline time.Duration) error {
282282
cfg.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
283283
cfg.Timeout = 1 * time.Second
284284
cli, err := restclient.UnversionedRESTClientFor(&cfg)
285285
if err != nil {
286286
return fmt.Errorf("couldn't create client: %v", err)
287287
}
288288

289-
ctx, cancel := context.WithTimeout(context.TODO(), deadline)
289+
ctx, cancel := context.WithTimeout(ctx, deadline)
290290
defer cancel()
291291

292292
var connected bool
293293
wait.JitterUntil(func() {
294-
if _, err := cli.Get().AbsPath("/healthz").Do(context.TODO()).Raw(); err != nil {
294+
if _, err := cli.Get().AbsPath("/healthz").Do(ctx).Raw(); err != nil {
295295
klog.Infof("Failed to connect to apiserver: %v", err)
296296
return
297297
}
@@ -312,7 +312,7 @@ func waitForServer(cfg restclient.Config, deadline time.Duration) error {
312312
// certificate (pem-encoded). If there is any errors, or the watch timeouts, it
313313
// will return an error. This is intended for use on nodes (kubelet and
314314
// kubeadm).
315-
func requestNodeCertificate(client clientset.Interface, privateKeyData []byte, nodeName types.NodeName) (certData []byte, err error) {
315+
func requestNodeCertificate(ctx context.Context, client clientset.Interface, privateKeyData []byte, nodeName types.NodeName) (certData []byte, err error) {
316316
subject := &pkix.Name{
317317
Organization: []string{"system:nodes"},
318318
CommonName: "system:node:" + string(nodeName),
@@ -349,7 +349,7 @@ func requestNodeCertificate(client clientset.Interface, privateKeyData []byte, n
349349
return nil, err
350350
}
351351

352-
ctx, cancel := context.WithTimeout(context.Background(), 3600*time.Second)
352+
ctx, cancel := context.WithTimeout(ctx, 3600*time.Second)
353353
defer cancel()
354354

355355
klog.V(2).Infof("Waiting for client certificate to be issued")

pkg/kubelet/certificate/bootstrap/bootstrap_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package bootstrap
1818

1919
import (
20+
"context"
2021
"fmt"
2122
"io/ioutil"
2223
"os"
@@ -95,7 +96,7 @@ users:
9596
}
9697

9798
func TestRequestNodeCertificateNoKeyData(t *testing.T) {
98-
certData, err := requestNodeCertificate(newClientset(fakeClient{}), []byte{}, "fake-node-name")
99+
certData, err := requestNodeCertificate(context.TODO(), newClientset(fakeClient{}), []byte{}, "fake-node-name")
99100
if err == nil {
100101
t.Errorf("Got no error, wanted error an error because there was an empty private key passed in.")
101102
}
@@ -113,7 +114,7 @@ func TestRequestNodeCertificateErrorCreatingCSR(t *testing.T) {
113114
t.Fatalf("Unable to generate a new private key: %v", err)
114115
}
115116

116-
certData, err := requestNodeCertificate(client, privateKeyData, "fake-node-name")
117+
certData, err := requestNodeCertificate(context.TODO(), client, privateKeyData, "fake-node-name")
117118
if err == nil {
118119
t.Errorf("Got no error, wanted error an error because client.Create failed.")
119120
}
@@ -128,7 +129,7 @@ func TestRequestNodeCertificate(t *testing.T) {
128129
t.Fatalf("Unable to generate a new private key: %v", err)
129130
}
130131

131-
certData, err := requestNodeCertificate(newClientset(fakeClient{}), privateKeyData, "fake-node-name")
132+
certData, err := requestNodeCertificate(context.TODO(), newClientset(fakeClient{}), privateKeyData, "fake-node-name")
132133
if err != nil {
133134
t.Errorf("Got %v, wanted no error.", err)
134135
}

staging/src/k8s.io/apiserver/pkg/server/signal.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package server
1818

1919
import (
20+
"context"
2021
"os"
2122
"os/signal"
2223
)
@@ -27,21 +28,30 @@ var shutdownHandler chan os.Signal
2728
// SetupSignalHandler registered for SIGTERM and SIGINT. A stop channel is returned
2829
// which is closed on one of these signals. If a second signal is caught, the program
2930
// is terminated with exit code 1.
31+
// Only one of SetupSignalContext and SetupSignalHandler should be called, and only can
32+
// be called once.
3033
func SetupSignalHandler() <-chan struct{} {
34+
return SetupSignalContext().Done()
35+
}
36+
37+
// SetupSignalContext is same as SetupSignalHandler, but a context.Context is returned.
38+
// Only one of SetupSignalContext and SetupSignalHandler should be called, and only can
39+
// be called once.
40+
func SetupSignalContext() context.Context {
3141
close(onlyOneSignalHandler) // panics when called twice
3242

3343
shutdownHandler = make(chan os.Signal, 2)
3444

35-
stop := make(chan struct{})
45+
ctx, cancel := context.WithCancel(context.Background())
3646
signal.Notify(shutdownHandler, shutdownSignals...)
3747
go func() {
3848
<-shutdownHandler
39-
close(stop)
49+
cancel()
4050
<-shutdownHandler
4151
os.Exit(1) // second signal. Exit directly.
4252
}()
4353

44-
return stop
54+
return ctx
4555
}
4656

4757
// RequestShutdown emulates a received event that is considered as shutdown signal (SIGTERM/SIGINT)

0 commit comments

Comments
 (0)