mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	Merge pull request #77211 from dixudx/bootstrap_token_refactor
Bootstrap token refactor
This commit is contained in:
		@@ -26,6 +26,7 @@ go_library(
 | 
				
			|||||||
        "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/cluster-bootstrap/token/api:go_default_library",
 | 
					        "//staging/src/k8s.io/cluster-bootstrap/token/api:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/cluster-bootstrap/token/util:go_default_library",
 | 
					        "//staging/src/k8s.io/cluster-bootstrap/token/util:go_default_library",
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/cluster-bootstrap/util/secrets:go_default_library",
 | 
				
			||||||
        "//vendor/github.com/pkg/errors:go_default_library",
 | 
					        "//vendor/github.com/pkg/errors:go_default_library",
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,7 @@ import (
 | 
				
			|||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
	bootstrapapi "k8s.io/cluster-bootstrap/token/api"
 | 
						bootstrapapi "k8s.io/cluster-bootstrap/token/api"
 | 
				
			||||||
	bootstraputil "k8s.io/cluster-bootstrap/token/util"
 | 
						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
 | 
					// ToSecret converts the given BootstrapToken object to its Secret representation that
 | 
				
			||||||
@@ -55,7 +56,7 @@ func encodeTokenSecretData(token *BootstrapToken, now time.Time) map[string][]by
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// If for some strange reason both token.TTL and token.Expires would be set
 | 
						// If for some strange reason both token.TTL and token.Expires would be set
 | 
				
			||||||
	// (they are mutually exlusive in validation so this shouldn't be the case),
 | 
						// (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.
 | 
						// token.Expires has higher priority, as can be seen in the logic here.
 | 
				
			||||||
	if token.Expires != nil {
 | 
						if token.Expires != nil {
 | 
				
			||||||
		// Format the expiration date accordingly
 | 
							// Format the expiration date accordingly
 | 
				
			||||||
@@ -83,7 +84,7 @@ func encodeTokenSecretData(token *BootstrapToken, now time.Time) map[string][]by
 | 
				
			|||||||
// BootstrapTokenFromSecret returns a BootstrapToken object from the given Secret
 | 
					// BootstrapTokenFromSecret returns a BootstrapToken object from the given Secret
 | 
				
			||||||
func BootstrapTokenFromSecret(secret *v1.Secret) (*BootstrapToken, error) {
 | 
					func BootstrapTokenFromSecret(secret *v1.Secret) (*BootstrapToken, error) {
 | 
				
			||||||
	// Get the Token ID field from the Secret data
 | 
						// Get the Token ID field from the Secret data
 | 
				
			||||||
	tokenID := getSecretString(secret, bootstrapapi.BootstrapTokenIDKey)
 | 
						tokenID := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenIDKey)
 | 
				
			||||||
	if len(tokenID) == 0 {
 | 
						if len(tokenID) == 0 {
 | 
				
			||||||
		return nil, errors.Errorf("bootstrap Token Secret has no token-id data: %s", secret.Name)
 | 
							return nil, errors.Errorf("bootstrap Token Secret has no token-id data: %s", secret.Name)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -94,7 +95,7 @@ func BootstrapTokenFromSecret(secret *v1.Secret) (*BootstrapToken, error) {
 | 
				
			|||||||
			bootstrapapi.BootstrapTokenSecretPrefix, secret.Name, bootstraputil.BootstrapTokenSecretName(tokenID))
 | 
								bootstrapapi.BootstrapTokenSecretPrefix, secret.Name, bootstraputil.BootstrapTokenSecretName(tokenID))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tokenSecret := getSecretString(secret, bootstrapapi.BootstrapTokenSecretKey)
 | 
						tokenSecret := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenSecretKey)
 | 
				
			||||||
	if len(tokenSecret) == 0 {
 | 
						if len(tokenSecret) == 0 {
 | 
				
			||||||
		return nil, errors.Errorf("bootstrap Token Secret has no token-secret data: %s", secret.Name)
 | 
							return nil, errors.Errorf("bootstrap Token Secret has no token-secret data: %s", secret.Name)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -106,11 +107,11 @@ func BootstrapTokenFromSecret(secret *v1.Secret) (*BootstrapToken, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get the description (if any) from the Secret
 | 
						// Get the description (if any) from the Secret
 | 
				
			||||||
	description := getSecretString(secret, bootstrapapi.BootstrapTokenDescriptionKey)
 | 
						description := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenDescriptionKey)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Expiration time is optional, if not specified this implies the token
 | 
						// Expiration time is optional, if not specified this implies the token
 | 
				
			||||||
	// never expires.
 | 
						// never expires.
 | 
				
			||||||
	secretExpiration := getSecretString(secret, bootstrapapi.BootstrapTokenExpirationKey)
 | 
						secretExpiration := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenExpirationKey)
 | 
				
			||||||
	var expires *metav1.Time
 | 
						var expires *metav1.Time
 | 
				
			||||||
	if len(secretExpiration) > 0 {
 | 
						if len(secretExpiration) > 0 {
 | 
				
			||||||
		expTime, err := time.Parse(time.RFC3339, secretExpiration)
 | 
							expTime, err := time.Parse(time.RFC3339, secretExpiration)
 | 
				
			||||||
@@ -142,7 +143,7 @@ func BootstrapTokenFromSecret(secret *v1.Secret) (*BootstrapToken, error) {
 | 
				
			|||||||
	// It's done this way to make .Groups be nil in case there is no items, rather than an
 | 
						// 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
 | 
						// empty slice or an empty slice with a "" string only
 | 
				
			||||||
	var groups []string
 | 
						var groups []string
 | 
				
			||||||
	groupsString := getSecretString(secret, bootstrapapi.BootstrapTokenExtraGroupsKey)
 | 
						groupsString := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenExtraGroupsKey)
 | 
				
			||||||
	g := strings.Split(groupsString, ",")
 | 
						g := strings.Split(groupsString, ",")
 | 
				
			||||||
	if len(g) > 0 && len(g[0]) > 0 {
 | 
						if len(g) > 0 && len(g[0]) > 0 {
 | 
				
			||||||
		groups = g
 | 
							groups = g
 | 
				
			||||||
@@ -156,14 +157,3 @@ func BootstrapTokenFromSecret(secret *v1.Secret) (*BootstrapToken, error) {
 | 
				
			|||||||
		Groups:      groups,
 | 
							Groups:      groups,
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
// getSecretString returns the string value for the given key in the specified Secret
 | 
					 | 
				
			||||||
func getSecretString(secret *v1.Secret, key string) string {
 | 
					 | 
				
			||||||
	if secret.Data == nil {
 | 
					 | 
				
			||||||
		return ""
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if val, ok := secret.Data[key]; ok {
 | 
					 | 
				
			||||||
		return string(val)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return ""
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -454,55 +454,3 @@ func jsonMarshal(bt *BootstrapToken) string {
 | 
				
			|||||||
	b, _ := json.Marshal(*bt)
 | 
						b, _ := json.Marshal(*bt)
 | 
				
			||||||
	return string(b)
 | 
						return string(b)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestGetSecretString(t *testing.T) {
 | 
					 | 
				
			||||||
	var tests = []struct {
 | 
					 | 
				
			||||||
		name        string
 | 
					 | 
				
			||||||
		secret      *v1.Secret
 | 
					 | 
				
			||||||
		key         string
 | 
					 | 
				
			||||||
		expectedVal string
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name: "existing key",
 | 
					 | 
				
			||||||
			secret: &v1.Secret{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{Name: "foo"},
 | 
					 | 
				
			||||||
				Data: map[string][]byte{
 | 
					 | 
				
			||||||
					"foo": []byte("bar"),
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			key:         "foo",
 | 
					 | 
				
			||||||
			expectedVal: "bar",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name: "non-existing key",
 | 
					 | 
				
			||||||
			secret: &v1.Secret{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{Name: "foo"},
 | 
					 | 
				
			||||||
				Data: map[string][]byte{
 | 
					 | 
				
			||||||
					"foo": []byte("bar"),
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			key:         "baz",
 | 
					 | 
				
			||||||
			expectedVal: "",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name: "no data",
 | 
					 | 
				
			||||||
			secret: &v1.Secret{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{Name: "foo"},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			key:         "foo",
 | 
					 | 
				
			||||||
			expectedVal: "",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, rt := range tests {
 | 
					 | 
				
			||||||
		t.Run(rt.name, func(t *testing.T) {
 | 
					 | 
				
			||||||
			actual := getSecretString(rt.secret, rt.key)
 | 
					 | 
				
			||||||
			if actual != rt.expectedVal {
 | 
					 | 
				
			||||||
				t.Errorf(
 | 
					 | 
				
			||||||
					"failed getSecretString:\n\texpected: %s\n\t  actual: %s",
 | 
					 | 
				
			||||||
					rt.expectedVal,
 | 
					 | 
				
			||||||
					actual,
 | 
					 | 
				
			||||||
				)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,6 +59,7 @@ go_library(
 | 
				
			|||||||
        "//staging/src/k8s.io/client-go/tools/cache:go_default_library",
 | 
					        "//staging/src/k8s.io/client-go/tools/cache:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
 | 
					        "//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/cluster-bootstrap/token/api:go_default_library",
 | 
					        "//staging/src/k8s.io/cluster-bootstrap/token/api:go_default_library",
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/cluster-bootstrap/util/secrets:go_default_library",
 | 
				
			||||||
        "//vendor/gopkg.in/square/go-jose.v2:go_default_library",
 | 
					        "//vendor/gopkg.in/square/go-jose.v2:go_default_library",
 | 
				
			||||||
        "//vendor/k8s.io/klog:go_default_library",
 | 
					        "//vendor/k8s.io/klog:go_default_library",
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,7 @@ import (
 | 
				
			|||||||
	"k8s.io/client-go/tools/cache"
 | 
						"k8s.io/client-go/tools/cache"
 | 
				
			||||||
	"k8s.io/client-go/util/workqueue"
 | 
						"k8s.io/client-go/util/workqueue"
 | 
				
			||||||
	bootstrapapi "k8s.io/cluster-bootstrap/token/api"
 | 
						bootstrapapi "k8s.io/cluster-bootstrap/token/api"
 | 
				
			||||||
 | 
						bootstrapsecretutil "k8s.io/cluster-bootstrap/util/secrets"
 | 
				
			||||||
	"k8s.io/klog"
 | 
						"k8s.io/klog"
 | 
				
			||||||
	api "k8s.io/kubernetes/pkg/apis/core"
 | 
						api "k8s.io/kubernetes/pkg/apis/core"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller"
 | 
						"k8s.io/kubernetes/pkg/controller"
 | 
				
			||||||
@@ -187,7 +188,7 @@ func (tc *TokenCleaner) syncFunc(key string) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (tc *TokenCleaner) evalSecret(o interface{}) {
 | 
					func (tc *TokenCleaner) evalSecret(o interface{}) {
 | 
				
			||||||
	secret := o.(*v1.Secret)
 | 
						secret := o.(*v1.Secret)
 | 
				
			||||||
	if isSecretExpired(secret) {
 | 
						if bootstrapsecretutil.HasExpired(secret, time.Now()) {
 | 
				
			||||||
		klog.V(3).Infof("Deleting expired secret %s/%s", secret.Namespace, secret.Name)
 | 
							klog.V(3).Infof("Deleting expired secret %s/%s", secret.Namespace, secret.Name)
 | 
				
			||||||
		var options *metav1.DeleteOptions
 | 
							var options *metav1.DeleteOptions
 | 
				
			||||||
		if len(secret.UID) > 0 {
 | 
							if len(secret.UID) > 0 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,46 +17,23 @@ limitations under the License.
 | 
				
			|||||||
package bootstrap
 | 
					package bootstrap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"regexp"
 | 
					 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"k8s.io/klog"
 | 
						"k8s.io/klog"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"k8s.io/api/core/v1"
 | 
						"k8s.io/api/core/v1"
 | 
				
			||||||
	bootstrapapi "k8s.io/cluster-bootstrap/token/api"
 | 
						bootstrapapi "k8s.io/cluster-bootstrap/token/api"
 | 
				
			||||||
 | 
						bootstrapsecretutil "k8s.io/cluster-bootstrap/util/secrets"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var namePattern = `^` + regexp.QuoteMeta(bootstrapapi.BootstrapTokenSecretPrefix) + `([a-z0-9]{6})$`
 | 
					 | 
				
			||||||
var nameRegExp = regexp.MustCompile(namePattern)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getSecretString gets a string value from a secret.  If there is an error or
 | 
					 | 
				
			||||||
// if the key doesn't exist, an empty string is returned.
 | 
					 | 
				
			||||||
func getSecretString(secret *v1.Secret, key string) string {
 | 
					 | 
				
			||||||
	data, ok := secret.Data[key]
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		return ""
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return string(data)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// parseSecretName parses the name of the secret to extract the secret ID.
 | 
					 | 
				
			||||||
func parseSecretName(name string) (secretID string, ok bool) {
 | 
					 | 
				
			||||||
	r := nameRegExp.FindStringSubmatch(name)
 | 
					 | 
				
			||||||
	if r == nil {
 | 
					 | 
				
			||||||
		return "", false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return r[1], true
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func validateSecretForSigning(secret *v1.Secret) (tokenID, tokenSecret string, ok bool) {
 | 
					func validateSecretForSigning(secret *v1.Secret) (tokenID, tokenSecret string, ok bool) {
 | 
				
			||||||
	nameTokenID, ok := parseSecretName(secret.Name)
 | 
						nameTokenID, ok := bootstrapsecretutil.ParseName(secret.Name)
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		klog.V(3).Infof("Invalid secret name: %s. Must be of form %s<secret-id>.", secret.Name, bootstrapapi.BootstrapTokenSecretPrefix)
 | 
							klog.V(3).Infof("Invalid secret name: %s. Must be of form %s<secret-id>.", secret.Name, bootstrapapi.BootstrapTokenSecretPrefix)
 | 
				
			||||||
		return "", "", false
 | 
							return "", "", false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tokenID = getSecretString(secret, bootstrapapi.BootstrapTokenIDKey)
 | 
						tokenID = bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenIDKey)
 | 
				
			||||||
	if len(tokenID) == 0 {
 | 
						if len(tokenID) == 0 {
 | 
				
			||||||
		klog.V(3).Infof("No %s key in %s/%s Secret", bootstrapapi.BootstrapTokenIDKey, secret.Namespace, secret.Name)
 | 
							klog.V(3).Infof("No %s key in %s/%s Secret", bootstrapapi.BootstrapTokenIDKey, secret.Namespace, secret.Name)
 | 
				
			||||||
		return "", "", false
 | 
							return "", "", false
 | 
				
			||||||
@@ -67,7 +44,7 @@ func validateSecretForSigning(secret *v1.Secret) (tokenID, tokenSecret string, o
 | 
				
			|||||||
		return "", "", false
 | 
							return "", "", false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tokenSecret = getSecretString(secret, bootstrapapi.BootstrapTokenSecretKey)
 | 
						tokenSecret = bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenSecretKey)
 | 
				
			||||||
	if len(tokenSecret) == 0 {
 | 
						if len(tokenSecret) == 0 {
 | 
				
			||||||
		klog.V(3).Infof("No %s key in %s/%s Secret", bootstrapapi.BootstrapTokenSecretKey, secret.Namespace, secret.Name)
 | 
							klog.V(3).Infof("No %s key in %s/%s Secret", bootstrapapi.BootstrapTokenSecretKey, secret.Namespace, secret.Name)
 | 
				
			||||||
		return "", "", false
 | 
							return "", "", false
 | 
				
			||||||
@@ -76,34 +53,15 @@ func validateSecretForSigning(secret *v1.Secret) (tokenID, tokenSecret string, o
 | 
				
			|||||||
	// Ensure this secret hasn't expired.  The TokenCleaner should remove this
 | 
						// Ensure this secret hasn't expired.  The TokenCleaner should remove this
 | 
				
			||||||
	// but if that isn't working or it hasn't gotten there yet we should check
 | 
						// but if that isn't working or it hasn't gotten there yet we should check
 | 
				
			||||||
	// here.
 | 
						// here.
 | 
				
			||||||
	if isSecretExpired(secret) {
 | 
						if bootstrapsecretutil.HasExpired(secret, time.Now()) {
 | 
				
			||||||
		return "", "", false
 | 
							return "", "", false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Make sure this secret can be used for signing
 | 
						// Make sure this secret can be used for signing
 | 
				
			||||||
	okToSign := getSecretString(secret, bootstrapapi.BootstrapTokenUsageSigningKey)
 | 
						okToSign := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenUsageSigningKey)
 | 
				
			||||||
	if okToSign != "true" {
 | 
						if okToSign != "true" {
 | 
				
			||||||
		return "", "", false
 | 
							return "", "", false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return tokenID, tokenSecret, true
 | 
						return tokenID, tokenSecret, true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
// isSecretExpired returns true if the Secret is expired.
 | 
					 | 
				
			||||||
func isSecretExpired(secret *v1.Secret) bool {
 | 
					 | 
				
			||||||
	expiration := getSecretString(secret, bootstrapapi.BootstrapTokenExpirationKey)
 | 
					 | 
				
			||||||
	if len(expiration) > 0 {
 | 
					 | 
				
			||||||
		expTime, err2 := time.Parse(time.RFC3339, expiration)
 | 
					 | 
				
			||||||
		if err2 != nil {
 | 
					 | 
				
			||||||
			klog.V(3).Infof("Unparseable expiration time (%s) in %s/%s Secret: %v. Treating as expired.",
 | 
					 | 
				
			||||||
				expiration, secret.Namespace, secret.Name, err2)
 | 
					 | 
				
			||||||
			return true
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if time.Now().After(expTime) {
 | 
					 | 
				
			||||||
			klog.V(3).Infof("Expired bootstrap token in %s/%s Secret: %v",
 | 
					 | 
				
			||||||
				secret.Namespace, secret.Name, expiration)
 | 
					 | 
				
			||||||
			return true
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,8 +20,6 @@ import (
 | 
				
			|||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"k8s.io/api/core/v1"
 | 
						"k8s.io/api/core/v1"
 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
	bootstrapapi "k8s.io/cluster-bootstrap/token/api"
 | 
						bootstrapapi "k8s.io/cluster-bootstrap/token/api"
 | 
				
			||||||
@@ -180,27 +178,3 @@ func TestMismatchSecretName(t *testing.T) {
 | 
				
			|||||||
		t.Errorf("Token validation should fail with mismatched name")
 | 
							t.Errorf("Token validation should fail with mismatched name")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestParseSecretName(t *testing.T) {
 | 
					 | 
				
			||||||
	tokenID, ok := parseSecretName("bootstrap-token-abc123")
 | 
					 | 
				
			||||||
	assert.True(t, ok, "parseSecretName should accept valid name")
 | 
					 | 
				
			||||||
	assert.Equal(t, "abc123", tokenID, "parseSecretName should return token ID")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_, ok = parseSecretName("")
 | 
					 | 
				
			||||||
	assert.False(t, ok, "parseSecretName should reject blank name")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_, ok = parseSecretName("abc123")
 | 
					 | 
				
			||||||
	assert.False(t, ok, "parseSecretName should reject with no prefix")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_, ok = parseSecretName("bootstrap-token-")
 | 
					 | 
				
			||||||
	assert.False(t, ok, "parseSecretName should reject no token ID")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_, ok = parseSecretName("bootstrap-token-abc")
 | 
					 | 
				
			||||||
	assert.False(t, ok, "parseSecretName should reject short token ID")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_, ok = parseSecretName("bootstrap-token-abc123ghi")
 | 
					 | 
				
			||||||
	assert.False(t, ok, "parseSecretName should reject long token ID")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_, ok = parseSecretName("bootstrap-token-ABC123")
 | 
					 | 
				
			||||||
	assert.False(t, ok, "parseSecretName should reject invalid token ID")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,12 +28,12 @@ go_library(
 | 
				
			|||||||
    deps = [
 | 
					    deps = [
 | 
				
			||||||
        "//staging/src/k8s.io/api/core/v1:go_default_library",
 | 
					        "//staging/src/k8s.io/api/core/v1:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
 | 
					 | 
				
			||||||
        "//staging/src/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library",
 | 
					        "//staging/src/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
 | 
					        "//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
 | 
					        "//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/cluster-bootstrap/token/api:go_default_library",
 | 
					        "//staging/src/k8s.io/cluster-bootstrap/token/api:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/cluster-bootstrap/token/util:go_default_library",
 | 
					        "//staging/src/k8s.io/cluster-bootstrap/util/secrets:go_default_library",
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/cluster-bootstrap/util/tokens:go_default_library",
 | 
				
			||||||
        "//vendor/k8s.io/klog:go_default_library",
 | 
					        "//vendor/k8s.io/klog:go_default_library",
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,20 +23,18 @@ import (
 | 
				
			|||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"crypto/subtle"
 | 
						"crypto/subtle"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"regexp"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"k8s.io/klog"
 | 
						"k8s.io/klog"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	corev1 "k8s.io/api/core/v1"
 | 
						corev1 "k8s.io/api/core/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/api/errors"
 | 
						"k8s.io/apimachinery/pkg/api/errors"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/util/sets"
 | 
					 | 
				
			||||||
	"k8s.io/apiserver/pkg/authentication/authenticator"
 | 
						"k8s.io/apiserver/pkg/authentication/authenticator"
 | 
				
			||||||
	"k8s.io/apiserver/pkg/authentication/user"
 | 
						"k8s.io/apiserver/pkg/authentication/user"
 | 
				
			||||||
	corev1listers "k8s.io/client-go/listers/core/v1"
 | 
						corev1listers "k8s.io/client-go/listers/core/v1"
 | 
				
			||||||
	bootstrapapi "k8s.io/cluster-bootstrap/token/api"
 | 
						bootstrapapi "k8s.io/cluster-bootstrap/token/api"
 | 
				
			||||||
	bootstraputil "k8s.io/cluster-bootstrap/token/util"
 | 
						bootstrapsecretutil "k8s.io/cluster-bootstrap/util/secrets"
 | 
				
			||||||
 | 
						bootstraptokenutil "k8s.io/cluster-bootstrap/util/tokens"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: A few methods in this package is copied from other sources. Either
 | 
					// TODO: A few methods in this package is copied from other sources. Either
 | 
				
			||||||
@@ -92,7 +90,7 @@ func tokenErrorf(s *corev1.Secret, format string, i ...interface{}) {
 | 
				
			|||||||
//     ( token-id ).( token-secret )
 | 
					//     ( token-id ).( token-secret )
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
func (t *TokenAuthenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
 | 
					func (t *TokenAuthenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
 | 
				
			||||||
	tokenID, tokenSecret, err := parseToken(token)
 | 
						tokenID, tokenSecret, err := bootstraptokenutil.ParseToken(token)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		// Token isn't of the correct form, ignore it.
 | 
							// Token isn't of the correct form, ignore it.
 | 
				
			||||||
		return nil, false, nil
 | 
							return nil, false, nil
 | 
				
			||||||
@@ -118,29 +116,29 @@ func (t *TokenAuthenticator) AuthenticateToken(ctx context.Context, token string
 | 
				
			|||||||
		return nil, false, nil
 | 
							return nil, false, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ts := getSecretString(secret, bootstrapapi.BootstrapTokenSecretKey)
 | 
						ts := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenSecretKey)
 | 
				
			||||||
	if subtle.ConstantTimeCompare([]byte(ts), []byte(tokenSecret)) != 1 {
 | 
						if subtle.ConstantTimeCompare([]byte(ts), []byte(tokenSecret)) != 1 {
 | 
				
			||||||
		tokenErrorf(secret, "has invalid value for key %s, expected %s.", bootstrapapi.BootstrapTokenSecretKey, tokenSecret)
 | 
							tokenErrorf(secret, "has invalid value for key %s, expected %s.", bootstrapapi.BootstrapTokenSecretKey, tokenSecret)
 | 
				
			||||||
		return nil, false, nil
 | 
							return nil, false, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	id := getSecretString(secret, bootstrapapi.BootstrapTokenIDKey)
 | 
						id := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenIDKey)
 | 
				
			||||||
	if id != tokenID {
 | 
						if id != tokenID {
 | 
				
			||||||
		tokenErrorf(secret, "has invalid value for key %s, expected %s.", bootstrapapi.BootstrapTokenIDKey, tokenID)
 | 
							tokenErrorf(secret, "has invalid value for key %s, expected %s.", bootstrapapi.BootstrapTokenIDKey, tokenID)
 | 
				
			||||||
		return nil, false, nil
 | 
							return nil, false, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if isSecretExpired(secret) {
 | 
						if bootstrapsecretutil.HasExpired(secret, time.Now()) {
 | 
				
			||||||
		// logging done in isSecretExpired method.
 | 
							// logging done in isSecretExpired method.
 | 
				
			||||||
		return nil, false, nil
 | 
							return nil, false, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if getSecretString(secret, bootstrapapi.BootstrapTokenUsageAuthentication) != "true" {
 | 
						if bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenUsageAuthentication) != "true" {
 | 
				
			||||||
		tokenErrorf(secret, "not marked %s=true.", bootstrapapi.BootstrapTokenUsageAuthentication)
 | 
							tokenErrorf(secret, "not marked %s=true.", bootstrapapi.BootstrapTokenUsageAuthentication)
 | 
				
			||||||
		return nil, false, nil
 | 
							return nil, false, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	groups, err := getGroups(secret)
 | 
						groups, err := bootstrapsecretutil.GetGroups(secret)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		tokenErrorf(secret, "has invalid value for key %s: %v.", bootstrapapi.BootstrapTokenExtraGroupsKey, err)
 | 
							tokenErrorf(secret, "has invalid value for key %s: %v.", bootstrapapi.BootstrapTokenExtraGroupsKey, err)
 | 
				
			||||||
		return nil, false, nil
 | 
							return nil, false, nil
 | 
				
			||||||
@@ -153,76 +151,3 @@ func (t *TokenAuthenticator) AuthenticateToken(ctx context.Context, token string
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
	}, true, nil
 | 
						}, true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
// Copied from k8s.io/cluster-bootstrap/token/api
 | 
					 | 
				
			||||||
func getSecretString(secret *corev1.Secret, key string) string {
 | 
					 | 
				
			||||||
	data, ok := secret.Data[key]
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		return ""
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return string(data)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Copied from k8s.io/cluster-bootstrap/token/api
 | 
					 | 
				
			||||||
func isSecretExpired(secret *corev1.Secret) bool {
 | 
					 | 
				
			||||||
	expiration := getSecretString(secret, bootstrapapi.BootstrapTokenExpirationKey)
 | 
					 | 
				
			||||||
	if len(expiration) > 0 {
 | 
					 | 
				
			||||||
		expTime, err2 := time.Parse(time.RFC3339, expiration)
 | 
					 | 
				
			||||||
		if err2 != nil {
 | 
					 | 
				
			||||||
			klog.V(3).Infof("Unparseable expiration time (%s) in %s/%s Secret: %v. Treating as expired.",
 | 
					 | 
				
			||||||
				expiration, secret.Namespace, secret.Name, err2)
 | 
					 | 
				
			||||||
			return true
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if time.Now().After(expTime) {
 | 
					 | 
				
			||||||
			klog.V(3).Infof("Expired bootstrap token in %s/%s Secret: %v",
 | 
					 | 
				
			||||||
				secret.Namespace, secret.Name, expiration)
 | 
					 | 
				
			||||||
			return true
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Copied from kubernetes/cmd/kubeadm/app/util/token
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var (
 | 
					 | 
				
			||||||
	// tokenRegexpString defines id.secret regular expression pattern
 | 
					 | 
				
			||||||
	tokenRegexpString = "^([a-z0-9]{6})\\.([a-z0-9]{16})$"
 | 
					 | 
				
			||||||
	// tokenRegexp is a compiled regular expression of TokenRegexpString
 | 
					 | 
				
			||||||
	tokenRegexp = regexp.MustCompile(tokenRegexpString)
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// parseToken tries and parse a valid token from a string.
 | 
					 | 
				
			||||||
// A token ID and token secret are returned in case of success, an error otherwise.
 | 
					 | 
				
			||||||
func parseToken(s string) (string, string, error) {
 | 
					 | 
				
			||||||
	split := tokenRegexp.FindStringSubmatch(s)
 | 
					 | 
				
			||||||
	if len(split) != 3 {
 | 
					 | 
				
			||||||
		return "", "", fmt.Errorf("token [%q] was not of form [%q]", s, tokenRegexpString)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return split[1], split[2], nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getGroups loads and validates the bootstrapapi.BootstrapTokenExtraGroupsKey
 | 
					 | 
				
			||||||
// key from the bootstrap token secret, returning a list of group names or an
 | 
					 | 
				
			||||||
// error if any of the group names are invalid.
 | 
					 | 
				
			||||||
func getGroups(secret *corev1.Secret) ([]string, error) {
 | 
					 | 
				
			||||||
	// always include the default group
 | 
					 | 
				
			||||||
	groups := sets.NewString(bootstrapapi.BootstrapDefaultGroup)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// grab any extra groups and if there are none, return just the default
 | 
					 | 
				
			||||||
	extraGroupsString := getSecretString(secret, bootstrapapi.BootstrapTokenExtraGroupsKey)
 | 
					 | 
				
			||||||
	if extraGroupsString == "" {
 | 
					 | 
				
			||||||
		return groups.List(), nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// validate the names of the extra groups
 | 
					 | 
				
			||||||
	for _, group := range strings.Split(extraGroupsString, ",") {
 | 
					 | 
				
			||||||
		if err := bootstraputil.ValidateBootstrapGroupName(group); err != nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		groups.Insert(group)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// return the result as a deduplicated, sorted list
 | 
					 | 
				
			||||||
	return groups.List(), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -288,72 +288,3 @@ func TestTokenAuthenticator(t *testing.T) {
 | 
				
			|||||||
		}()
 | 
							}()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestGetGroups(t *testing.T) {
 | 
					 | 
				
			||||||
	tests := []struct {
 | 
					 | 
				
			||||||
		name         string
 | 
					 | 
				
			||||||
		secret       *corev1.Secret
 | 
					 | 
				
			||||||
		expectResult []string
 | 
					 | 
				
			||||||
		expectError  bool
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name: "not set",
 | 
					 | 
				
			||||||
			secret: &corev1.Secret{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{Name: "test"},
 | 
					 | 
				
			||||||
				Data:       map[string][]byte{},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			expectResult: []string{"system:bootstrappers"},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name: "set to empty value",
 | 
					 | 
				
			||||||
			secret: &corev1.Secret{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{Name: "test"},
 | 
					 | 
				
			||||||
				Data: map[string][]byte{
 | 
					 | 
				
			||||||
					bootstrapapi.BootstrapTokenExtraGroupsKey: []byte(""),
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			expectResult: []string{"system:bootstrappers"},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name: "invalid prefix",
 | 
					 | 
				
			||||||
			secret: &corev1.Secret{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{Name: "test"},
 | 
					 | 
				
			||||||
				Data: map[string][]byte{
 | 
					 | 
				
			||||||
					bootstrapapi.BootstrapTokenExtraGroupsKey: []byte("foo"),
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			expectError: true,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name: "valid",
 | 
					 | 
				
			||||||
			secret: &corev1.Secret{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{Name: "test"},
 | 
					 | 
				
			||||||
				Data: map[string][]byte{
 | 
					 | 
				
			||||||
					bootstrapapi.BootstrapTokenExtraGroupsKey: []byte("system:bootstrappers:foo,system:bootstrappers:bar,system:bootstrappers:bar"),
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			// expect the results in deduplicated, sorted order
 | 
					 | 
				
			||||||
			expectResult: []string{
 | 
					 | 
				
			||||||
				"system:bootstrappers",
 | 
					 | 
				
			||||||
				"system:bootstrappers:bar",
 | 
					 | 
				
			||||||
				"system:bootstrappers:foo",
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, test := range tests {
 | 
					 | 
				
			||||||
		result, err := getGroups(test.secret)
 | 
					 | 
				
			||||||
		if test.expectError {
 | 
					 | 
				
			||||||
			if err == nil {
 | 
					 | 
				
			||||||
				t.Errorf("test %q expected an error, but didn't get one (result: %#v)", test.name, result)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			t.Errorf("test %q return an unexpected error: %v", test.name, err)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if !reflect.DeepEqual(result, test.expectResult) {
 | 
					 | 
				
			||||||
			t.Errorf("test %q expected %#v, got %#v", test.name, test.expectResult, result)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -201,6 +201,7 @@
 | 
				
			|||||||
  - k8s.io/api
 | 
					  - k8s.io/api
 | 
				
			||||||
  - k8s.io/apimachinery
 | 
					  - k8s.io/apimachinery
 | 
				
			||||||
  - k8s.io/cluster-bootstrap
 | 
					  - k8s.io/cluster-bootstrap
 | 
				
			||||||
 | 
					  - k8s.io/klog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- baseImportPath: "./vendor/k8s.io/cloud-provider/"
 | 
					- baseImportPath: "./vendor/k8s.io/cloud-provider/"
 | 
				
			||||||
  allowedImports:
 | 
					  allowedImports:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,8 @@ filegroup(
 | 
				
			|||||||
        ":package-srcs",
 | 
					        ":package-srcs",
 | 
				
			||||||
        "//staging/src/k8s.io/cluster-bootstrap/token/api:all-srcs",
 | 
					        "//staging/src/k8s.io/cluster-bootstrap/token/api:all-srcs",
 | 
				
			||||||
        "//staging/src/k8s.io/cluster-bootstrap/token/util:all-srcs",
 | 
					        "//staging/src/k8s.io/cluster-bootstrap/token/util:all-srcs",
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/cluster-bootstrap/util/secrets:all-srcs",
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/cluster-bootstrap/util/tokens:all-srcs",
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    tags = ["automanaged"],
 | 
					    tags = ["automanaged"],
 | 
				
			||||||
    visibility = ["//visibility:public"],
 | 
					    visibility = ["//visibility:public"],
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ go 1.12
 | 
				
			|||||||
require (
 | 
					require (
 | 
				
			||||||
	k8s.io/api v0.0.0
 | 
						k8s.io/api v0.0.0
 | 
				
			||||||
	k8s.io/apimachinery v0.0.0
 | 
						k8s.io/apimachinery v0.0.0
 | 
				
			||||||
 | 
						k8s.io/klog v0.3.1
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
replace (
 | 
					replace (
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,8 @@ import (
 | 
				
			|||||||
	"k8s.io/cluster-bootstrap/token/api"
 | 
						"k8s.io/cluster-bootstrap/token/api"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO(dixudx): refactor this to util/secrets and util/tokens
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// validBootstrapTokenChars defines the characters a bootstrap token can consist of
 | 
					// validBootstrapTokenChars defines the characters a bootstrap token can consist of
 | 
				
			||||||
const validBootstrapTokenChars = "0123456789abcdefghijklmnopqrstuvwxyz"
 | 
					const validBootstrapTokenChars = "0123456789abcdefghijklmnopqrstuvwxyz"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -110,6 +112,7 @@ func BootstrapTokenSecretName(tokenID string) string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// ValidateBootstrapGroupName checks if the provided group name is a valid
 | 
					// ValidateBootstrapGroupName checks if the provided group name is a valid
 | 
				
			||||||
// bootstrap group name. Returns nil if valid or a validation error if invalid.
 | 
					// bootstrap group name. Returns nil if valid or a validation error if invalid.
 | 
				
			||||||
 | 
					// TODO(dixudx): should be moved to util/secrets
 | 
				
			||||||
func ValidateBootstrapGroupName(name string) error {
 | 
					func ValidateBootstrapGroupName(name string) error {
 | 
				
			||||||
	if BootstrapGroupRegexp.Match([]byte(name)) {
 | 
						if BootstrapGroupRegexp.Match([]byte(name)) {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										41
									
								
								staging/src/k8s.io/cluster-bootstrap/util/secrets/BUILD
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								staging/src/k8s.io/cluster-bootstrap/util/secrets/BUILD
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					go_library(
 | 
				
			||||||
 | 
					    name = "go_default_library",
 | 
				
			||||||
 | 
					    srcs = ["secrets.go"],
 | 
				
			||||||
 | 
					    importmap = "k8s.io/kubernetes/vendor/k8s.io/cluster-bootstrap/util/secrets",
 | 
				
			||||||
 | 
					    importpath = "k8s.io/cluster-bootstrap/util/secrets",
 | 
				
			||||||
 | 
					    visibility = ["//visibility:public"],
 | 
				
			||||||
 | 
					    deps = [
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/api/core/v1:go_default_library",
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/cluster-bootstrap/token/api:go_default_library",
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/cluster-bootstrap/token/util:go_default_library",
 | 
				
			||||||
 | 
					        "//vendor/k8s.io/klog:go_default_library",
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					go_test(
 | 
				
			||||||
 | 
					    name = "go_default_test",
 | 
				
			||||||
 | 
					    srcs = ["secrets_test.go"],
 | 
				
			||||||
 | 
					    embed = [":go_default_library"],
 | 
				
			||||||
 | 
					    deps = [
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/api/core/v1:go_default_library",
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/cluster-bootstrap/token/api:go_default_library",
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					filegroup(
 | 
				
			||||||
 | 
					    name = "package-srcs",
 | 
				
			||||||
 | 
					    srcs = glob(["**"]),
 | 
				
			||||||
 | 
					    tags = ["automanaged"],
 | 
				
			||||||
 | 
					    visibility = ["//visibility:private"],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					filegroup(
 | 
				
			||||||
 | 
					    name = "all-srcs",
 | 
				
			||||||
 | 
					    srcs = [":package-srcs"],
 | 
				
			||||||
 | 
					    tags = ["automanaged"],
 | 
				
			||||||
 | 
					    visibility = ["//visibility:public"],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										98
									
								
								staging/src/k8s.io/cluster-bootstrap/util/secrets/secrets.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								staging/src/k8s.io/cluster-bootstrap/util/secrets/secrets.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2019 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 secrets
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/api/core/v1"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/util/sets"
 | 
				
			||||||
 | 
						"k8s.io/cluster-bootstrap/token/api"
 | 
				
			||||||
 | 
						legacyutil "k8s.io/cluster-bootstrap/token/util"
 | 
				
			||||||
 | 
						"k8s.io/klog"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						secretNameRe = regexp.MustCompile(`^` + regexp.QuoteMeta(api.BootstrapTokenSecretPrefix) + `([a-z0-9]{6})$`)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetData returns the string value for the given key in the specified Secret
 | 
				
			||||||
 | 
					// If there is an error or if the key doesn't exist, an empty string is returned.
 | 
				
			||||||
 | 
					func GetData(secret *v1.Secret, key string) string {
 | 
				
			||||||
 | 
						if secret.Data == nil {
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if val, ok := secret.Data[key]; ok {
 | 
				
			||||||
 | 
							return string(val)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// HasExpired will identify whether the secret expires
 | 
				
			||||||
 | 
					func HasExpired(secret *v1.Secret, currentTime time.Time) bool {
 | 
				
			||||||
 | 
						expiration := GetData(secret, api.BootstrapTokenExpirationKey)
 | 
				
			||||||
 | 
						if len(expiration) > 0 {
 | 
				
			||||||
 | 
							expTime, err2 := time.Parse(time.RFC3339, expiration)
 | 
				
			||||||
 | 
							if err2 != nil {
 | 
				
			||||||
 | 
								klog.V(3).Infof("Unparseable expiration time (%s) in %s/%s Secret: %v. Treating as expired.",
 | 
				
			||||||
 | 
									expiration, secret.Namespace, secret.Name, err2)
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if currentTime.After(expTime) {
 | 
				
			||||||
 | 
								klog.V(3).Infof("Expired bootstrap token in %s/%s Secret: %v",
 | 
				
			||||||
 | 
									secret.Namespace, secret.Name, expiration)
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ParseName parses the name of the secret to extract the secret ID.
 | 
				
			||||||
 | 
					func ParseName(name string) (secretID string, ok bool) {
 | 
				
			||||||
 | 
						r := secretNameRe.FindStringSubmatch(name)
 | 
				
			||||||
 | 
						if r == nil {
 | 
				
			||||||
 | 
							return "", false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return r[1], true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetGroups loads and validates the bootstrapapi.BootstrapTokenExtraGroupsKey
 | 
				
			||||||
 | 
					// key from the bootstrap token secret, returning a list of group names or an
 | 
				
			||||||
 | 
					// error if any of the group names are invalid.
 | 
				
			||||||
 | 
					func GetGroups(secret *v1.Secret) ([]string, error) {
 | 
				
			||||||
 | 
						// always include the default group
 | 
				
			||||||
 | 
						groups := sets.NewString(api.BootstrapDefaultGroup)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// grab any extra groups and if there are none, return just the default
 | 
				
			||||||
 | 
						extraGroupsString := GetData(secret, api.BootstrapTokenExtraGroupsKey)
 | 
				
			||||||
 | 
						if extraGroupsString == "" {
 | 
				
			||||||
 | 
							return groups.List(), nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// validate the names of the extra groups
 | 
				
			||||||
 | 
						for _, group := range strings.Split(extraGroupsString, ",") {
 | 
				
			||||||
 | 
							if err := legacyutil.ValidateBootstrapGroupName(group); err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							groups.Insert(group)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// return the result as a deduplicated, sorted list
 | 
				
			||||||
 | 
						return groups.List(), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,187 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2019 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 secrets
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/api/core/v1"
 | 
				
			||||||
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
 | 
						bootstrapapi "k8s.io/cluster-bootstrap/token/api"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGetSecretString(t *testing.T) {
 | 
				
			||||||
 | 
						var tests = []struct {
 | 
				
			||||||
 | 
							name        string
 | 
				
			||||||
 | 
							secret      *v1.Secret
 | 
				
			||||||
 | 
							key         string
 | 
				
			||||||
 | 
							expectedVal string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "existing key",
 | 
				
			||||||
 | 
								secret: &v1.Secret{
 | 
				
			||||||
 | 
									ObjectMeta: metav1.ObjectMeta{Name: "foo"},
 | 
				
			||||||
 | 
									Data: map[string][]byte{
 | 
				
			||||||
 | 
										"foo": []byte("bar"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								key:         "foo",
 | 
				
			||||||
 | 
								expectedVal: "bar",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "non-existing key",
 | 
				
			||||||
 | 
								secret: &v1.Secret{
 | 
				
			||||||
 | 
									ObjectMeta: metav1.ObjectMeta{Name: "foo"},
 | 
				
			||||||
 | 
									Data: map[string][]byte{
 | 
				
			||||||
 | 
										"foo": []byte("bar"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								key:         "baz",
 | 
				
			||||||
 | 
								expectedVal: "",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "no data",
 | 
				
			||||||
 | 
								secret: &v1.Secret{
 | 
				
			||||||
 | 
									ObjectMeta: metav1.ObjectMeta{Name: "foo"},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								key:         "foo",
 | 
				
			||||||
 | 
								expectedVal: "",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, rt := range tests {
 | 
				
			||||||
 | 
							t.Run(rt.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								actual := GetData(rt.secret, rt.key)
 | 
				
			||||||
 | 
								if actual != rt.expectedVal {
 | 
				
			||||||
 | 
									t.Errorf(
 | 
				
			||||||
 | 
										"failed getSecretString:\n\texpected: %s\n\t  actual: %s",
 | 
				
			||||||
 | 
										rt.expectedVal,
 | 
				
			||||||
 | 
										actual,
 | 
				
			||||||
 | 
									)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestParseSecretName(t *testing.T) {
 | 
				
			||||||
 | 
						tokenID, ok := ParseName("bootstrap-token-abc123")
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							t.Error("ParseName should accept valid name")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if tokenID != "abc123" {
 | 
				
			||||||
 | 
							t.Error("ParseName should return token ID")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, ok = ParseName("")
 | 
				
			||||||
 | 
						if ok {
 | 
				
			||||||
 | 
							t.Error("ParseName should reject blank name")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, ok = ParseName("abc123")
 | 
				
			||||||
 | 
						if ok {
 | 
				
			||||||
 | 
							t.Error("ParseName should reject with no prefix")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, ok = ParseName("bootstrap-token-")
 | 
				
			||||||
 | 
						if ok {
 | 
				
			||||||
 | 
							t.Error("ParseName should reject no token ID")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, ok = ParseName("bootstrap-token-abc")
 | 
				
			||||||
 | 
						if ok {
 | 
				
			||||||
 | 
							t.Error("ParseName should reject short token ID")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, ok = ParseName("bootstrap-token-abc123ghi")
 | 
				
			||||||
 | 
						if ok {
 | 
				
			||||||
 | 
							t.Error("ParseName should reject long token ID")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, ok = ParseName("bootstrap-token-ABC123")
 | 
				
			||||||
 | 
						if ok {
 | 
				
			||||||
 | 
							t.Error("ParseName should reject invalid token ID")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGetGroups(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name         string
 | 
				
			||||||
 | 
							secret       *v1.Secret
 | 
				
			||||||
 | 
							expectResult []string
 | 
				
			||||||
 | 
							expectError  bool
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "not set",
 | 
				
			||||||
 | 
								secret: &v1.Secret{
 | 
				
			||||||
 | 
									ObjectMeta: metav1.ObjectMeta{Name: "test"},
 | 
				
			||||||
 | 
									Data:       map[string][]byte{},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expectResult: []string{"system:bootstrappers"},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "set to empty value",
 | 
				
			||||||
 | 
								secret: &v1.Secret{
 | 
				
			||||||
 | 
									ObjectMeta: metav1.ObjectMeta{Name: "test"},
 | 
				
			||||||
 | 
									Data: map[string][]byte{
 | 
				
			||||||
 | 
										bootstrapapi.BootstrapTokenExtraGroupsKey: []byte(""),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expectResult: []string{"system:bootstrappers"},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "invalid prefix",
 | 
				
			||||||
 | 
								secret: &v1.Secret{
 | 
				
			||||||
 | 
									ObjectMeta: metav1.ObjectMeta{Name: "test"},
 | 
				
			||||||
 | 
									Data: map[string][]byte{
 | 
				
			||||||
 | 
										bootstrapapi.BootstrapTokenExtraGroupsKey: []byte("foo"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expectError: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "valid",
 | 
				
			||||||
 | 
								secret: &v1.Secret{
 | 
				
			||||||
 | 
									ObjectMeta: metav1.ObjectMeta{Name: "test"},
 | 
				
			||||||
 | 
									Data: map[string][]byte{
 | 
				
			||||||
 | 
										bootstrapapi.BootstrapTokenExtraGroupsKey: []byte("system:bootstrappers:foo,system:bootstrappers:bar,system:bootstrappers:bar"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								// expect the results in deduplicated, sorted order
 | 
				
			||||||
 | 
								expectResult: []string{
 | 
				
			||||||
 | 
									"system:bootstrappers",
 | 
				
			||||||
 | 
									"system:bootstrappers:bar",
 | 
				
			||||||
 | 
									"system:bootstrappers:foo",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, test := range tests {
 | 
				
			||||||
 | 
							result, err := GetGroups(test.secret)
 | 
				
			||||||
 | 
							if test.expectError {
 | 
				
			||||||
 | 
								if err == nil {
 | 
				
			||||||
 | 
									t.Errorf("test %q expected an error, but didn't get one (result: %#v)", test.name, result)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Errorf("test %q return an unexpected error: %v", test.name, err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !reflect.DeepEqual(result, test.expectResult) {
 | 
				
			||||||
 | 
								t.Errorf("test %q expected %#v, got %#v", test.name, test.expectResult, result)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										24
									
								
								staging/src/k8s.io/cluster-bootstrap/util/tokens/BUILD
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								staging/src/k8s.io/cluster-bootstrap/util/tokens/BUILD
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					load("@io_bazel_rules_go//go:def.bzl", "go_library")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					go_library(
 | 
				
			||||||
 | 
					    name = "go_default_library",
 | 
				
			||||||
 | 
					    srcs = ["tokens.go"],
 | 
				
			||||||
 | 
					    importmap = "k8s.io/kubernetes/vendor/k8s.io/cluster-bootstrap/util/tokens",
 | 
				
			||||||
 | 
					    importpath = "k8s.io/cluster-bootstrap/util/tokens",
 | 
				
			||||||
 | 
					    visibility = ["//visibility:public"],
 | 
				
			||||||
 | 
					    deps = ["//staging/src/k8s.io/cluster-bootstrap/token/api:go_default_library"],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					filegroup(
 | 
				
			||||||
 | 
					    name = "package-srcs",
 | 
				
			||||||
 | 
					    srcs = glob(["**"]),
 | 
				
			||||||
 | 
					    tags = ["automanaged"],
 | 
				
			||||||
 | 
					    visibility = ["//visibility:private"],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					filegroup(
 | 
				
			||||||
 | 
					    name = "all-srcs",
 | 
				
			||||||
 | 
					    srcs = [":package-srcs"],
 | 
				
			||||||
 | 
					    tags = ["automanaged"],
 | 
				
			||||||
 | 
					    visibility = ["//visibility:public"],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										38
									
								
								staging/src/k8s.io/cluster-bootstrap/util/tokens/tokens.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								staging/src/k8s.io/cluster-bootstrap/util/tokens/tokens.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2019 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 tokens
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/cluster-bootstrap/token/api"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						bootstrapTokenRe = regexp.MustCompile(api.BootstrapTokenPattern)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ParseToken tries and parse a valid token from a string.
 | 
				
			||||||
 | 
					// A token ID and token secret are returned in case of success, an error otherwise.
 | 
				
			||||||
 | 
					func ParseToken(s string) (tokenID, tokenSecret string, err error) {
 | 
				
			||||||
 | 
						split := bootstrapTokenRe.FindStringSubmatch(s)
 | 
				
			||||||
 | 
						if len(split) != 3 {
 | 
				
			||||||
 | 
							return "", "", fmt.Errorf("token [%q] was not of form [%q]", s, api.BootstrapTokenPattern)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return split[1], split[2], nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										2
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							@@ -1544,6 +1544,8 @@ k8s.io/cloud-provider/volume/helpers
 | 
				
			|||||||
# k8s.io/cluster-bootstrap v0.0.0 => ./staging/src/k8s.io/cluster-bootstrap
 | 
					# k8s.io/cluster-bootstrap v0.0.0 => ./staging/src/k8s.io/cluster-bootstrap
 | 
				
			||||||
k8s.io/cluster-bootstrap/token/api
 | 
					k8s.io/cluster-bootstrap/token/api
 | 
				
			||||||
k8s.io/cluster-bootstrap/token/util
 | 
					k8s.io/cluster-bootstrap/token/util
 | 
				
			||||||
 | 
					k8s.io/cluster-bootstrap/util/secrets
 | 
				
			||||||
 | 
					k8s.io/cluster-bootstrap/util/tokens
 | 
				
			||||||
# k8s.io/code-generator v0.0.0 => ./staging/src/k8s.io/code-generator
 | 
					# k8s.io/code-generator v0.0.0 => ./staging/src/k8s.io/code-generator
 | 
				
			||||||
k8s.io/code-generator/cmd/go-to-protobuf
 | 
					k8s.io/code-generator/cmd/go-to-protobuf
 | 
				
			||||||
k8s.io/code-generator/cmd/go-to-protobuf/protobuf
 | 
					k8s.io/code-generator/cmd/go-to-protobuf/protobuf
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user