From 11d444b00e0b51c3cc178c5486fa2c5cf6c35295 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Fri, 18 Jun 2021 00:31:27 +0300 Subject: [PATCH] kubeadm: remove versioned copies of the bootstrap token API and utils Given bootstraptoken/v1 is now a separate GV, there is no need to duplicate the API and utilities inside v1beta3 and the internal version. v1beta2 must continue to use its internal copy due, since output/v1alpha1 embeds the v1beta2.BootstrapToken object. See issue 2427 in k/kubeadm. --- .../app/apis/kubeadm/bootstraptokenhelpers.go | 159 ------ .../kubeadm/bootstraptokenhelpers_test.go | 456 ------------------ .../app/apis/kubeadm/bootstraptokenstring.go | 92 ---- .../apis/kubeadm/bootstraptokenstring_test.go | 249 ---------- .../kubeadm/v1beta3/bootstraptokenstring.go | 88 ---- .../v1beta3/bootstraptokenstring_test.go | 249 ---------- 6 files changed, 1293 deletions(-) delete mode 100644 cmd/kubeadm/app/apis/kubeadm/bootstraptokenhelpers.go delete mode 100644 cmd/kubeadm/app/apis/kubeadm/bootstraptokenhelpers_test.go delete mode 100644 cmd/kubeadm/app/apis/kubeadm/bootstraptokenstring.go delete mode 100644 cmd/kubeadm/app/apis/kubeadm/bootstraptokenstring_test.go delete mode 100644 cmd/kubeadm/app/apis/kubeadm/v1beta3/bootstraptokenstring.go delete mode 100644 cmd/kubeadm/app/apis/kubeadm/v1beta3/bootstraptokenstring_test.go diff --git a/cmd/kubeadm/app/apis/kubeadm/bootstraptokenhelpers.go b/cmd/kubeadm/app/apis/kubeadm/bootstraptokenhelpers.go deleted file mode 100644 index d8319865078..00000000000 --- a/cmd/kubeadm/app/apis/kubeadm/bootstraptokenhelpers.go +++ /dev/null @@ -1,159 +0,0 @@ -/* -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 ( - "sort" - "strings" - "time" - - "github.com/pkg/errors" - - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - bootstrapapi "k8s.io/cluster-bootstrap/token/api" - bootstraputil "k8s.io/cluster-bootstrap/token/util" - bootstrapsecretutil "k8s.io/cluster-bootstrap/util/secrets" -) - -// ToSecret converts the given BootstrapToken object to its Secret representation that -// may be submitted to the API Server in order to be stored. -func (bt *BootstrapToken) ToSecret() *v1.Secret { - return &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: bootstraputil.BootstrapTokenSecretName(bt.Token.ID), - Namespace: metav1.NamespaceSystem, - }, - Type: v1.SecretType(bootstrapapi.SecretTypeBootstrapToken), - Data: encodeTokenSecretData(bt, time.Now()), - } -} - -// encodeTokenSecretData takes the token discovery object and an optional duration and returns the .Data for the Secret -// now is passed in order to be able to used in unit testing -func encodeTokenSecretData(token *BootstrapToken, now time.Time) map[string][]byte { - data := map[string][]byte{ - bootstrapapi.BootstrapTokenIDKey: []byte(token.Token.ID), - bootstrapapi.BootstrapTokenSecretKey: []byte(token.Token.Secret), - } - - if len(token.Description) > 0 { - data[bootstrapapi.BootstrapTokenDescriptionKey] = []byte(token.Description) - } - - // If for some strange reason both token.TTL and token.Expires would be set - // (they are mutually exclusive in validation so this shouldn't be the case), - // token.Expires has higher priority, as can be seen in the logic here. - if token.Expires != nil { - // Format the expiration date accordingly - // TODO: This maybe should be a helper function in bootstraputil? - expirationString := token.Expires.Time.UTC().Format(time.RFC3339) - data[bootstrapapi.BootstrapTokenExpirationKey] = []byte(expirationString) - - } else if token.TTL != nil && token.TTL.Duration > 0 { - // Only if .Expires is unset, TTL might have an effect - // Get the current time, add the specified duration, and format it accordingly - expirationString := now.Add(token.TTL.Duration).UTC().Format(time.RFC3339) - data[bootstrapapi.BootstrapTokenExpirationKey] = []byte(expirationString) - } - - for _, usage := range token.Usages { - data[bootstrapapi.BootstrapTokenUsagePrefix+usage] = []byte("true") - } - - if len(token.Groups) > 0 { - data[bootstrapapi.BootstrapTokenExtraGroupsKey] = []byte(strings.Join(token.Groups, ",")) - } - return data -} - -// BootstrapTokenFromSecret returns a BootstrapToken object from the given Secret -func BootstrapTokenFromSecret(secret *v1.Secret) (*BootstrapToken, error) { - // Get the Token ID field from the Secret data - tokenID := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenIDKey) - if len(tokenID) == 0 { - return nil, errors.Errorf("bootstrap Token Secret has no token-id data: %s", secret.Name) - } - - // Enforce the right naming convention - if secret.Name != bootstraputil.BootstrapTokenSecretName(tokenID) { - return nil, errors.Errorf("bootstrap token name is not of the form '%s(token-id)'. Actual: %q. Expected: %q", - bootstrapapi.BootstrapTokenSecretPrefix, secret.Name, bootstraputil.BootstrapTokenSecretName(tokenID)) - } - - tokenSecret := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenSecretKey) - if len(tokenSecret) == 0 { - return nil, errors.Errorf("bootstrap Token Secret has no token-secret data: %s", secret.Name) - } - - // Create the BootstrapTokenString object based on the ID and Secret - bts, err := NewBootstrapTokenStringFromIDAndSecret(tokenID, tokenSecret) - if err != nil { - return nil, errors.Wrap(err, "bootstrap Token Secret is invalid and couldn't be parsed") - } - - // Get the description (if any) from the Secret - description := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenDescriptionKey) - - // Expiration time is optional, if not specified this implies the token - // never expires. - secretExpiration := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenExpirationKey) - var expires *metav1.Time - if len(secretExpiration) > 0 { - expTime, err := time.Parse(time.RFC3339, secretExpiration) - if err != nil { - return nil, errors.Wrapf(err, "can't parse expiration time of bootstrap token %q", secret.Name) - } - expires = &metav1.Time{Time: expTime} - } - - // Build an usages string slice from the Secret data - var usages []string - for k, v := range secret.Data { - // Skip all fields that don't include this prefix - if !strings.HasPrefix(k, bootstrapapi.BootstrapTokenUsagePrefix) { - continue - } - // Skip those that don't have this usage set to true - if string(v) != "true" { - continue - } - usages = append(usages, strings.TrimPrefix(k, bootstrapapi.BootstrapTokenUsagePrefix)) - } - // Only sort the slice if defined - if usages != nil { - sort.Strings(usages) - } - - // Get the extra groups information from the Secret - // It's done this way to make .Groups be nil in case there is no items, rather than an - // empty slice or an empty slice with a "" string only - var groups []string - groupsString := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenExtraGroupsKey) - g := strings.Split(groupsString, ",") - if len(g) > 0 && len(g[0]) > 0 { - groups = g - } - - return &BootstrapToken{ - Token: bts, - Description: description, - Expires: expires, - Usages: usages, - Groups: groups, - }, nil -} diff --git a/cmd/kubeadm/app/apis/kubeadm/bootstraptokenhelpers_test.go b/cmd/kubeadm/app/apis/kubeadm/bootstraptokenhelpers_test.go deleted file mode 100644 index 58eac205142..00000000000 --- a/cmd/kubeadm/app/apis/kubeadm/bootstraptokenhelpers_test.go +++ /dev/null @@ -1,456 +0,0 @@ -/* -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 { - t.Run(rt.bt.Token.ID, func(t *testing.T) { - 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 { - t.Run(rt.bt.Token.ID, func(t *testing.T) { - 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 { - 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) -} diff --git a/cmd/kubeadm/app/apis/kubeadm/bootstraptokenstring.go b/cmd/kubeadm/app/apis/kubeadm/bootstraptokenstring.go deleted file mode 100644 index 2c1175b9d8b..00000000000 --- a/cmd/kubeadm/app/apis/kubeadm/bootstraptokenstring.go +++ /dev/null @@ -1,92 +0,0 @@ -/* -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 holds the internal kubeadm API types -// Note: This file should be kept in sync with the similar one for the external API -// TODO: The BootstrapTokenString object should move out to either k8s.io/client-go or k8s.io/api in the future -// (probably as part of Bootstrap Tokens going GA). It should not be staged under the kubeadm API as it is now. -package kubeadm - -import ( - "fmt" - "strings" - - "github.com/pkg/errors" - - bootstrapapi "k8s.io/cluster-bootstrap/token/api" - bootstraputil "k8s.io/cluster-bootstrap/token/util" -) - -// BootstrapTokenString is a token of the format abcdef.abcdef0123456789 that is used -// for both validation of the practically of the API server from a joining node's point -// of view and as an authentication method for the node in the bootstrap phase of -// "kubeadm join". This token is and should be short-lived -type BootstrapTokenString struct { - ID string - Secret string -} - -// MarshalJSON implements the json.Marshaler interface. -func (bts BootstrapTokenString) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf(`"%s"`, bts.String())), nil -} - -// UnmarshalJSON implements the json.Unmarshaller interface. -func (bts *BootstrapTokenString) UnmarshalJSON(b []byte) error { - // If the token is represented as "", just return quickly without an error - if len(b) == 0 { - return nil - } - - // Remove unnecessary " characters coming from the JSON parser - token := strings.Replace(string(b), `"`, ``, -1) - // Convert the string Token to a BootstrapTokenString object - newbts, err := NewBootstrapTokenString(token) - if err != nil { - return err - } - bts.ID = newbts.ID - bts.Secret = newbts.Secret - return nil -} - -// String returns the string representation of the BootstrapTokenString -func (bts BootstrapTokenString) String() string { - if len(bts.ID) > 0 && len(bts.Secret) > 0 { - return bootstraputil.TokenFromIDAndSecret(bts.ID, bts.Secret) - } - return "" -} - -// NewBootstrapTokenString converts the given Bootstrap Token as a string -// to the BootstrapTokenString object used for serialization/deserialization -// and internal usage. It also automatically validates that the given token -// is of the right format -func NewBootstrapTokenString(token string) (*BootstrapTokenString, error) { - substrs := bootstraputil.BootstrapTokenRegexp.FindStringSubmatch(token) - // TODO: Add a constant for the 3 value here, and explain better why it's needed (other than because how the regexp parsin works) - if len(substrs) != 3 { - return nil, errors.Errorf("the bootstrap token %q was not of the form %q", token, bootstrapapi.BootstrapTokenPattern) - } - - return &BootstrapTokenString{ID: substrs[1], Secret: substrs[2]}, nil -} - -// NewBootstrapTokenStringFromIDAndSecret is a wrapper around NewBootstrapTokenString -// that allows the caller to specify the ID and Secret separately -func NewBootstrapTokenStringFromIDAndSecret(id, secret string) (*BootstrapTokenString, error) { - return NewBootstrapTokenString(bootstraputil.TokenFromIDAndSecret(id, secret)) -} diff --git a/cmd/kubeadm/app/apis/kubeadm/bootstraptokenstring_test.go b/cmd/kubeadm/app/apis/kubeadm/bootstraptokenstring_test.go deleted file mode 100644 index 040a2090508..00000000000 --- a/cmd/kubeadm/app/apis/kubeadm/bootstraptokenstring_test.go +++ /dev/null @@ -1,249 +0,0 @@ -/* -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" - - "github.com/pkg/errors" -) - -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, - ) - } - }) - } -} diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta3/bootstraptokenstring.go b/cmd/kubeadm/app/apis/kubeadm/v1beta3/bootstraptokenstring.go deleted file mode 100644 index 49792fdcd03..00000000000 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta3/bootstraptokenstring.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -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 v1beta3 - -import ( - "fmt" - "strings" - - "github.com/pkg/errors" - - bootstrapapi "k8s.io/cluster-bootstrap/token/api" - bootstraputil "k8s.io/cluster-bootstrap/token/util" -) - -// BootstrapTokenString is a token of the format abcdef.abcdef0123456789 that is used -// for both validation of the practically of the API server from a joining node's point -// of view and as an authentication method for the node in the bootstrap phase of -// "kubeadm join". This token is and should be short-lived -type BootstrapTokenString struct { - ID string `json:"-"` - Secret string `json:"-" datapolicy:"token"` -} - -// MarshalJSON implements the json.Marshaler interface. -func (bts BootstrapTokenString) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf(`"%s"`, bts.String())), nil -} - -// UnmarshalJSON implements the json.Unmarshaller interface. -func (bts *BootstrapTokenString) UnmarshalJSON(b []byte) error { - // If the token is represented as "", just return quickly without an error - if len(b) == 0 { - return nil - } - - // Remove unnecessary " characters coming from the JSON parser - token := strings.Replace(string(b), `"`, ``, -1) - // Convert the string Token to a BootstrapTokenString object - newbts, err := NewBootstrapTokenString(token) - if err != nil { - return err - } - bts.ID = newbts.ID - bts.Secret = newbts.Secret - return nil -} - -// String returns the string representation of the BootstrapTokenString -func (bts BootstrapTokenString) String() string { - if len(bts.ID) > 0 && len(bts.Secret) > 0 { - return bootstraputil.TokenFromIDAndSecret(bts.ID, bts.Secret) - } - return "" -} - -// NewBootstrapTokenString converts the given Bootstrap Token as a string -// to the BootstrapTokenString object used for serialization/deserialization -// and internal usage. It also automatically validates that the given token -// is of the right format -func NewBootstrapTokenString(token string) (*BootstrapTokenString, error) { - substrs := bootstraputil.BootstrapTokenRegexp.FindStringSubmatch(token) - // TODO: Add a constant for the 3 value here, and explain better why it's needed (other than because how the regexp parsin works) - if len(substrs) != 3 { - return nil, errors.Errorf("the bootstrap token %q was not of the form %q", token, bootstrapapi.BootstrapTokenPattern) - } - - return &BootstrapTokenString{ID: substrs[1], Secret: substrs[2]}, nil -} - -// NewBootstrapTokenStringFromIDAndSecret is a wrapper around NewBootstrapTokenString -// that allows the caller to specify the ID and Secret separately -func NewBootstrapTokenStringFromIDAndSecret(id, secret string) (*BootstrapTokenString, error) { - return NewBootstrapTokenString(bootstraputil.TokenFromIDAndSecret(id, secret)) -} diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta3/bootstraptokenstring_test.go b/cmd/kubeadm/app/apis/kubeadm/v1beta3/bootstraptokenstring_test.go deleted file mode 100644 index 9426c1922fd..00000000000 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta3/bootstraptokenstring_test.go +++ /dev/null @@ -1,249 +0,0 @@ -/* -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 v1beta3 - -import ( - "encoding/json" - "reflect" - "testing" - - "github.com/pkg/errors" -) - -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, - ) - } - }) - } -}