mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 19:01:49 +00:00
682 lines
21 KiB
Go
682 lines
21 KiB
Go
/*
|
|
Copyright 2021 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 v1
|
|
|
|
import (
|
|
"encoding/json"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
v1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
)
|
|
|
|
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 {
|
|
t.Run(rt.bts.ID, func(t *testing.T) {
|
|
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 {
|
|
t.Run(rt.input, func(t *testing.T) {
|
|
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 {
|
|
t.Run(rt.input, func(t *testing.T) {
|
|
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 errors.Wrap(err, "expected no unmarshal error, got error")
|
|
}
|
|
if b, err = json.Marshal(newbts); err != nil {
|
|
return errors.Wrap(err, "expected no marshal error, got error")
|
|
}
|
|
if input != string(b) {
|
|
return errors.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 errors.Wrap(err, "expected no marshal error, got error")
|
|
}
|
|
if err := json.Unmarshal(b, newbts); err != nil {
|
|
return errors.Wrap(err, "expected no unmarshal error, got error")
|
|
}
|
|
if !reflect.DeepEqual(bts, newbts) {
|
|
return errors.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 {
|
|
t.Run(rt.bts.ID, func(t *testing.T) {
|
|
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 {
|
|
t.Run(rt.token, func(t *testing.T) {
|
|
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 {
|
|
t.Run(rt.id, func(t *testing.T) {
|
|
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,
|
|
)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// 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 TestBootstrapTokenToSecret(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 {
|
|
t.Run(rt.bt.Token.ID, func(t *testing.T) {
|
|
actual := BootstrapTokenToSecret(rt.bt)
|
|
if !reflect.DeepEqual(actual, rt.secret) {
|
|
t.Errorf(
|
|
"failed BootstrapTokenToSecret():\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 {
|
|
t.Run(rt.bt.Token.ID, func(t *testing.T) {
|
|
actual, err := BootstrapTokenFromSecret(BootstrapTokenToSecret(rt.bt))
|
|
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 {
|
|
name string
|
|
bt *BootstrapToken
|
|
data map[string][]byte
|
|
}{
|
|
{
|
|
"the minimum amount of information needed to be specified",
|
|
&BootstrapToken{
|
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
|
},
|
|
map[string][]byte{
|
|
"token-id": []byte("abcdef"),
|
|
"token-secret": []byte("abcdef0123456789"),
|
|
},
|
|
},
|
|
{
|
|
"adds description",
|
|
&BootstrapToken{
|
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
|
Description: "foo",
|
|
},
|
|
map[string][]byte{
|
|
"token-id": []byte("abcdef"),
|
|
"token-secret": []byte("abcdef0123456789"),
|
|
"description": []byte("foo"),
|
|
},
|
|
},
|
|
{
|
|
"adds ttl",
|
|
&BootstrapToken{
|
|
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)),
|
|
},
|
|
},
|
|
{
|
|
"adds expiration",
|
|
&BootstrapToken{
|
|
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)),
|
|
},
|
|
},
|
|
{
|
|
"adds ttl and expiration, should favor expiration",
|
|
&BootstrapToken{
|
|
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)),
|
|
},
|
|
},
|
|
{
|
|
"adds usages",
|
|
&BootstrapToken{
|
|
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"),
|
|
},
|
|
},
|
|
{
|
|
"adds groups",
|
|
&BootstrapToken{
|
|
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"),
|
|
},
|
|
},
|
|
{
|
|
"all together",
|
|
&BootstrapToken{
|
|
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 {
|
|
t.Run(rt.name, func(t *testing.T) {
|
|
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 {
|
|
desc string
|
|
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 {
|
|
t.Run(rt.desc, func(t *testing.T) {
|
|
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
|
|
return
|
|
}
|
|
// 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)
|
|
}
|