Add unit tests for the new Bootstrap Token objects and functions

This commit is contained in:
Lucas Käldström 2018-05-31 22:19:47 +03:00
parent c473039580
commit 17adbf9b08
No known key found for this signature in database
GPG Key ID: 3FA3783D77751514
9 changed files with 973 additions and 272 deletions

View File

@ -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,
)
}
}
}

View File

@ -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,
)
}
}
}

View File

@ -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,
)
}
}
}

View File

@ -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},
}

View File

@ -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"

View File

@ -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: ""
`),
},

View File

@ -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",
)
}
}
}
}

View File

@ -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" {

View File

@ -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,
)
}
}
}