Skip to content

Commit a6d7920

Browse files
author
Dmitry Rozhkov
committed
kubeadm: do unit testing of actual public function
Even though CreateServiceAccountKeyAndPublicKeyFiles() function is an interface function it's not unittested. Instead it wraps a couple of internal functions which are used only inside CreateServiceAccountKeyAndPublicKeyFiles() and those internal functions are tested. Rewrite the function to do only what it's intended to do and add unit tests for it.
1 parent 6d691c9 commit a6d7920

File tree

3 files changed

+60
-109
lines changed

3 files changed

+60
-109
lines changed

cmd/kubeadm/app/phases/certs/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ go_library(
3939
"//cmd/kubeadm/app/constants:go_default_library",
4040
"//cmd/kubeadm/app/util/pkiutil:go_default_library",
4141
"//staging/src/k8s.io/client-go/util/cert:go_default_library",
42+
"//staging/src/k8s.io/client-go/util/keyutil:go_default_library",
4243
"//vendor/github.com/pkg/errors:go_default_library",
4344
"//vendor/k8s.io/klog:go_default_library",
4445
],

cmd/kubeadm/app/phases/certs/certs.go

Lines changed: 19 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525

2626
"github.com/pkg/errors"
2727
certutil "k8s.io/client-go/util/cert"
28+
"k8s.io/client-go/util/keyutil"
2829
"k8s.io/klog"
2930
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
3031
pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
@@ -67,27 +68,31 @@ func CreatePKIAssets(cfg *kubeadmapi.InitConfiguration) error {
6768
// If the sa public/private key files already exists in the target folder, they are used only if evaluated equals; otherwise an error is returned.
6869
func CreateServiceAccountKeyAndPublicKeyFiles(certsDir string) error {
6970
klog.V(1).Infoln("creating a new public/private key files for signing service account users")
70-
saSigningKey, err := NewServiceAccountSigningKey()
71+
_, err := keyutil.PrivateKeyFromFile(filepath.Join(certsDir, kubeadmconstants.ServiceAccountPrivateKeyName))
72+
if err == nil {
73+
// kubeadm doesn't validate the existing certificate key more than this;
74+
// Basically, if we find a key file with the same path kubeadm thinks those files
75+
// are equal and doesn't bother writing a new file
76+
fmt.Printf("[certs] Using the existing %q key\n", kubeadmconstants.ServiceAccountKeyBaseName)
77+
return nil
78+
} else if !os.IsNotExist(err) {
79+
return errors.Wrapf(err, "file %s existed but it could not be loaded properly", kubeadmconstants.ServiceAccountPrivateKeyName)
80+
}
81+
82+
// The key does NOT exist, let's generate it now
83+
key, err := pkiutil.NewPrivateKey()
7184
if err != nil {
7285
return err
7386
}
7487

75-
return writeKeyFilesIfNotExist(
76-
certsDir,
77-
kubeadmconstants.ServiceAccountKeyBaseName,
78-
saSigningKey,
79-
)
80-
}
88+
// Write .key and .pub files to disk
89+
fmt.Printf("[certs] Generating %q key and public key\n", kubeadmconstants.ServiceAccountKeyBaseName)
8190

82-
// NewServiceAccountSigningKey generate public/private key pairs for signing service account tokens.
83-
func NewServiceAccountSigningKey() (crypto.Signer, error) {
84-
// The key does NOT exist, let's generate it now
85-
saSigningKey, err := pkiutil.NewPrivateKey()
86-
if err != nil {
87-
return nil, errors.Wrap(err, "failure while creating service account token signing key")
91+
if err := pkiutil.WriteKey(certsDir, kubeadmconstants.ServiceAccountKeyBaseName, key); err != nil {
92+
return err
8893
}
8994

90-
return saSigningKey, nil
95+
return pkiutil.WritePublicKey(certsDir, kubeadmconstants.ServiceAccountKeyBaseName, key.Public())
9196
}
9297

9398
// CreateCACertAndKeyFiles generates and writes out a given certificate authority.
@@ -246,42 +251,6 @@ func writeCertificateFilesIfNotExist(pkiDir string, baseName string, signingCert
246251
return nil
247252
}
248253

249-
// writeKeyFilesIfNotExist write a new key to the given path.
250-
// If there already is a key file at the given path; kubeadm tries to load it and check if the values in the
251-
// existing and the expected key equals. If they do; kubeadm will just skip writing the file as it's up-to-date,
252-
// otherwise this function returns an error.
253-
func writeKeyFilesIfNotExist(pkiDir string, baseName string, key crypto.Signer) error {
254-
255-
// Checks if the key exists in the PKI directory
256-
if pkiutil.CertOrKeyExist(pkiDir, baseName) {
257-
258-
// Try to load .key from the PKI directory
259-
_, err := pkiutil.TryLoadKeyFromDisk(pkiDir, baseName)
260-
if err != nil {
261-
return errors.Wrapf(err, "%s key existed but it could not be loaded properly", baseName)
262-
}
263-
264-
// kubeadm doesn't validate the existing certificate key more than this;
265-
// Basically, if we find a key file with the same path kubeadm thinks those files
266-
// are equal and doesn't bother writing a new file
267-
fmt.Printf("[certs] Using the existing %q key\n", baseName)
268-
} else {
269-
270-
// Write .key and .pub files to disk
271-
fmt.Printf("[certs] Generating %q key and public key\n", baseName)
272-
273-
if err := pkiutil.WriteKey(pkiDir, baseName, key); err != nil {
274-
return errors.Wrapf(err, "failure while saving %s key", baseName)
275-
}
276-
277-
if err := pkiutil.WritePublicKey(pkiDir, baseName, key.Public()); err != nil {
278-
return errors.Wrapf(err, "failure while saving %s public key", baseName)
279-
}
280-
}
281-
282-
return nil
283-
}
284-
285254
// writeCSRFilesIfNotExist writes a new CSR to the given path.
286255
// If there already is a CSR file at the given path; kubeadm tries to load it and check if it's a valid certificate.
287256
// otherwise this function returns an error.

cmd/kubeadm/app/phases/certs/certs_test.go

Lines changed: 40 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -301,84 +301,65 @@ func TestWriteCSRFilesIfNotExist(t *testing.T) {
301301

302302
}
303303

304-
func TestWriteKeyFilesIfNotExist(t *testing.T) {
305-
306-
setupKey, _ := NewServiceAccountSigningKey()
307-
key, _ := NewServiceAccountSigningKey()
304+
func TestCreateServiceAccountKeyAndPublicKeyFiles(t *testing.T) {
305+
setupKey, err := keyutil.MakeEllipticPrivateKeyPEM()
306+
if err != nil {
307+
t.Fatalf("Can't setup test: %v", err)
308+
}
308309

309-
var tests = []struct {
310-
setupFunc func(pkiDir string) error
311-
expectedError bool
312-
expectedKey crypto.Signer
310+
tcases := []struct {
311+
name string
312+
setupFunc func(pkiDir string) error
313+
expectedErr bool
314+
expectedKey []byte
313315
}{
314316
{ // key does not exists > key written
315-
expectedKey: key,
317+
name: "generate successfully",
316318
},
317319
{ // key exists > existing key used
320+
name: "use existing key",
318321
setupFunc: func(pkiDir string) error {
319-
return writeKeyFilesIfNotExist(pkiDir, "dummy", setupKey)
322+
err := keyutil.WriteKey(filepath.Join(pkiDir, kubeadmconstants.ServiceAccountPrivateKeyName), setupKey)
323+
return err
320324
},
321325
expectedKey: setupKey,
322326
},
323327
{ // some file exists, but it is not a valid key > err
328+
name: "empty key",
324329
setupFunc: func(pkiDir string) error {
325-
testutil.SetupEmptyFiles(t, pkiDir, "dummy.key")
330+
testutil.SetupEmptyFiles(t, pkiDir, kubeadmconstants.ServiceAccountPrivateKeyName)
326331
return nil
327332
},
328-
expectedError: true,
333+
expectedErr: true,
329334
},
330335
}
336+
for _, tt := range tcases {
337+
t.Run(tt.name, func(t *testing.T) {
338+
dir := testutil.SetupTempDir(t)
339+
defer os.RemoveAll(dir)
331340

332-
for _, test := range tests {
333-
// Create temp folder for the test case
334-
tmpdir := testutil.SetupTempDir(t)
335-
defer os.RemoveAll(tmpdir)
336-
337-
// executes setup func (if necessary)
338-
if test.setupFunc != nil {
339-
if err := test.setupFunc(tmpdir); err != nil {
340-
t.Errorf("error executing setupFunc: %v", err)
341-
continue
341+
if tt.setupFunc != nil {
342+
if err := tt.setupFunc(dir); err != nil {
343+
t.Fatalf("error executing setupFunc: %v", err)
344+
}
342345
}
343-
}
344346

345-
// executes create func
346-
err := writeKeyFilesIfNotExist(tmpdir, "dummy", key)
347-
348-
if !test.expectedError && err != nil {
349-
t.Errorf("error writeKeyFilesIfNotExist failed when not expected to fail: %v", err)
350-
continue
351-
} else if test.expectedError && err == nil {
352-
t.Error("error writeKeyFilesIfNotExist didn't failed when expected")
353-
continue
354-
} else if test.expectedError || test.expectedKey == nil {
355-
continue
356-
}
357-
358-
// asserts expected files are there
359-
testutil.AssertFileExists(t, tmpdir, "dummy.key", "dummy.pub")
360-
361-
// check created key
362-
resultingKey, err := pkiutil.TryLoadKeyFromDisk(tmpdir, "dummy")
363-
if err != nil {
364-
t.Errorf("failure reading created key: %v", err)
365-
continue
366-
}
367-
368-
resultingKeyPEM, err := keyutil.MarshalPrivateKeyToPEM(resultingKey)
369-
if err != nil {
370-
t.Errorf("failure marshaling created key: %v", err)
371-
continue
372-
}
373-
374-
expectedKeyPEM, err := keyutil.MarshalPrivateKeyToPEM(test.expectedKey)
375-
if err != nil {
376-
t.Fatalf("Failed to marshal expected private key: %v", err)
377-
}
347+
err := CreateServiceAccountKeyAndPublicKeyFiles(dir)
348+
if (err != nil) != tt.expectedErr {
349+
t.Fatalf("expected error: %v, got: %v, error: %v", tt.expectedErr, err != nil, err)
350+
} else if tt.expectedErr {
351+
return
352+
}
378353

379-
if !bytes.Equal(resultingKeyPEM, expectedKeyPEM) {
380-
t.Error("created key does not match expected key")
381-
}
354+
resultingKeyPEM, wasGenerated, err := keyutil.LoadOrGenerateKeyFile(filepath.Join(dir, kubeadmconstants.ServiceAccountPrivateKeyName))
355+
if err != nil {
356+
t.Errorf("Can't load created key: %v", err)
357+
} else if wasGenerated {
358+
t.Error("The key was not created")
359+
} else if tt.expectedKey != nil && !bytes.Equal(resultingKeyPEM, tt.expectedKey) {
360+
t.Error("Non-existing key is used")
361+
}
362+
})
382363
}
383364
}
384365

0 commit comments

Comments
 (0)