...

Source file src/k8s.io/kubernetes/cmd/kubeadm/app/apis/bootstraptoken/v1/utils_test.go

Documentation: k8s.io/kubernetes/cmd/kubeadm/app/apis/bootstraptoken/v1

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package v1
    18  
    19  import (
    20  	"encoding/json"
    21  	"reflect"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/pkg/errors"
    26  
    27  	v1 "k8s.io/api/core/v1"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	bootstrapapi "k8s.io/cluster-bootstrap/token/api"
    30  )
    31  
    32  func TestMarshalJSON(t *testing.T) {
    33  	var tests = []struct {
    34  		bts      BootstrapTokenString
    35  		expected string
    36  	}{
    37  		{BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, `"abcdef.abcdef0123456789"`},
    38  		{BootstrapTokenString{ID: "foo", Secret: "bar"}, `"foo.bar"`},
    39  		{BootstrapTokenString{ID: "h", Secret: "b"}, `"h.b"`},
    40  	}
    41  	for _, rt := range tests {
    42  		t.Run(rt.bts.ID, func(t *testing.T) {
    43  			b, err := json.Marshal(rt.bts)
    44  			if err != nil {
    45  				t.Fatalf("json.Marshal returned an unexpected error: %v", err)
    46  			}
    47  			if string(b) != rt.expected {
    48  				t.Errorf(
    49  					"failed BootstrapTokenString.MarshalJSON:\n\texpected: %s\n\t  actual: %s",
    50  					rt.expected,
    51  					string(b),
    52  				)
    53  			}
    54  		})
    55  	}
    56  }
    57  
    58  func TestUnmarshalJSON(t *testing.T) {
    59  	var tests = []struct {
    60  		input         string
    61  		bts           *BootstrapTokenString
    62  		expectedError bool
    63  	}{
    64  		{`"f.s"`, &BootstrapTokenString{}, true},
    65  		{`"abcdef."`, &BootstrapTokenString{}, true},
    66  		{`"abcdef:abcdef0123456789"`, &BootstrapTokenString{}, true},
    67  		{`abcdef.abcdef0123456789`, &BootstrapTokenString{}, true},
    68  		{`"abcdef.abcdef0123456789`, &BootstrapTokenString{}, true},
    69  		{`"abcdef.ABCDEF0123456789"`, &BootstrapTokenString{}, true},
    70  		{`"abcdef.abcdef0123456789"`, &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, false},
    71  		{`"123456.aabbccddeeffgghh"`, &BootstrapTokenString{ID: "123456", Secret: "aabbccddeeffgghh"}, false},
    72  	}
    73  	for _, rt := range tests {
    74  		t.Run(rt.input, func(t *testing.T) {
    75  			newbts := &BootstrapTokenString{}
    76  			err := json.Unmarshal([]byte(rt.input), newbts)
    77  			if (err != nil) != rt.expectedError {
    78  				t.Errorf("failed BootstrapTokenString.UnmarshalJSON:\n\texpected error: %t\n\t  actual error: %v", rt.expectedError, err)
    79  			} else if !reflect.DeepEqual(rt.bts, newbts) {
    80  				t.Errorf(
    81  					"failed BootstrapTokenString.UnmarshalJSON:\n\texpected: %v\n\t  actual: %v",
    82  					rt.bts,
    83  					newbts,
    84  				)
    85  			}
    86  		})
    87  	}
    88  }
    89  
    90  func TestJSONRoundtrip(t *testing.T) {
    91  	var tests = []struct {
    92  		input string
    93  		bts   *BootstrapTokenString
    94  	}{
    95  		{`"abcdef.abcdef0123456789"`, nil},
    96  		{"", &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}},
    97  	}
    98  	for _, rt := range tests {
    99  		t.Run(rt.input, func(t *testing.T) {
   100  			if err := roundtrip(rt.input, rt.bts); err != nil {
   101  				t.Errorf("failed BootstrapTokenString JSON roundtrip with error: %v", err)
   102  			}
   103  		})
   104  	}
   105  }
   106  
   107  func roundtrip(input string, bts *BootstrapTokenString) error {
   108  	var b []byte
   109  	var err error
   110  	newbts := &BootstrapTokenString{}
   111  	// If string input was specified, roundtrip like this: string -> (unmarshal) -> object -> (marshal) -> string
   112  	if len(input) > 0 {
   113  		if err := json.Unmarshal([]byte(input), newbts); err != nil {
   114  			return errors.Wrap(err, "expected no unmarshal error, got error")
   115  		}
   116  		if b, err = json.Marshal(newbts); err != nil {
   117  			return errors.Wrap(err, "expected no marshal error, got error")
   118  		}
   119  		if input != string(b) {
   120  			return errors.Errorf(
   121  				"expected token: %s\n\t  actual: %s",
   122  				input,
   123  				string(b),
   124  			)
   125  		}
   126  	} else { // Otherwise, roundtrip like this: object -> (marshal) -> string -> (unmarshal) -> object
   127  		if b, err = json.Marshal(bts); err != nil {
   128  			return errors.Wrap(err, "expected no marshal error, got error")
   129  		}
   130  		if err := json.Unmarshal(b, newbts); err != nil {
   131  			return errors.Wrap(err, "expected no unmarshal error, got error")
   132  		}
   133  		if !reflect.DeepEqual(bts, newbts) {
   134  			return errors.Errorf(
   135  				"expected object: %v\n\t  actual: %v",
   136  				bts,
   137  				newbts,
   138  			)
   139  		}
   140  	}
   141  	return nil
   142  }
   143  
   144  func TestTokenFromIDAndSecret(t *testing.T) {
   145  	var tests = []struct {
   146  		bts      BootstrapTokenString
   147  		expected string
   148  	}{
   149  		{BootstrapTokenString{ID: "foo", Secret: "bar"}, "foo.bar"},
   150  		{BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, "abcdef.abcdef0123456789"},
   151  		{BootstrapTokenString{ID: "h", Secret: "b"}, "h.b"},
   152  	}
   153  	for _, rt := range tests {
   154  		t.Run(rt.bts.ID, func(t *testing.T) {
   155  			actual := rt.bts.String()
   156  			if actual != rt.expected {
   157  				t.Errorf(
   158  					"failed BootstrapTokenString.String():\n\texpected: %s\n\t  actual: %s",
   159  					rt.expected,
   160  					actual,
   161  				)
   162  			}
   163  		})
   164  	}
   165  }
   166  
   167  func TestNewBootstrapTokenString(t *testing.T) {
   168  	var tests = []struct {
   169  		token         string
   170  		expectedError bool
   171  		bts           *BootstrapTokenString
   172  	}{
   173  		{token: "", expectedError: true, bts: nil},
   174  		{token: ".", expectedError: true, bts: nil},
   175  		{token: "1234567890123456789012", expectedError: true, bts: nil},   // invalid parcel size
   176  		{token: "12345.1234567890123456", expectedError: true, bts: nil},   // invalid parcel size
   177  		{token: ".1234567890123456", expectedError: true, bts: nil},        // invalid parcel size
   178  		{token: "123456.", expectedError: true, bts: nil},                  // invalid parcel size
   179  		{token: "123456:1234567890.123456", expectedError: true, bts: nil}, // invalid separation
   180  		{token: "abcdef:1234567890123456", expectedError: true, bts: nil},  // invalid separation
   181  		{token: "Abcdef.1234567890123456", expectedError: true, bts: nil},  // invalid token id
   182  		{token: "123456.AABBCCDDEEFFGGHH", expectedError: true, bts: nil},  // invalid token secret
   183  		{token: "123456.AABBCCD-EEFFGGHH", expectedError: true, bts: nil},  // invalid character
   184  		{token: "abc*ef.1234567890123456", expectedError: true, bts: nil},  // invalid character
   185  		{token: "abcdef.1234567890123456", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "1234567890123456"}},
   186  		{token: "123456.aabbccddeeffgghh", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "aabbccddeeffgghh"}},
   187  		{token: "abcdef.abcdef0123456789", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}},
   188  		{token: "123456.1234560123456789", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "1234560123456789"}},
   189  	}
   190  	for _, rt := range tests {
   191  		t.Run(rt.token, func(t *testing.T) {
   192  			actual, err := NewBootstrapTokenString(rt.token)
   193  			if (err != nil) != rt.expectedError {
   194  				t.Errorf(
   195  					"failed NewBootstrapTokenString for the token %q\n\texpected error: %t\n\t  actual error: %v",
   196  					rt.token,
   197  					rt.expectedError,
   198  					err,
   199  				)
   200  			} else if !reflect.DeepEqual(actual, rt.bts) {
   201  				t.Errorf(
   202  					"failed NewBootstrapTokenString for the token %q\n\texpected: %v\n\t  actual: %v",
   203  					rt.token,
   204  					rt.bts,
   205  					actual,
   206  				)
   207  			}
   208  		})
   209  	}
   210  }
   211  
   212  func TestNewBootstrapTokenStringFromIDAndSecret(t *testing.T) {
   213  	var tests = []struct {
   214  		id, secret    string
   215  		expectedError bool
   216  		bts           *BootstrapTokenString
   217  	}{
   218  		{id: "", secret: "", expectedError: true, bts: nil},
   219  		{id: "1234567890123456789012", secret: "", expectedError: true, bts: nil}, // invalid parcel size
   220  		{id: "12345", secret: "1234567890123456", expectedError: true, bts: nil},  // invalid parcel size
   221  		{id: "", secret: "1234567890123456", expectedError: true, bts: nil},       // invalid parcel size
   222  		{id: "123456", secret: "", expectedError: true, bts: nil},                 // invalid parcel size
   223  		{id: "Abcdef", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid token id
   224  		{id: "123456", secret: "AABBCCDDEEFFGGHH", expectedError: true, bts: nil}, // invalid token secret
   225  		{id: "123456", secret: "AABBCCD-EEFFGGHH", expectedError: true, bts: nil}, // invalid character
   226  		{id: "abc*ef", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid character
   227  		{id: "abcdef", secret: "1234567890123456", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "1234567890123456"}},
   228  		{id: "123456", secret: "aabbccddeeffgghh", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "aabbccddeeffgghh"}},
   229  		{id: "abcdef", secret: "abcdef0123456789", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}},
   230  		{id: "123456", secret: "1234560123456789", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "1234560123456789"}},
   231  	}
   232  	for _, rt := range tests {
   233  		t.Run(rt.id, func(t *testing.T) {
   234  			actual, err := NewBootstrapTokenStringFromIDAndSecret(rt.id, rt.secret)
   235  			if (err != nil) != rt.expectedError {
   236  				t.Errorf(
   237  					"failed NewBootstrapTokenStringFromIDAndSecret for the token with id %q and secret %q\n\texpected error: %t\n\t  actual error: %v",
   238  					rt.id,
   239  					rt.secret,
   240  					rt.expectedError,
   241  					err,
   242  				)
   243  			} else if !reflect.DeepEqual(actual, rt.bts) {
   244  				t.Errorf(
   245  					"failed NewBootstrapTokenStringFromIDAndSecret for the token with id %q and secret %q\n\texpected: %v\n\t  actual: %v",
   246  					rt.id,
   247  					rt.secret,
   248  					rt.bts,
   249  					actual,
   250  				)
   251  			}
   252  		})
   253  	}
   254  }
   255  
   256  // This timestamp is used as the reference value when computing expiration dates based on TTLs in these unit tests
   257  var refTime = time.Date(1970, time.January, 1, 1, 1, 1, 0, time.UTC)
   258  
   259  func TestBootstrapTokenToSecret(t *testing.T) {
   260  	var tests = []struct {
   261  		bt     *BootstrapToken
   262  		secret *v1.Secret
   263  	}{
   264  		{
   265  			&BootstrapToken{ // all together
   266  				Token:       &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
   267  				Description: "foo",
   268  				Expires: &metav1.Time{
   269  					Time: refTime,
   270  				},
   271  				Usages: []string{"signing", "authentication"},
   272  				Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"},
   273  			},
   274  			&v1.Secret{
   275  				ObjectMeta: metav1.ObjectMeta{
   276  					Name:      "bootstrap-token-abcdef",
   277  					Namespace: "kube-system",
   278  				},
   279  				Type: bootstrapapi.SecretTypeBootstrapToken,
   280  				Data: map[string][]byte{
   281  					"token-id":                       []byte("abcdef"),
   282  					"token-secret":                   []byte("abcdef0123456789"),
   283  					"description":                    []byte("foo"),
   284  					"expiration":                     []byte(refTime.Format(time.RFC3339)),
   285  					"usage-bootstrap-signing":        []byte("true"),
   286  					"usage-bootstrap-authentication": []byte("true"),
   287  					"auth-extra-groups":              []byte("system:bootstrappers,system:bootstrappers:foo"),
   288  				},
   289  			},
   290  		},
   291  	}
   292  	for _, rt := range tests {
   293  		t.Run(rt.bt.Token.ID, func(t *testing.T) {
   294  			actual := BootstrapTokenToSecret(rt.bt)
   295  			if !reflect.DeepEqual(actual, rt.secret) {
   296  				t.Errorf(
   297  					"failed BootstrapTokenToSecret():\n\texpected: %v\n\t  actual: %v",
   298  					rt.secret,
   299  					actual,
   300  				)
   301  			}
   302  		})
   303  	}
   304  }
   305  
   306  func TestBootstrapTokenToSecretRoundtrip(t *testing.T) {
   307  	var tests = []struct {
   308  		bt *BootstrapToken
   309  	}{
   310  		{
   311  			&BootstrapToken{
   312  				Token:       &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
   313  				Description: "foo",
   314  				Expires: &metav1.Time{
   315  					Time: refTime,
   316  				},
   317  				Usages: []string{"authentication", "signing"},
   318  				Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"},
   319  			},
   320  		},
   321  	}
   322  	for _, rt := range tests {
   323  		t.Run(rt.bt.Token.ID, func(t *testing.T) {
   324  			actual, err := BootstrapTokenFromSecret(BootstrapTokenToSecret(rt.bt))
   325  			if err != nil {
   326  				t.Errorf("failed BootstrapToken to Secret roundtrip with error: %v", err)
   327  			}
   328  			if !reflect.DeepEqual(actual, rt.bt) {
   329  				t.Errorf(
   330  					"failed BootstrapToken to Secret roundtrip:\n\texpected: %v\n\t  actual: %v",
   331  					rt.bt,
   332  					actual,
   333  				)
   334  			}
   335  		})
   336  	}
   337  }
   338  
   339  func TestEncodeTokenSecretData(t *testing.T) {
   340  	var tests = []struct {
   341  		name string
   342  		bt   *BootstrapToken
   343  		data map[string][]byte
   344  	}{
   345  		{
   346  			"the minimum amount of information needed to be specified",
   347  			&BootstrapToken{
   348  				Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
   349  			},
   350  			map[string][]byte{
   351  				"token-id":     []byte("abcdef"),
   352  				"token-secret": []byte("abcdef0123456789"),
   353  			},
   354  		},
   355  		{
   356  			"adds description",
   357  			&BootstrapToken{
   358  				Token:       &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
   359  				Description: "foo",
   360  			},
   361  			map[string][]byte{
   362  				"token-id":     []byte("abcdef"),
   363  				"token-secret": []byte("abcdef0123456789"),
   364  				"description":  []byte("foo"),
   365  			},
   366  		},
   367  		{
   368  			"adds ttl",
   369  			&BootstrapToken{
   370  				Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
   371  				TTL: &metav1.Duration{
   372  					Duration: mustParseDuration("2h", t),
   373  				},
   374  			},
   375  			map[string][]byte{
   376  				"token-id":     []byte("abcdef"),
   377  				"token-secret": []byte("abcdef0123456789"),
   378  				"expiration":   []byte(refTime.Add(mustParseDuration("2h", t)).Format(time.RFC3339)),
   379  			},
   380  		},
   381  		{
   382  			"adds expiration",
   383  			&BootstrapToken{
   384  				Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
   385  				Expires: &metav1.Time{
   386  					Time: refTime,
   387  				},
   388  			},
   389  			map[string][]byte{
   390  				"token-id":     []byte("abcdef"),
   391  				"token-secret": []byte("abcdef0123456789"),
   392  				"expiration":   []byte(refTime.Format(time.RFC3339)),
   393  			},
   394  		},
   395  		{
   396  			"adds ttl and expiration, should favor expiration",
   397  			&BootstrapToken{
   398  				Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
   399  				TTL: &metav1.Duration{
   400  					Duration: mustParseDuration("2h", t),
   401  				},
   402  				Expires: &metav1.Time{
   403  					Time: refTime,
   404  				},
   405  			},
   406  			map[string][]byte{
   407  				"token-id":     []byte("abcdef"),
   408  				"token-secret": []byte("abcdef0123456789"),
   409  				"expiration":   []byte(refTime.Format(time.RFC3339)),
   410  			},
   411  		},
   412  		{
   413  			"adds usages",
   414  			&BootstrapToken{
   415  				Token:  &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
   416  				Usages: []string{"authentication", "signing"},
   417  			},
   418  			map[string][]byte{
   419  				"token-id":                       []byte("abcdef"),
   420  				"token-secret":                   []byte("abcdef0123456789"),
   421  				"usage-bootstrap-signing":        []byte("true"),
   422  				"usage-bootstrap-authentication": []byte("true"),
   423  			},
   424  		},
   425  		{
   426  			"adds groups",
   427  			&BootstrapToken{
   428  				Token:  &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
   429  				Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"},
   430  			},
   431  			map[string][]byte{
   432  				"token-id":          []byte("abcdef"),
   433  				"token-secret":      []byte("abcdef0123456789"),
   434  				"auth-extra-groups": []byte("system:bootstrappers,system:bootstrappers:foo"),
   435  			},
   436  		},
   437  		{
   438  			"all together",
   439  			&BootstrapToken{
   440  				Token:       &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
   441  				Description: "foo",
   442  				TTL: &metav1.Duration{
   443  					Duration: mustParseDuration("2h", t),
   444  				},
   445  				Expires: &metav1.Time{
   446  					Time: refTime,
   447  				},
   448  				Usages: []string{"authentication", "signing"},
   449  				Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"},
   450  			},
   451  			map[string][]byte{
   452  				"token-id":                       []byte("abcdef"),
   453  				"token-secret":                   []byte("abcdef0123456789"),
   454  				"description":                    []byte("foo"),
   455  				"expiration":                     []byte(refTime.Format(time.RFC3339)),
   456  				"usage-bootstrap-signing":        []byte("true"),
   457  				"usage-bootstrap-authentication": []byte("true"),
   458  				"auth-extra-groups":              []byte("system:bootstrappers,system:bootstrappers:foo"),
   459  			},
   460  		},
   461  	}
   462  	for _, rt := range tests {
   463  		t.Run(rt.name, func(t *testing.T) {
   464  			actual := encodeTokenSecretData(rt.bt, refTime)
   465  			if !reflect.DeepEqual(actual, rt.data) {
   466  				t.Errorf(
   467  					"failed encodeTokenSecretData:\n\texpected: %v\n\t  actual: %v",
   468  					rt.data,
   469  					actual,
   470  				)
   471  			}
   472  		})
   473  	}
   474  }
   475  
   476  func mustParseDuration(durationStr string, t *testing.T) time.Duration {
   477  	d, err := time.ParseDuration(durationStr)
   478  	if err != nil {
   479  		t.Fatalf("couldn't parse duration %q: %v", durationStr, err)
   480  	}
   481  	return d
   482  }
   483  
   484  func TestBootstrapTokenFromSecret(t *testing.T) {
   485  	var tests = []struct {
   486  		desc          string
   487  		name          string
   488  		data          map[string][]byte
   489  		bt            *BootstrapToken
   490  		expectedError bool
   491  	}{
   492  		{
   493  			"minimum information",
   494  			"bootstrap-token-abcdef",
   495  			map[string][]byte{
   496  				"token-id":     []byte("abcdef"),
   497  				"token-secret": []byte("abcdef0123456789"),
   498  			},
   499  			&BootstrapToken{
   500  				Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
   501  			},
   502  			false,
   503  		},
   504  		{
   505  			"invalid token id",
   506  			"bootstrap-token-abcdef",
   507  			map[string][]byte{
   508  				"token-id":     []byte("abcdeF"),
   509  				"token-secret": []byte("abcdef0123456789"),
   510  			},
   511  			nil,
   512  			true,
   513  		},
   514  		{
   515  			"invalid secret naming",
   516  			"foo",
   517  			map[string][]byte{
   518  				"token-id":     []byte("abcdef"),
   519  				"token-secret": []byte("abcdef0123456789"),
   520  			},
   521  			nil,
   522  			true,
   523  		},
   524  		{
   525  			"invalid token secret",
   526  			"bootstrap-token-abcdef",
   527  			map[string][]byte{
   528  				"token-id":     []byte("abcdef"),
   529  				"token-secret": []byte("ABCDEF0123456789"),
   530  			},
   531  			nil,
   532  			true,
   533  		},
   534  		{
   535  			"adds description",
   536  			"bootstrap-token-abcdef",
   537  			map[string][]byte{
   538  				"token-id":     []byte("abcdef"),
   539  				"token-secret": []byte("abcdef0123456789"),
   540  				"description":  []byte("foo"),
   541  			},
   542  			&BootstrapToken{
   543  				Token:       &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
   544  				Description: "foo",
   545  			},
   546  			false,
   547  		},
   548  		{
   549  			"adds expiration",
   550  			"bootstrap-token-abcdef",
   551  			map[string][]byte{
   552  				"token-id":     []byte("abcdef"),
   553  				"token-secret": []byte("abcdef0123456789"),
   554  				"expiration":   []byte(refTime.Format(time.RFC3339)),
   555  			},
   556  			&BootstrapToken{
   557  				Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
   558  				Expires: &metav1.Time{
   559  					Time: refTime,
   560  				},
   561  			},
   562  			false,
   563  		},
   564  		{
   565  			"invalid expiration",
   566  			"bootstrap-token-abcdef",
   567  			map[string][]byte{
   568  				"token-id":     []byte("abcdef"),
   569  				"token-secret": []byte("abcdef0123456789"),
   570  				"expiration":   []byte("invalid date"),
   571  			},
   572  			nil,
   573  			true,
   574  		},
   575  		{
   576  			"adds usages",
   577  			"bootstrap-token-abcdef",
   578  			map[string][]byte{
   579  				"token-id":                       []byte("abcdef"),
   580  				"token-secret":                   []byte("abcdef0123456789"),
   581  				"usage-bootstrap-signing":        []byte("true"),
   582  				"usage-bootstrap-authentication": []byte("true"),
   583  			},
   584  			&BootstrapToken{
   585  				Token:  &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
   586  				Usages: []string{"authentication", "signing"},
   587  			},
   588  			false,
   589  		},
   590  		{
   591  			"should ignore usages that aren't set to true",
   592  			"bootstrap-token-abcdef",
   593  			map[string][]byte{
   594  				"token-id":                       []byte("abcdef"),
   595  				"token-secret":                   []byte("abcdef0123456789"),
   596  				"usage-bootstrap-signing":        []byte("true"),
   597  				"usage-bootstrap-authentication": []byte("true"),
   598  				"usage-bootstrap-foo":            []byte("false"),
   599  				"usage-bootstrap-bar":            []byte(""),
   600  			},
   601  			&BootstrapToken{
   602  				Token:  &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
   603  				Usages: []string{"authentication", "signing"},
   604  			},
   605  			false,
   606  		},
   607  		{
   608  			"adds groups",
   609  			"bootstrap-token-abcdef",
   610  			map[string][]byte{
   611  				"token-id":          []byte("abcdef"),
   612  				"token-secret":      []byte("abcdef0123456789"),
   613  				"auth-extra-groups": []byte("system:bootstrappers,system:bootstrappers:foo"),
   614  			},
   615  			&BootstrapToken{
   616  				Token:  &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
   617  				Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"},
   618  			},
   619  			false,
   620  		},
   621  		{
   622  			"all fields set",
   623  			"bootstrap-token-abcdef",
   624  			map[string][]byte{
   625  				"token-id":                       []byte("abcdef"),
   626  				"token-secret":                   []byte("abcdef0123456789"),
   627  				"description":                    []byte("foo"),
   628  				"expiration":                     []byte(refTime.Format(time.RFC3339)),
   629  				"usage-bootstrap-signing":        []byte("true"),
   630  				"usage-bootstrap-authentication": []byte("true"),
   631  				"auth-extra-groups":              []byte("system:bootstrappers,system:bootstrappers:foo"),
   632  			},
   633  			&BootstrapToken{
   634  				Token:       &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
   635  				Description: "foo",
   636  				Expires: &metav1.Time{
   637  					Time: refTime,
   638  				},
   639  				Usages: []string{"authentication", "signing"},
   640  				Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"},
   641  			},
   642  			false,
   643  		},
   644  	}
   645  	for _, rt := range tests {
   646  		t.Run(rt.desc, func(t *testing.T) {
   647  			actual, err := BootstrapTokenFromSecret(&v1.Secret{
   648  				ObjectMeta: metav1.ObjectMeta{
   649  					Name:      rt.name,
   650  					Namespace: "kube-system",
   651  				},
   652  				Type: bootstrapapi.SecretTypeBootstrapToken,
   653  				Data: rt.data,
   654  			})
   655  			if (err != nil) != rt.expectedError {
   656  				t.Errorf(
   657  					"failed BootstrapTokenFromSecret\n\texpected error: %t\n\t  actual error: %v",
   658  					rt.expectedError,
   659  					err,
   660  				)
   661  			} else {
   662  				if actual == nil && rt.bt == nil {
   663  					// if both pointers are nil, it's okay, just continue
   664  					return
   665  				}
   666  				// If one of the pointers is defined but the other isn't, throw error. If both pointers are defined but unequal, throw error
   667  				if (actual == nil && rt.bt != nil) || (actual != nil && rt.bt == nil) || !reflect.DeepEqual(*actual, *rt.bt) {
   668  					t.Errorf(
   669  						"failed BootstrapTokenFromSecret\n\texpected: %s\n\t  actual: %s",
   670  						jsonMarshal(rt.bt),
   671  						jsonMarshal(actual),
   672  					)
   673  				}
   674  			}
   675  		})
   676  	}
   677  }
   678  
   679  func jsonMarshal(bt *BootstrapToken) string {
   680  	b, _ := json.Marshal(*bt)
   681  	return string(b)
   682  }
   683  

View as plain text