mirror of
https://github.com/kubernetes/client-go.git
synced 2025-09-20 10:39:46 +00:00
Have the certificate manager decide if the server is healthy
Prevent a Kubelet from shutting down when the server isn't responding to us but we cannot get a new certificate. This allows a cluster to coast if the master is unresponsive or a node is partitioned and their client cert expires. Kubernetes-commit: b3a11aa635022761637090f4fc8d5cb57f3f0010
This commit is contained in:
committed by
Kubernetes Publisher
parent
8c1471aeda
commit
f7a735a8c2
@@ -27,7 +27,10 @@ import (
|
||||
"time"
|
||||
|
||||
certificates "k8s.io/api/certificates/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
certificatesclient "k8s.io/client-go/kubernetes/typed/certificates/v1beta1"
|
||||
)
|
||||
@@ -288,6 +291,7 @@ func TestRotateCertWaitingForResultError(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
certificateWaitBackoff = wait.Backoff{Steps: 1}
|
||||
if success, err := m.rotateCerts(); success {
|
||||
t.Errorf("Got success from 'rotateCerts', wanted failure.")
|
||||
} else if err != nil {
|
||||
@@ -612,11 +616,170 @@ func TestInitializeOtherRESTClients(t *testing.T) {
|
||||
} else {
|
||||
m.setRotationDeadline()
|
||||
if m.shouldRotate() {
|
||||
if success, err := certificateManager.(*manager).rotateCerts(); !success {
|
||||
t.Errorf("Got failure from 'rotateCerts', expected success")
|
||||
} else if err != nil {
|
||||
success, err := certificateManager.(*manager).rotateCerts()
|
||||
if err != nil {
|
||||
t.Errorf("Got error %v, expected none.", err)
|
||||
return
|
||||
}
|
||||
if !success {
|
||||
t.Errorf("Unexpected response 'rotateCerts': %t", success)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
certificate = certificateManager.Current()
|
||||
if !certificatesEqual(certificate, tc.expectedCertAfterStart.certificate) {
|
||||
t.Errorf("Got %v, wanted %v", certificateString(certificate), certificateString(tc.expectedCertAfterStart.certificate))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerHealth(t *testing.T) {
|
||||
type certs struct {
|
||||
storeCert *certificateData
|
||||
bootstrapCert *certificateData
|
||||
apiCert *certificateData
|
||||
expectedCertBeforeStart *certificateData
|
||||
expectedCertAfterStart *certificateData
|
||||
}
|
||||
|
||||
updatedCerts := certs{
|
||||
storeCert: storeCertData,
|
||||
bootstrapCert: bootstrapCertData,
|
||||
apiCert: apiServerCertData,
|
||||
expectedCertBeforeStart: storeCertData,
|
||||
expectedCertAfterStart: apiServerCertData,
|
||||
}
|
||||
|
||||
currentCerts := certs{
|
||||
storeCert: storeCertData,
|
||||
bootstrapCert: bootstrapCertData,
|
||||
apiCert: apiServerCertData,
|
||||
expectedCertBeforeStart: storeCertData,
|
||||
expectedCertAfterStart: storeCertData,
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
certs
|
||||
|
||||
failureType fakeClientFailureType
|
||||
clientErr error
|
||||
|
||||
expectRotateFail bool
|
||||
expectHealthy bool
|
||||
}{
|
||||
{
|
||||
description: "Current certificate, bootstrap certificate",
|
||||
certs: updatedCerts,
|
||||
expectHealthy: true,
|
||||
},
|
||||
{
|
||||
description: "Generic error on create",
|
||||
certs: currentCerts,
|
||||
|
||||
failureType: createError,
|
||||
expectRotateFail: true,
|
||||
},
|
||||
{
|
||||
description: "Unauthorized error on create",
|
||||
certs: currentCerts,
|
||||
|
||||
failureType: createError,
|
||||
clientErr: errors.NewUnauthorized("unauthorized"),
|
||||
expectRotateFail: true,
|
||||
expectHealthy: true,
|
||||
},
|
||||
{
|
||||
description: "Generic unauthorized error on create",
|
||||
certs: currentCerts,
|
||||
|
||||
failureType: createError,
|
||||
clientErr: errors.NewGenericServerResponse(401, "POST", schema.GroupResource{}, "", "", 0, true),
|
||||
expectRotateFail: true,
|
||||
expectHealthy: true,
|
||||
},
|
||||
{
|
||||
description: "Generic not found error on create",
|
||||
certs: currentCerts,
|
||||
|
||||
failureType: createError,
|
||||
clientErr: errors.NewGenericServerResponse(404, "POST", schema.GroupResource{}, "", "", 0, true),
|
||||
expectRotateFail: true,
|
||||
expectHealthy: false,
|
||||
},
|
||||
{
|
||||
description: "Not found error on create",
|
||||
certs: currentCerts,
|
||||
|
||||
failureType: createError,
|
||||
clientErr: errors.NewGenericServerResponse(404, "POST", schema.GroupResource{}, "", "", 0, false),
|
||||
expectRotateFail: true,
|
||||
expectHealthy: true,
|
||||
},
|
||||
{
|
||||
description: "Conflict error on watch",
|
||||
certs: currentCerts,
|
||||
|
||||
failureType: watchError,
|
||||
clientErr: errors.NewGenericServerResponse(409, "POST", schema.GroupResource{}, "", "", 0, false),
|
||||
expectRotateFail: true,
|
||||
expectHealthy: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
certificateStore := &fakeStore{
|
||||
cert: tc.storeCert.certificate,
|
||||
}
|
||||
|
||||
certificateManager, err := NewManager(&Config{
|
||||
Template: &x509.CertificateRequest{
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"system:nodes"},
|
||||
CommonName: "system:node:fake-node-name",
|
||||
},
|
||||
},
|
||||
Usages: []certificates.KeyUsage{
|
||||
certificates.UsageDigitalSignature,
|
||||
certificates.UsageKeyEncipherment,
|
||||
certificates.UsageClientAuth,
|
||||
},
|
||||
CertificateStore: certificateStore,
|
||||
BootstrapCertificatePEM: tc.bootstrapCert.certificatePEM,
|
||||
BootstrapKeyPEM: tc.bootstrapCert.keyPEM,
|
||||
CertificateSigningRequestClient: &fakeClient{
|
||||
certificatePEM: tc.apiCert.certificatePEM,
|
||||
failureType: tc.failureType,
|
||||
err: tc.clientErr,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Got %v, wanted no error.", err)
|
||||
}
|
||||
|
||||
certificate := certificateManager.Current()
|
||||
if !certificatesEqual(certificate, tc.expectedCertBeforeStart.certificate) {
|
||||
t.Errorf("Got %v, wanted %v", certificateString(certificate), certificateString(tc.expectedCertBeforeStart.certificate))
|
||||
}
|
||||
|
||||
if _, ok := certificateManager.(*manager); !ok {
|
||||
t.Errorf("Expected a '*manager' from 'NewManager'")
|
||||
} else {
|
||||
success, err := certificateManager.(*manager).rotateCerts()
|
||||
if err != nil {
|
||||
t.Errorf("Got error %v, expected none.", err)
|
||||
return
|
||||
}
|
||||
if !success != tc.expectRotateFail {
|
||||
t.Errorf("Unexpected response 'rotateCerts': %t", success)
|
||||
return
|
||||
}
|
||||
if actual := certificateManager.(*manager).ServerHealthy(); actual != tc.expectHealthy {
|
||||
t.Errorf("Unexpected manager server health: %t", actual)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -641,10 +804,14 @@ type fakeClient struct {
|
||||
certificatesclient.CertificateSigningRequestInterface
|
||||
failureType fakeClientFailureType
|
||||
certificatePEM []byte
|
||||
err error
|
||||
}
|
||||
|
||||
func (c fakeClient) List(opts v1.ListOptions) (*certificates.CertificateSigningRequestList, error) {
|
||||
if c.failureType == watchError {
|
||||
if c.err != nil {
|
||||
return nil, c.err
|
||||
}
|
||||
return nil, fmt.Errorf("Watch error")
|
||||
}
|
||||
csrReply := certificates.CertificateSigningRequestList{
|
||||
@@ -657,6 +824,9 @@ func (c fakeClient) List(opts v1.ListOptions) (*certificates.CertificateSigningR
|
||||
|
||||
func (c fakeClient) Create(*certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, error) {
|
||||
if c.failureType == createError {
|
||||
if c.err != nil {
|
||||
return nil, c.err
|
||||
}
|
||||
return nil, fmt.Errorf("Create error")
|
||||
}
|
||||
csrReply := certificates.CertificateSigningRequest{}
|
||||
@@ -666,6 +836,9 @@ func (c fakeClient) Create(*certificates.CertificateSigningRequest) (*certificat
|
||||
|
||||
func (c fakeClient) Watch(opts v1.ListOptions) (watch.Interface, error) {
|
||||
if c.failureType == watchError {
|
||||
if c.err != nil {
|
||||
return nil, c.err
|
||||
}
|
||||
return nil, fmt.Errorf("Watch error")
|
||||
}
|
||||
return &fakeWatch{
|
||||
|
Reference in New Issue
Block a user