From 17adbf9b085884ac3b028b3ae77754152788588a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20K=C3=A4ldstr=C3=B6m?= Date: Thu, 31 May 2018 22:19:47 +0300 Subject: [PATCH] Add unit tests for the new Bootstrap Token objects and functions --- .../kubeadm/bootstraptokenhelpers_test.go | 473 ++++++++++++++++++ .../apis/kubeadm/bootstraptokenstring_test.go | 236 +++++++++ .../v1alpha2/bootstraptokenstring_test.go | 236 +++++++++ .../kubeadm/validation/validation_test.go | 8 +- cmd/kubeadm/app/cmd/token_test.go | 42 +- cmd/kubeadm/app/cmd/upgrade/common_test.go | 2 - .../phases/bootstraptoken/node/token_test.go | 59 --- .../phases/uploadconfig/uploadconfig_test.go | 16 +- cmd/kubeadm/app/util/token/tokens_test.go | 173 ------- 9 files changed, 973 insertions(+), 272 deletions(-) create mode 100644 cmd/kubeadm/app/apis/kubeadm/bootstraptokenhelpers_test.go create mode 100644 cmd/kubeadm/app/apis/kubeadm/bootstraptokenstring_test.go create mode 100644 cmd/kubeadm/app/apis/kubeadm/v1alpha2/bootstraptokenstring_test.go delete mode 100644 cmd/kubeadm/app/phases/bootstraptoken/node/token_test.go delete mode 100644 cmd/kubeadm/app/util/token/tokens_test.go diff --git a/cmd/kubeadm/app/apis/kubeadm/bootstraptokenhelpers_test.go b/cmd/kubeadm/app/apis/kubeadm/bootstraptokenhelpers_test.go new file mode 100644 index 00000000000..153d08e7c33 --- /dev/null +++ b/cmd/kubeadm/app/apis/kubeadm/bootstraptokenhelpers_test.go @@ -0,0 +1,473 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kubeadm + +import ( + "encoding/json" + "reflect" + "testing" + "time" + + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// This timestamp is used as the reference value when computing expiration dates based on TTLs in these unit tests +var refTime = time.Date(1970, time.January, 1, 1, 1, 1, 0, time.UTC) + +func TestToSecret(t *testing.T) { + + var tests = []struct { + bt *BootstrapToken + secret *v1.Secret + }{ + { + &BootstrapToken{ // all together + Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, + Description: "foo", + Expires: &metav1.Time{ + Time: refTime, + }, + Usages: []string{"signing", "authentication"}, + Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"}, + }, + &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bootstrap-token-abcdef", + Namespace: "kube-system", + }, + Type: v1.SecretType("bootstrap.kubernetes.io/token"), + Data: map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("abcdef0123456789"), + "description": []byte("foo"), + "expiration": []byte(refTime.Format(time.RFC3339)), + "usage-bootstrap-signing": []byte("true"), + "usage-bootstrap-authentication": []byte("true"), + "auth-extra-groups": []byte("system:bootstrappers,system:bootstrappers:foo"), + }, + }, + }, + } + for _, rt := range tests { + actual := rt.bt.ToSecret() + if !reflect.DeepEqual(actual, rt.secret) { + t.Errorf( + "failed BootstrapToken.ToSecret():\n\texpected: %v\n\t actual: %v", + rt.secret, + actual, + ) + } + } +} + +func TestBootstrapTokenToSecretRoundtrip(t *testing.T) { + var tests = []struct { + bt *BootstrapToken + }{ + { + &BootstrapToken{ + Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, + Description: "foo", + Expires: &metav1.Time{ + Time: refTime, + }, + Usages: []string{"authentication", "signing"}, + Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"}, + }, + }, + } + for _, rt := range tests { + actual, err := BootstrapTokenFromSecret(rt.bt.ToSecret()) + if err != nil { + t.Errorf("failed BootstrapToken to Secret roundtrip with error: %v", err) + } + if !reflect.DeepEqual(actual, rt.bt) { + t.Errorf( + "failed BootstrapToken to Secret roundtrip:\n\texpected: %v\n\t actual: %v", + rt.bt, + actual, + ) + } + } +} + +func TestEncodeTokenSecretData(t *testing.T) { + var tests = []struct { + bt *BootstrapToken + data map[string][]byte + }{ + { + &BootstrapToken{ // the minimum amount of information needed to be specified + Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, + }, + map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("abcdef0123456789"), + }, + }, + { + &BootstrapToken{ // adds description + Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, + Description: "foo", + }, + map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("abcdef0123456789"), + "description": []byte("foo"), + }, + }, + { + &BootstrapToken{ // adds ttl + Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, + TTL: &metav1.Duration{ + Duration: mustParseDuration("2h", t), + }, + }, + map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("abcdef0123456789"), + "expiration": []byte(refTime.Add(mustParseDuration("2h", t)).Format(time.RFC3339)), + }, + }, + { + &BootstrapToken{ // adds expiration + Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, + Expires: &metav1.Time{ + Time: refTime, + }, + }, + map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("abcdef0123456789"), + "expiration": []byte(refTime.Format(time.RFC3339)), + }, + }, + { + &BootstrapToken{ // adds ttl and expiration, should favor expiration + Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, + TTL: &metav1.Duration{ + Duration: mustParseDuration("2h", t), + }, + Expires: &metav1.Time{ + Time: refTime, + }, + }, + map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("abcdef0123456789"), + "expiration": []byte(refTime.Format(time.RFC3339)), + }, + }, + { + &BootstrapToken{ // adds usages + Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, + Usages: []string{"authentication", "signing"}, + }, + map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("abcdef0123456789"), + "usage-bootstrap-signing": []byte("true"), + "usage-bootstrap-authentication": []byte("true"), + }, + }, + { + &BootstrapToken{ // adds groups + Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, + Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"}, + }, + map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("abcdef0123456789"), + "auth-extra-groups": []byte("system:bootstrappers,system:bootstrappers:foo"), + }, + }, + { + &BootstrapToken{ // all together + Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, + Description: "foo", + TTL: &metav1.Duration{ + Duration: mustParseDuration("2h", t), + }, + Expires: &metav1.Time{ + Time: refTime, + }, + Usages: []string{"authentication", "signing"}, + Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"}, + }, + map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("abcdef0123456789"), + "description": []byte("foo"), + "expiration": []byte(refTime.Format(time.RFC3339)), + "usage-bootstrap-signing": []byte("true"), + "usage-bootstrap-authentication": []byte("true"), + "auth-extra-groups": []byte("system:bootstrappers,system:bootstrappers:foo"), + }, + }, + } + for _, rt := range tests { + actual := encodeTokenSecretData(rt.bt, refTime) + if !reflect.DeepEqual(actual, rt.data) { + t.Errorf( + "failed encodeTokenSecretData:\n\texpected: %v\n\t actual: %v", + rt.data, + actual, + ) + } + } +} + +func mustParseDuration(durationStr string, t *testing.T) time.Duration { + d, err := time.ParseDuration(durationStr) + if err != nil { + t.Fatalf("couldn't parse duration %q: %v", durationStr, err) + } + return d +} + +func TestBootstrapTokenFromSecret(t *testing.T) { + var tests = []struct { + name string + data map[string][]byte + bt *BootstrapToken + expectedError bool + }{ + { // minimum information + "bootstrap-token-abcdef", + map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("abcdef0123456789"), + }, + &BootstrapToken{ + Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, + }, + false, + }, + { // invalid token id + "bootstrap-token-abcdef", + map[string][]byte{ + "token-id": []byte("abcdeF"), + "token-secret": []byte("abcdef0123456789"), + }, + nil, + true, + }, + { // invalid secret naming + "foo", + map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("abcdef0123456789"), + }, + nil, + true, + }, + { // invalid token secret + "bootstrap-token-abcdef", + map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("ABCDEF0123456789"), + }, + nil, + true, + }, + { // adds description + "bootstrap-token-abcdef", + map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("abcdef0123456789"), + "description": []byte("foo"), + }, + &BootstrapToken{ + Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, + Description: "foo", + }, + false, + }, + { // adds expiration + "bootstrap-token-abcdef", + map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("abcdef0123456789"), + "expiration": []byte(refTime.Format(time.RFC3339)), + }, + &BootstrapToken{ + Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, + Expires: &metav1.Time{ + Time: refTime, + }, + }, + false, + }, + { // invalid expiration + "bootstrap-token-abcdef", + map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("abcdef0123456789"), + "expiration": []byte("invalid date"), + }, + nil, + true, + }, + { // adds usages + "bootstrap-token-abcdef", + map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("abcdef0123456789"), + "usage-bootstrap-signing": []byte("true"), + "usage-bootstrap-authentication": []byte("true"), + }, + &BootstrapToken{ + Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, + Usages: []string{"authentication", "signing"}, + }, + false, + }, + { // should ignore usages that aren't set to true + "bootstrap-token-abcdef", + map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("abcdef0123456789"), + "usage-bootstrap-signing": []byte("true"), + "usage-bootstrap-authentication": []byte("true"), + "usage-bootstrap-foo": []byte("false"), + "usage-bootstrap-bar": []byte(""), + }, + &BootstrapToken{ + Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, + Usages: []string{"authentication", "signing"}, + }, + false, + }, + { // adds groups + "bootstrap-token-abcdef", + map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("abcdef0123456789"), + "auth-extra-groups": []byte("system:bootstrappers,system:bootstrappers:foo"), + }, + &BootstrapToken{ + Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, + Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"}, + }, + false, + }, + { // all fields set + "bootstrap-token-abcdef", + map[string][]byte{ + "token-id": []byte("abcdef"), + "token-secret": []byte("abcdef0123456789"), + "description": []byte("foo"), + "expiration": []byte(refTime.Format(time.RFC3339)), + "usage-bootstrap-signing": []byte("true"), + "usage-bootstrap-authentication": []byte("true"), + "auth-extra-groups": []byte("system:bootstrappers,system:bootstrappers:foo"), + }, + &BootstrapToken{ + Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, + Description: "foo", + Expires: &metav1.Time{ + Time: refTime, + }, + Usages: []string{"authentication", "signing"}, + Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"}, + }, + false, + }, + } + for _, rt := range tests { + actual, err := BootstrapTokenFromSecret(&v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: rt.name, + Namespace: "kube-system", + }, + Type: v1.SecretType("bootstrap.kubernetes.io/token"), + Data: rt.data, + }) + if (err != nil) != rt.expectedError { + t.Errorf( + "failed BootstrapTokenFromSecret\n\texpected error: %t\n\t actual error: %v", + rt.expectedError, + err, + ) + } else { + if actual == nil && rt.bt == nil { + // if both pointers are nil, it's okay, just continue + continue + } + // If one of the pointers is defined but the other isn't, throw error. If both pointers are defined but unequal, throw error + if (actual == nil && rt.bt != nil) || (actual != nil && rt.bt == nil) || !reflect.DeepEqual(*actual, *rt.bt) { + t.Errorf( + "failed BootstrapTokenFromSecret\n\texpected: %s\n\t actual: %s", + jsonMarshal(rt.bt), + jsonMarshal(actual), + ) + } + } + } +} + +func jsonMarshal(bt *BootstrapToken) string { + b, _ := json.Marshal(*bt) + return string(b) +} + +func TestGetSecretString(t *testing.T) { + var tests = []struct { + secret *v1.Secret + key string + expectedVal string + }{ + { + secret: &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "foo"}, + Data: map[string][]byte{ + "foo": []byte("bar"), + }, + }, + key: "foo", + expectedVal: "bar", + }, + { + secret: &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "foo"}, + Data: map[string][]byte{ + "foo": []byte("bar"), + }, + }, + key: "baz", + expectedVal: "", + }, + { + secret: &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "foo"}, + }, + key: "foo", + expectedVal: "", + }, + } + for _, rt := range tests { + actual := getSecretString(rt.secret, rt.key) + if actual != rt.expectedVal { + t.Errorf( + "failed getSecretString:\n\texpected: %s\n\t actual: %s", + rt.expectedVal, + actual, + ) + } + } +} diff --git a/cmd/kubeadm/app/apis/kubeadm/bootstraptokenstring_test.go b/cmd/kubeadm/app/apis/kubeadm/bootstraptokenstring_test.go new file mode 100644 index 00000000000..8a389f83ec1 --- /dev/null +++ b/cmd/kubeadm/app/apis/kubeadm/bootstraptokenstring_test.go @@ -0,0 +1,236 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kubeadm + +import ( + "encoding/json" + "fmt" + "reflect" + "testing" +) + +func TestMarshalJSON(t *testing.T) { + var tests = []struct { + bts BootstrapTokenString + expected string + }{ + {BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, `"abcdef.abcdef0123456789"`}, + {BootstrapTokenString{ID: "foo", Secret: "bar"}, `"foo.bar"`}, + {BootstrapTokenString{ID: "h", Secret: "b"}, `"h.b"`}, + } + for _, rt := range tests { + b, err := json.Marshal(rt.bts) + if err != nil { + t.Fatalf("json.Marshal returned an unexpected error: %v", err) + } + if string(b) != rt.expected { + t.Errorf( + "failed BootstrapTokenString.MarshalJSON:\n\texpected: %s\n\t actual: %s", + rt.expected, + string(b), + ) + } + } +} + +func TestUnmarshalJSON(t *testing.T) { + var tests = []struct { + input string + bts *BootstrapTokenString + expectedError bool + }{ + {`"f.s"`, &BootstrapTokenString{}, true}, + {`"abcdef."`, &BootstrapTokenString{}, true}, + {`"abcdef:abcdef0123456789"`, &BootstrapTokenString{}, true}, + {`abcdef.abcdef0123456789`, &BootstrapTokenString{}, true}, + {`"abcdef.abcdef0123456789`, &BootstrapTokenString{}, true}, + {`"abcdef.ABCDEF0123456789"`, &BootstrapTokenString{}, true}, + {`"abcdef.abcdef0123456789"`, &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, false}, + {`"123456.aabbccddeeffgghh"`, &BootstrapTokenString{ID: "123456", Secret: "aabbccddeeffgghh"}, false}, + } + for _, rt := range tests { + newbts := &BootstrapTokenString{} + err := json.Unmarshal([]byte(rt.input), newbts) + if (err != nil) != rt.expectedError { + t.Errorf("failed BootstrapTokenString.UnmarshalJSON:\n\texpected error: %t\n\t actual error: %v", rt.expectedError, err) + } else if !reflect.DeepEqual(rt.bts, newbts) { + t.Errorf( + "failed BootstrapTokenString.UnmarshalJSON:\n\texpected: %v\n\t actual: %v", + rt.bts, + newbts, + ) + } + } +} + +func TestJSONRoundtrip(t *testing.T) { + var tests = []struct { + input string + bts *BootstrapTokenString + }{ + {`"abcdef.abcdef0123456789"`, nil}, + {"", &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}}, + } + for _, rt := range tests { + if err := roundtrip(rt.input, rt.bts); err != nil { + t.Errorf("failed BootstrapTokenString JSON roundtrip with error: %v", err) + } + } +} + +func roundtrip(input string, bts *BootstrapTokenString) error { + var b []byte + var err error + newbts := &BootstrapTokenString{} + // If string input was specified, roundtrip like this: string -> (unmarshal) -> object -> (marshal) -> string + if len(input) > 0 { + if err := json.Unmarshal([]byte(input), newbts); err != nil { + return fmt.Errorf("expected no unmarshal error, got error: %v", err) + } + if b, err = json.Marshal(newbts); err != nil { + return fmt.Errorf("expected no marshal error, got error: %v", err) + } + if input != string(b) { + return fmt.Errorf( + "expected token: %s\n\t actual: %s", + input, + string(b), + ) + } + } else { // Otherwise, roundtrip like this: object -> (marshal) -> string -> (unmarshal) -> object + if b, err = json.Marshal(bts); err != nil { + return fmt.Errorf("expected no marshal error, got error: %v", err) + } + if err := json.Unmarshal(b, newbts); err != nil { + return fmt.Errorf("expected no unmarshal error, got error: %v", err) + } + if !reflect.DeepEqual(bts, newbts) { + return fmt.Errorf( + "expected object: %v\n\t actual: %v", + bts, + newbts, + ) + } + } + return nil +} + +func TestTokenFromIDAndSecret(t *testing.T) { + var tests = []struct { + bts BootstrapTokenString + expected string + }{ + {BootstrapTokenString{ID: "foo", Secret: "bar"}, "foo.bar"}, + {BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, "abcdef.abcdef0123456789"}, + {BootstrapTokenString{ID: "h", Secret: "b"}, "h.b"}, + } + for _, rt := range tests { + actual := rt.bts.String() + if actual != rt.expected { + t.Errorf( + "failed BootstrapTokenString.String():\n\texpected: %s\n\t actual: %s", + rt.expected, + actual, + ) + } + } +} + +func TestNewBootstrapTokenString(t *testing.T) { + var tests = []struct { + token string + expectedError bool + bts *BootstrapTokenString + }{ + {token: "", expectedError: true, bts: nil}, + {token: ".", expectedError: true, bts: nil}, + {token: "1234567890123456789012", expectedError: true, bts: nil}, // invalid parcel size + {token: "12345.1234567890123456", expectedError: true, bts: nil}, // invalid parcel size + {token: ".1234567890123456", expectedError: true, bts: nil}, // invalid parcel size + {token: "123456.", expectedError: true, bts: nil}, // invalid parcel size + {token: "123456:1234567890.123456", expectedError: true, bts: nil}, // invalid separation + {token: "abcdef:1234567890123456", expectedError: true, bts: nil}, // invalid separation + {token: "Abcdef.1234567890123456", expectedError: true, bts: nil}, // invalid token id + {token: "123456.AABBCCDDEEFFGGHH", expectedError: true, bts: nil}, // invalid token secret + {token: "123456.AABBCCD-EEFFGGHH", expectedError: true, bts: nil}, // invalid character + {token: "abc*ef.1234567890123456", expectedError: true, bts: nil}, // invalid character + {token: "abcdef.1234567890123456", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "1234567890123456"}}, + {token: "123456.aabbccddeeffgghh", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "aabbccddeeffgghh"}}, + {token: "abcdef.abcdef0123456789", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}}, + {token: "123456.1234560123456789", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "1234560123456789"}}, + } + for _, rt := range tests { + actual, err := NewBootstrapTokenString(rt.token) + if (err != nil) != rt.expectedError { + t.Errorf( + "failed NewBootstrapTokenString for the token %q\n\texpected error: %t\n\t actual error: %v", + rt.token, + rt.expectedError, + err, + ) + } else if !reflect.DeepEqual(actual, rt.bts) { + t.Errorf( + "failed NewBootstrapTokenString for the token %q\n\texpected: %v\n\t actual: %v", + rt.token, + rt.bts, + actual, + ) + } + } +} + +func TestNewBootstrapTokenStringFromIDAndSecret(t *testing.T) { + var tests = []struct { + id, secret string + expectedError bool + bts *BootstrapTokenString + }{ + {id: "", secret: "", expectedError: true, bts: nil}, + {id: "1234567890123456789012", secret: "", expectedError: true, bts: nil}, // invalid parcel size + {id: "12345", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid parcel size + {id: "", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid parcel size + {id: "123456", secret: "", expectedError: true, bts: nil}, // invalid parcel size + {id: "Abcdef", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid token id + {id: "123456", secret: "AABBCCDDEEFFGGHH", expectedError: true, bts: nil}, // invalid token secret + {id: "123456", secret: "AABBCCD-EEFFGGHH", expectedError: true, bts: nil}, // invalid character + {id: "abc*ef", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid character + {id: "abcdef", secret: "1234567890123456", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "1234567890123456"}}, + {id: "123456", secret: "aabbccddeeffgghh", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "aabbccddeeffgghh"}}, + {id: "abcdef", secret: "abcdef0123456789", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}}, + {id: "123456", secret: "1234560123456789", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "1234560123456789"}}, + } + for _, rt := range tests { + actual, err := NewBootstrapTokenStringFromIDAndSecret(rt.id, rt.secret) + if (err != nil) != rt.expectedError { + t.Errorf( + "failed NewBootstrapTokenStringFromIDAndSecret for the token with id %q and secret %q\n\texpected error: %t\n\t actual error: %v", + rt.id, + rt.secret, + rt.expectedError, + err, + ) + } else if !reflect.DeepEqual(actual, rt.bts) { + t.Errorf( + "failed NewBootstrapTokenStringFromIDAndSecret for the token with id %q and secret %q\n\texpected: %v\n\t actual: %v", + rt.id, + rt.secret, + rt.bts, + actual, + ) + } + } +} diff --git a/cmd/kubeadm/app/apis/kubeadm/v1alpha2/bootstraptokenstring_test.go b/cmd/kubeadm/app/apis/kubeadm/v1alpha2/bootstraptokenstring_test.go new file mode 100644 index 00000000000..0d06bd153e3 --- /dev/null +++ b/cmd/kubeadm/app/apis/kubeadm/v1alpha2/bootstraptokenstring_test.go @@ -0,0 +1,236 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha2 + +import ( + "encoding/json" + "fmt" + "reflect" + "testing" +) + +func TestMarshalJSON(t *testing.T) { + var tests = []struct { + bts BootstrapTokenString + expected string + }{ + {BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, `"abcdef.abcdef0123456789"`}, + {BootstrapTokenString{ID: "foo", Secret: "bar"}, `"foo.bar"`}, + {BootstrapTokenString{ID: "h", Secret: "b"}, `"h.b"`}, + } + for _, rt := range tests { + b, err := json.Marshal(rt.bts) + if err != nil { + t.Fatalf("json.Marshal returned an unexpected error: %v", err) + } + if string(b) != rt.expected { + t.Errorf( + "failed BootstrapTokenString.MarshalJSON:\n\texpected: %s\n\t actual: %s", + rt.expected, + string(b), + ) + } + } +} + +func TestUnmarshalJSON(t *testing.T) { + var tests = []struct { + input string + bts *BootstrapTokenString + expectedError bool + }{ + {`"f.s"`, &BootstrapTokenString{}, true}, + {`"abcdef."`, &BootstrapTokenString{}, true}, + {`"abcdef:abcdef0123456789"`, &BootstrapTokenString{}, true}, + {`abcdef.abcdef0123456789`, &BootstrapTokenString{}, true}, + {`"abcdef.abcdef0123456789`, &BootstrapTokenString{}, true}, + {`"abcdef.ABCDEF0123456789"`, &BootstrapTokenString{}, true}, + {`"abcdef.abcdef0123456789"`, &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, false}, + {`"123456.aabbccddeeffgghh"`, &BootstrapTokenString{ID: "123456", Secret: "aabbccddeeffgghh"}, false}, + } + for _, rt := range tests { + newbts := &BootstrapTokenString{} + err := json.Unmarshal([]byte(rt.input), newbts) + if (err != nil) != rt.expectedError { + t.Errorf("failed BootstrapTokenString.UnmarshalJSON:\n\texpected error: %t\n\t actual error: %v", rt.expectedError, err) + } else if !reflect.DeepEqual(rt.bts, newbts) { + t.Errorf( + "failed BootstrapTokenString.UnmarshalJSON:\n\texpected: %v\n\t actual: %v", + rt.bts, + newbts, + ) + } + } +} + +func TestJSONRoundtrip(t *testing.T) { + var tests = []struct { + input string + bts *BootstrapTokenString + }{ + {`"abcdef.abcdef0123456789"`, nil}, + {"", &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}}, + } + for _, rt := range tests { + if err := roundtrip(rt.input, rt.bts); err != nil { + t.Errorf("failed BootstrapTokenString JSON roundtrip with error: %v", err) + } + } +} + +func roundtrip(input string, bts *BootstrapTokenString) error { + var b []byte + var err error + newbts := &BootstrapTokenString{} + // If string input was specified, roundtrip like this: string -> (unmarshal) -> object -> (marshal) -> string + if len(input) > 0 { + if err := json.Unmarshal([]byte(input), newbts); err != nil { + return fmt.Errorf("expected no unmarshal error, got error: %v", err) + } + if b, err = json.Marshal(newbts); err != nil { + return fmt.Errorf("expected no marshal error, got error: %v", err) + } + if input != string(b) { + return fmt.Errorf( + "expected token: %s\n\t actual: %s", + input, + string(b), + ) + } + } else { // Otherwise, roundtrip like this: object -> (marshal) -> string -> (unmarshal) -> object + if b, err = json.Marshal(bts); err != nil { + return fmt.Errorf("expected no marshal error, got error: %v", err) + } + if err := json.Unmarshal(b, newbts); err != nil { + return fmt.Errorf("expected no unmarshal error, got error: %v", err) + } + if !reflect.DeepEqual(bts, newbts) { + return fmt.Errorf( + "expected object: %v\n\t actual: %v", + bts, + newbts, + ) + } + } + return nil +} + +func TestTokenFromIDAndSecret(t *testing.T) { + var tests = []struct { + bts BootstrapTokenString + expected string + }{ + {BootstrapTokenString{ID: "foo", Secret: "bar"}, "foo.bar"}, + {BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, "abcdef.abcdef0123456789"}, + {BootstrapTokenString{ID: "h", Secret: "b"}, "h.b"}, + } + for _, rt := range tests { + actual := rt.bts.String() + if actual != rt.expected { + t.Errorf( + "failed BootstrapTokenString.String():\n\texpected: %s\n\t actual: %s", + rt.expected, + actual, + ) + } + } +} + +func TestNewBootstrapTokenString(t *testing.T) { + var tests = []struct { + token string + expectedError bool + bts *BootstrapTokenString + }{ + {token: "", expectedError: true, bts: nil}, + {token: ".", expectedError: true, bts: nil}, + {token: "1234567890123456789012", expectedError: true, bts: nil}, // invalid parcel size + {token: "12345.1234567890123456", expectedError: true, bts: nil}, // invalid parcel size + {token: ".1234567890123456", expectedError: true, bts: nil}, // invalid parcel size + {token: "123456.", expectedError: true, bts: nil}, // invalid parcel size + {token: "123456:1234567890.123456", expectedError: true, bts: nil}, // invalid separation + {token: "abcdef:1234567890123456", expectedError: true, bts: nil}, // invalid separation + {token: "Abcdef.1234567890123456", expectedError: true, bts: nil}, // invalid token id + {token: "123456.AABBCCDDEEFFGGHH", expectedError: true, bts: nil}, // invalid token secret + {token: "123456.AABBCCD-EEFFGGHH", expectedError: true, bts: nil}, // invalid character + {token: "abc*ef.1234567890123456", expectedError: true, bts: nil}, // invalid character + {token: "abcdef.1234567890123456", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "1234567890123456"}}, + {token: "123456.aabbccddeeffgghh", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "aabbccddeeffgghh"}}, + {token: "abcdef.abcdef0123456789", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}}, + {token: "123456.1234560123456789", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "1234560123456789"}}, + } + for _, rt := range tests { + actual, err := NewBootstrapTokenString(rt.token) + if (err != nil) != rt.expectedError { + t.Errorf( + "failed NewBootstrapTokenString for the token %q\n\texpected error: %t\n\t actual error: %v", + rt.token, + rt.expectedError, + err, + ) + } else if !reflect.DeepEqual(actual, rt.bts) { + t.Errorf( + "failed NewBootstrapTokenString for the token %q\n\texpected: %v\n\t actual: %v", + rt.token, + rt.bts, + actual, + ) + } + } +} + +func TestNewBootstrapTokenStringFromIDAndSecret(t *testing.T) { + var tests = []struct { + id, secret string + expectedError bool + bts *BootstrapTokenString + }{ + {id: "", secret: "", expectedError: true, bts: nil}, + {id: "1234567890123456789012", secret: "", expectedError: true, bts: nil}, // invalid parcel size + {id: "12345", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid parcel size + {id: "", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid parcel size + {id: "123456", secret: "", expectedError: true, bts: nil}, // invalid parcel size + {id: "Abcdef", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid token id + {id: "123456", secret: "AABBCCDDEEFFGGHH", expectedError: true, bts: nil}, // invalid token secret + {id: "123456", secret: "AABBCCD-EEFFGGHH", expectedError: true, bts: nil}, // invalid character + {id: "abc*ef", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid character + {id: "abcdef", secret: "1234567890123456", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "1234567890123456"}}, + {id: "123456", secret: "aabbccddeeffgghh", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "aabbccddeeffgghh"}}, + {id: "abcdef", secret: "abcdef0123456789", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}}, + {id: "123456", secret: "1234560123456789", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "1234560123456789"}}, + } + for _, rt := range tests { + actual, err := NewBootstrapTokenStringFromIDAndSecret(rt.id, rt.secret) + if (err != nil) != rt.expectedError { + t.Errorf( + "failed NewBootstrapTokenStringFromIDAndSecret for the token with id %q and secret %q\n\texpected error: %t\n\t actual error: %v", + rt.id, + rt.secret, + rt.expectedError, + err, + ) + } else if !reflect.DeepEqual(actual, rt.bts) { + t.Errorf( + "failed NewBootstrapTokenStringFromIDAndSecret for the token with id %q and secret %q\n\texpected: %v\n\t actual: %v", + rt.id, + rt.secret, + rt.bts, + actual, + ) + } + } +} diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go index 6876bd702de..d4454fb4655 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go @@ -33,7 +33,7 @@ import ( utilpointer "k8s.io/kubernetes/pkg/util/pointer" ) -func TestValidateTokenDiscovery(t *testing.T) { +func TestValidateToken(t *testing.T) { var tests = []struct { c *kubeadm.NodeConfiguration f *field.Path @@ -51,7 +51,7 @@ func TestValidateTokenDiscovery(t *testing.T) { err := ValidateToken(rt.c.Token, rt.f).ToAggregate() if (err == nil) != rt.expected { t.Errorf( - "failed ValidateTokenDiscovery:\n\texpected: %t\n\t actual: %t", + "failed ValidateToken:\n\texpected: %t\n\t actual: %t", rt.expected, (err == nil), ) @@ -434,7 +434,6 @@ func TestValidateMasterConfiguration(t *testing.T) { DNSDomain: "cluster.local", }, CertificatesDir: "/some/other/cert/dir", - Token: "abcdef.0123456789abcdef", }, false}, {"valid master configuration with incorrect IPv4 pod subnet", &kubeadm.MasterConfiguration{ @@ -448,7 +447,6 @@ func TestValidateMasterConfiguration(t *testing.T) { PodSubnet: "10.0.1.15", }, CertificatesDir: "/some/other/cert/dir", - Token: "abcdef.0123456789abcdef", NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"}, }, false}, {"valid master configuration with IPv4 service subnet", @@ -494,7 +492,6 @@ func TestValidateMasterConfiguration(t *testing.T) { PodSubnet: "10.0.1.15/16", }, CertificatesDir: "/some/other/cert/dir", - Token: "abcdef.0123456789abcdef", NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"}, }, true}, {"valid master configuration using IPv6 service subnet", @@ -539,7 +536,6 @@ func TestValidateMasterConfiguration(t *testing.T) { DNSDomain: "cluster.local", }, CertificatesDir: "/some/other/cert/dir", - Token: "abcdef.0123456789abcdef", NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"}, }, true}, } diff --git a/cmd/kubeadm/app/cmd/token_test.go b/cmd/kubeadm/app/cmd/token_test.go index 74b3bc8614c..c919583a918 100644 --- a/cmd/kubeadm/app/cmd/token_test.go +++ b/cmd/kubeadm/app/cmd/token_test.go @@ -135,13 +135,6 @@ func TestRunCreateToken(t *testing.T) { extraGroups: []string{}, expectedError: false, }, - { - name: "invalid: incorrect token", - token: "123456.AABBCCDDEEFFGGHH", - usages: []string{"signing", "authentication"}, - extraGroups: []string{}, - expectedError: true, - }, { name: "invalid: incorrect extraGroups", token: "abcdef.1234567890123456", @@ -180,18 +173,27 @@ func TestRunCreateToken(t *testing.T) { }, } for _, tc := range testCases { + bts, err := kubeadmapiv1alpha2.NewBootstrapTokenString(tc.token) + if err != nil && len(tc.token) != 0 { // if tc.token is "" it's okay as it will be generated later at runtime + t.Fatalf("token couldn't be parsed for testing: %v", err) + } + cfg := &kubeadmapiv1alpha2.MasterConfiguration{ // KubernetesVersion is not used by bootstrap-token, but we set this explicitly to avoid // the lookup of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig KubernetesVersion: "v1.10.0", - Token: tc.token, - TokenTTL: &metav1.Duration{Duration: 0}, - TokenUsages: tc.usages, - TokenGroups: tc.extraGroups, + BootstrapTokens: []kubeadmapiv1alpha2.BootstrapToken{ + { + Token: bts, + TTL: &metav1.Duration{Duration: 0}, + Usages: tc.usages, + Groups: tc.extraGroups, + }, + }, } - err := RunCreateToken(&buf, fakeClient, "", cfg, "", tc.printJoin, "") + err = RunCreateToken(&buf, fakeClient, "", cfg, tc.printJoin, "") if (err != nil) != tc.expectedError { t.Errorf("Test case %s: RunCreateToken expected error: %v, saw: %v", tc.name, tc.expectedError, (err != nil)) } @@ -277,22 +279,6 @@ func TestNewCmdToken(t *testing.T) { } } -func TestGetSecretString(t *testing.T) { - secret := v1.Secret{} - key := "test-key" - if str := getSecretString(&secret, key); str != "" { - t.Errorf("getSecretString() did not return empty string for a nil v1.Secret.Data") - } - secret.Data = make(map[string][]byte) - if str := getSecretString(&secret, key); str != "" { - t.Errorf("getSecretString() did not return empty string for missing v1.Secret.Data key") - } - secret.Data[key] = []byte("test-value") - if str := getSecretString(&secret, key); str == "" { - t.Errorf("getSecretString() failed for a valid v1.Secret.Data key") - } -} - func TestGetClientset(t *testing.T) { testConfigTokenFile := "test-config-file" diff --git a/cmd/kubeadm/app/cmd/upgrade/common_test.go b/cmd/kubeadm/app/cmd/upgrade/common_test.go index 8745378b60f..19b32e09806 100644 --- a/cmd/kubeadm/app/cmd/upgrade/common_test.go +++ b/cmd/kubeadm/app/cmd/upgrade/common_test.go @@ -68,7 +68,6 @@ func TestPrintConfiguration(t *testing.T) { nodeRegistration: criSocket: "" name: "" - token: "" unifiedControlPlaneImage: "" `), }, @@ -113,7 +112,6 @@ func TestPrintConfiguration(t *testing.T) { nodeRegistration: criSocket: "" name: "" - token: "" unifiedControlPlaneImage: "" `), }, diff --git a/cmd/kubeadm/app/phases/bootstraptoken/node/token_test.go b/cmd/kubeadm/app/phases/bootstraptoken/node/token_test.go deleted file mode 100644 index 48bc8c1f168..00000000000 --- a/cmd/kubeadm/app/phases/bootstraptoken/node/token_test.go +++ /dev/null @@ -1,59 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package node - -import ( - "bytes" - "testing" - "time" - - kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" -) - -func TestEncodeTokenSecretData(t *testing.T) { - var tests = []struct { - token *kubeadmapi.TokenDiscovery - t time.Duration - }{ - {token: &kubeadmapi.TokenDiscovery{ID: "foo", Secret: "bar"}}, // should use default - {token: &kubeadmapi.TokenDiscovery{ID: "foo", Secret: "bar"}, t: time.Second}, // should use default - } - for _, rt := range tests { - actual, _ := encodeTokenSecretData(rt.token.ID, rt.token.Secret, rt.t, []string{}, []string{}, "") - if !bytes.Equal(actual["token-id"], []byte(rt.token.ID)) { - t.Errorf( - "failed EncodeTokenSecretData:\n\texpected: %s\n\t actual: %s", - rt.token.ID, - actual["token-id"], - ) - } - if !bytes.Equal(actual["token-secret"], []byte(rt.token.Secret)) { - t.Errorf( - "failed EncodeTokenSecretData:\n\texpected: %s\n\t actual: %s", - rt.token.Secret, - actual["token-secret"], - ) - } - if rt.t > 0 { - if actual["expiration"] == nil { - t.Errorf( - "failed EncodeTokenSecretData, duration was not added to time", - ) - } - } - } -} diff --git a/cmd/kubeadm/app/phases/uploadconfig/uploadconfig_test.go b/cmd/kubeadm/app/phases/uploadconfig/uploadconfig_test.go index e683bf3bbe7..accaff0904e 100644 --- a/cmd/kubeadm/app/phases/uploadconfig/uploadconfig_test.go +++ b/cmd/kubeadm/app/phases/uploadconfig/uploadconfig_test.go @@ -63,8 +63,15 @@ func TestUploadConfiguration(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cfg := &kubeadmapi.MasterConfiguration{ - KubernetesVersion: "1.7.3", - Token: "1234567", + KubernetesVersion: "v1.10.3", + BootstrapTokens: []kubeadmapi.BootstrapToken{ + { + Token: &kubeadmapi.BootstrapTokenString{ + ID: "abcdef", + Secret: "abcdef0123456789", + }, + }, + }, } client := clientsetfake.NewSimpleClientset() if tt.errOnCreate != nil { @@ -110,8 +117,9 @@ func TestUploadConfiguration(t *testing.T) { t.Errorf("Decoded value doesn't match, decoded = %#v, expected = %#v", decodedCfg.KubernetesVersion, cfg.KubernetesVersion) } - if decodedCfg.Token != "" { - t.Errorf("Decoded value contains token (sensitive info), decoded = %#v, expected = empty", decodedCfg.Token) + // If the decoded cfg has a BootstrapTokens array, verify the sensitive information we had isn't still there. + if len(decodedCfg.BootstrapTokens) > 0 && decodedCfg.BootstrapTokens[0].Token != nil && decodedCfg.BootstrapTokens[0].Token.String() == cfg.BootstrapTokens[0].Token.String() { + t.Errorf("Decoded value contains .BootstrapTokens (sensitive info), decoded = %#v, expected = empty", decodedCfg.BootstrapTokens) } if decodedExtCfg.Kind != "MasterConfiguration" { diff --git a/cmd/kubeadm/app/util/token/tokens_test.go b/cmd/kubeadm/app/util/token/tokens_test.go deleted file mode 100644 index 4146a027036..00000000000 --- a/cmd/kubeadm/app/util/token/tokens_test.go +++ /dev/null @@ -1,173 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package token - -import ( - "testing" - - kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" -) - -func TestTokenParse(t *testing.T) { - var tests = []struct { - token string - expected bool - }{ - {token: "1234567890123456789012", expected: false}, // invalid parcel size - {token: "12345.1234567890123456", expected: false}, // invalid parcel size - {token: ".1234567890123456", expected: false}, // invalid parcel size - {token: "123456:1234567890.123456", expected: false}, // invalid separation - {token: "abcdef:1234567890123456", expected: false}, // invalid separation - {token: "Abcdef.1234567890123456", expected: false}, // invalid token id - {token: "123456.AABBCCDDEEFFGGHH", expected: false}, // invalid token secret - {token: "abcdef.1234567890123456", expected: true}, - {token: "123456.aabbccddeeffgghh", expected: true}, - } - - for _, rt := range tests { - _, _, actual := ParseToken(rt.token) - if (actual == nil) != rt.expected { - t.Errorf( - "failed ParseToken for this token: [%s]\n\texpected: %t\n\t actual: %t", - rt.token, - rt.expected, - (actual == nil), - ) - } - } - -} - -func TestParseTokenID(t *testing.T) { - var tests = []struct { - tokenID string - expected bool - }{ - {tokenID: "", expected: false}, - {tokenID: "1234567890123456789012", expected: false}, - {tokenID: "12345", expected: false}, - {tokenID: "Abcdef", expected: false}, - {tokenID: "abcdef", expected: true}, - {tokenID: "123456", expected: true}, - } - for _, rt := range tests { - actual := ParseTokenID(rt.tokenID) - if (actual == nil) != rt.expected { - t.Errorf( - "failed ParseTokenID for this token ID: [%s]\n\texpected: %t\n\t actual: %t", - rt.tokenID, - rt.expected, - (actual == nil), - ) - } - } -} - -func TestValidateToken(t *testing.T) { - var tests = []struct { - token *kubeadmapi.TokenDiscovery - expected bool - }{ - {token: &kubeadmapi.TokenDiscovery{ID: "", Secret: ""}, expected: false}, - {token: &kubeadmapi.TokenDiscovery{ID: "1234567890123456789012", Secret: ""}, expected: false}, - {token: &kubeadmapi.TokenDiscovery{ID: "", Secret: "1234567890123456789012"}, expected: false}, - {token: &kubeadmapi.TokenDiscovery{ID: "12345", Secret: "1234567890123456"}, expected: false}, - {token: &kubeadmapi.TokenDiscovery{ID: "Abcdef", Secret: "1234567890123456"}, expected: false}, - {token: &kubeadmapi.TokenDiscovery{ID: "123456", Secret: "AABBCCDDEEFFGGHH"}, expected: false}, - {token: &kubeadmapi.TokenDiscovery{ID: "abc*ef", Secret: "1234567890123456"}, expected: false}, - {token: &kubeadmapi.TokenDiscovery{ID: "abcdef", Secret: "123456789*123456"}, expected: false}, - {token: &kubeadmapi.TokenDiscovery{ID: "abcdef", Secret: "1234567890123456"}, expected: true}, - {token: &kubeadmapi.TokenDiscovery{ID: "123456", Secret: "aabbccddeeffgghh"}, expected: true}, - {token: &kubeadmapi.TokenDiscovery{ID: "abc456", Secret: "1234567890123456"}, expected: true}, - {token: &kubeadmapi.TokenDiscovery{ID: "abcdef", Secret: "123456ddeeffgghh"}, expected: true}, - } - for _, rt := range tests { - valid, actual := ValidateToken(rt.token) - if (actual == nil) != rt.expected { - t.Errorf( - "failed ValidateToken for this token ID: [%s]\n\texpected: %t\n\t actual: %t", - rt.token, - rt.expected, - (actual == nil), - ) - } - if (valid == true) != rt.expected { - t.Errorf( - "failed ValidateToken for this token ID: [%s]\n\texpected: %t\n\t actual: %t", - rt.token, - rt.expected, - (actual == nil), - ) - } - } -} - -func TestGenerateToken(t *testing.T) { - token, err := GenerateToken() - if err != nil { - t.Fatalf("GenerateToken returned an unexpected error: %+v", err) - } - tokenID, tokenSecret, err := ParseToken(token) - if err != nil { - t.Fatalf("GenerateToken returned an unexpected error: %+v", err) - } - if len(tokenID) != 6 { - t.Errorf("failed GenerateToken first part length:\n\texpected: 6\n\t actual: %d", len(tokenID)) - } - if len(tokenSecret) != 16 { - t.Errorf("failed GenerateToken second part length:\n\texpected: 16\n\t actual: %d", len(tokenSecret)) - } -} - -func TestRandBytes(t *testing.T) { - var randTest = []int{ - 0, - 1, - 2, - 3, - 100, - } - - for _, rt := range randTest { - actual, err := randBytes(rt) - if err != nil { - t.Errorf("failed randBytes: %v", err) - } - if len(actual) != rt { - t.Errorf("failed randBytes:\n\texpected: %d\n\t actual: %d\n", rt, len(actual)) - } - } -} - -func TestBearerToken(t *testing.T) { - var tests = []struct { - token *kubeadmapi.TokenDiscovery - expected string - }{ - {token: &kubeadmapi.TokenDiscovery{ID: "foo", Secret: "bar"}, expected: "foo.bar"}, // should use default - } - for _, rt := range tests { - actual := BearerToken(rt.token) - if actual != rt.expected { - t.Errorf( - "failed BearerToken:\n\texpected: %s\n\t actual: %s", - rt.expected, - actual, - ) - } - } -}