mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-26 21:17:23 +00:00
Merge pull request #64408 from luxas/kubeadm_refactor_bt
Automatic merge from submit-queue (batch tested with PRs 64057, 63223, 64346, 64562, 64408). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. kubeadm: Refactor the Bootstrap Tokens usage in the API types **What this PR does / why we need it**: This PR: - Moves some common, generic Bootstrap Token helpers and constants from `k8s.io/kubernetes/cmd/kubeadm/app/util/token` to `k8s.io/client-go/tools/bootstrap/token/` - Breaks out the top-level Bootstrap Token fields to a dedicated `BootstrapToken` struct with helper functions. - Instead of representing the Bootstrap Token as a plain `string`, there is now a wrapper struct `BootstrapTokenString` that can marshal/unmarshal correctly and supports validation on create, and splitting up the full token in the ID/Secret parts automatically. - Makes kubeadm support multiple Bootstrap Tokens automatically by supporting a slice of `BootstrapToken` in the `MasterConfiguration` API object - Consolidates the place for kubeadm to create token-related flags in an `options` package - Supports automatic conversion from the `v1alpha1` to `v1alpha2` API - Adds support to set token expiration directly instead of setting a TTL (Expiration and TTL are mutually exclusive) - Removes the old `TokenDiscovery` struct we're not using anymore inside of kubeadm **Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*: Related to https://github.com/kubernetes/community/pull/2131 **Special notes for your reviewer**: This is work in progress. Please only review the first two commits for now. I will work on splitting up this PR in smaller chunks. I will also write unit tests tomorrow. **Release note**: ```release-note [action required] kubeadm: The Token-related fields in the `MasterConfiguration` object have now been refactored. Instead of the top-level `.Token`, `.TokenTTL`, `.TokenUsages`, `.TokenGroups` fields, there is now a `BootstrapTokens` slice of `BootstrapToken` objects that support the same features under the `.Token`, `.TTL`, `.Usages`, `.Groups` fields. ``` @kubernetes/sig-cluster-lifecycle-pr-reviews @mattmoyer @liztio
This commit is contained in:
commit
c7b71ebca9
@ -3,11 +3,14 @@ package(default_visibility = ["//visibility:public"])
|
|||||||
load(
|
load(
|
||||||
"@io_bazel_rules_go//go:def.bzl",
|
"@io_bazel_rules_go//go:def.bzl",
|
||||||
"go_library",
|
"go_library",
|
||||||
|
"go_test",
|
||||||
)
|
)
|
||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = [
|
srcs = [
|
||||||
|
"bootstraptokenhelpers.go",
|
||||||
|
"bootstraptokenstring.go",
|
||||||
"doc.go",
|
"doc.go",
|
||||||
"register.go",
|
"register.go",
|
||||||
"types.go",
|
"types.go",
|
||||||
@ -22,6 +25,8 @@ go_library(
|
|||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/tools/bootstrap/token/api:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/tools/bootstrap/token/util:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,7 +42,6 @@ filegroup(
|
|||||||
srcs = [
|
srcs = [
|
||||||
":package-srcs",
|
":package-srcs",
|
||||||
"//cmd/kubeadm/app/apis/kubeadm/fuzzer:all-srcs",
|
"//cmd/kubeadm/app/apis/kubeadm/fuzzer:all-srcs",
|
||||||
"//cmd/kubeadm/app/apis/kubeadm/install:all-srcs",
|
|
||||||
"//cmd/kubeadm/app/apis/kubeadm/scheme:all-srcs",
|
"//cmd/kubeadm/app/apis/kubeadm/scheme:all-srcs",
|
||||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:all-srcs",
|
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:all-srcs",
|
||||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha2:all-srcs",
|
"//cmd/kubeadm/app/apis/kubeadm/v1alpha2:all-srcs",
|
||||||
@ -45,3 +49,16 @@ filegroup(
|
|||||||
],
|
],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = [
|
||||||
|
"bootstraptokenhelpers_test.go",
|
||||||
|
"bootstraptokenstring_test.go",
|
||||||
|
],
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
deps = [
|
||||||
|
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
168
cmd/kubeadm/app/apis/kubeadm/bootstraptokenhelpers.go
Normal file
168
cmd/kubeadm/app/apis/kubeadm/bootstraptokenhelpers.go
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
bootstrapapi "k8s.io/client-go/tools/bootstrap/token/api"
|
||||||
|
bootstraputil "k8s.io/client-go/tools/bootstrap/token/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 exlusive 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.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).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 := getSecretString(secret, bootstrapapi.BootstrapTokenIDKey)
|
||||||
|
if len(tokenID) == 0 {
|
||||||
|
return nil, fmt.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, fmt.Errorf("bootstrap token name is not of the form '%s(token-id)'. Actual: %q. Expected: %q",
|
||||||
|
bootstrapapi.BootstrapTokenSecretPrefix, secret.Name, bootstraputil.BootstrapTokenSecretName(tokenID))
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenSecret := getSecretString(secret, bootstrapapi.BootstrapTokenSecretKey)
|
||||||
|
if len(tokenSecret) == 0 {
|
||||||
|
return nil, fmt.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, fmt.Errorf("Bootstrap Token Secret is invalid and couldn't be parsed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the description (if any) from the Secret
|
||||||
|
description := getSecretString(secret, bootstrapapi.BootstrapTokenDescriptionKey)
|
||||||
|
|
||||||
|
// Expiration time is optional, if not specified this implies the token
|
||||||
|
// never expires.
|
||||||
|
secretExpiration := getSecretString(secret, bootstrapapi.BootstrapTokenExpirationKey)
|
||||||
|
var expires *metav1.Time
|
||||||
|
if len(secretExpiration) > 0 {
|
||||||
|
expTime, err := time.Parse(time.RFC3339, secretExpiration)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't parse expiration time of bootstrap token %q: %v", secret.Name, err)
|
||||||
|
}
|
||||||
|
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 := getSecretString(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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 ""
|
||||||
|
}
|
473
cmd/kubeadm/app/apis/kubeadm/bootstraptokenhelpers_test.go
Normal file
473
cmd/kubeadm/app/apis/kubeadm/bootstraptokenhelpers_test.go
Normal file
@ -0,0 +1,473 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kubeadm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This timestamp is used as the reference value when computing expiration dates based on TTLs in these unit tests
|
||||||
|
var refTime = time.Date(1970, time.January, 1, 1, 1, 1, 0, time.UTC)
|
||||||
|
|
||||||
|
func TestToSecret(t *testing.T) {
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
bt *BootstrapToken
|
||||||
|
secret *v1.Secret
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
&BootstrapToken{ // all together
|
||||||
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
||||||
|
Description: "foo",
|
||||||
|
Expires: &metav1.Time{
|
||||||
|
Time: refTime,
|
||||||
|
},
|
||||||
|
Usages: []string{"signing", "authentication"},
|
||||||
|
Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"},
|
||||||
|
},
|
||||||
|
&v1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "bootstrap-token-abcdef",
|
||||||
|
Namespace: "kube-system",
|
||||||
|
},
|
||||||
|
Type: v1.SecretType("bootstrap.kubernetes.io/token"),
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
"description": []byte("foo"),
|
||||||
|
"expiration": []byte(refTime.Format(time.RFC3339)),
|
||||||
|
"usage-bootstrap-signing": []byte("true"),
|
||||||
|
"usage-bootstrap-authentication": []byte("true"),
|
||||||
|
"auth-extra-groups": []byte("system:bootstrappers,system:bootstrappers:foo"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
actual := rt.bt.ToSecret()
|
||||||
|
if !reflect.DeepEqual(actual, rt.secret) {
|
||||||
|
t.Errorf(
|
||||||
|
"failed BootstrapToken.ToSecret():\n\texpected: %v\n\t actual: %v",
|
||||||
|
rt.secret,
|
||||||
|
actual,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBootstrapTokenToSecretRoundtrip(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
bt *BootstrapToken
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
&BootstrapToken{
|
||||||
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
||||||
|
Description: "foo",
|
||||||
|
Expires: &metav1.Time{
|
||||||
|
Time: refTime,
|
||||||
|
},
|
||||||
|
Usages: []string{"authentication", "signing"},
|
||||||
|
Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
actual, err := BootstrapTokenFromSecret(rt.bt.ToSecret())
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed BootstrapToken to Secret roundtrip with error: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(actual, rt.bt) {
|
||||||
|
t.Errorf(
|
||||||
|
"failed BootstrapToken to Secret roundtrip:\n\texpected: %v\n\t actual: %v",
|
||||||
|
rt.bt,
|
||||||
|
actual,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncodeTokenSecretData(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
bt *BootstrapToken
|
||||||
|
data map[string][]byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
&BootstrapToken{ // the minimum amount of information needed to be specified
|
||||||
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
||||||
|
},
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&BootstrapToken{ // adds description
|
||||||
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
||||||
|
Description: "foo",
|
||||||
|
},
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
"description": []byte("foo"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&BootstrapToken{ // adds ttl
|
||||||
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
||||||
|
TTL: &metav1.Duration{
|
||||||
|
Duration: mustParseDuration("2h", t),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
"expiration": []byte(refTime.Add(mustParseDuration("2h", t)).Format(time.RFC3339)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&BootstrapToken{ // adds expiration
|
||||||
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
||||||
|
Expires: &metav1.Time{
|
||||||
|
Time: refTime,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
"expiration": []byte(refTime.Format(time.RFC3339)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&BootstrapToken{ // adds ttl and expiration, should favor expiration
|
||||||
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
||||||
|
TTL: &metav1.Duration{
|
||||||
|
Duration: mustParseDuration("2h", t),
|
||||||
|
},
|
||||||
|
Expires: &metav1.Time{
|
||||||
|
Time: refTime,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
"expiration": []byte(refTime.Format(time.RFC3339)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&BootstrapToken{ // adds usages
|
||||||
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
||||||
|
Usages: []string{"authentication", "signing"},
|
||||||
|
},
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
"usage-bootstrap-signing": []byte("true"),
|
||||||
|
"usage-bootstrap-authentication": []byte("true"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&BootstrapToken{ // adds groups
|
||||||
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
||||||
|
Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"},
|
||||||
|
},
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
"auth-extra-groups": []byte("system:bootstrappers,system:bootstrappers:foo"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&BootstrapToken{ // all together
|
||||||
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
||||||
|
Description: "foo",
|
||||||
|
TTL: &metav1.Duration{
|
||||||
|
Duration: mustParseDuration("2h", t),
|
||||||
|
},
|
||||||
|
Expires: &metav1.Time{
|
||||||
|
Time: refTime,
|
||||||
|
},
|
||||||
|
Usages: []string{"authentication", "signing"},
|
||||||
|
Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"},
|
||||||
|
},
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
"description": []byte("foo"),
|
||||||
|
"expiration": []byte(refTime.Format(time.RFC3339)),
|
||||||
|
"usage-bootstrap-signing": []byte("true"),
|
||||||
|
"usage-bootstrap-authentication": []byte("true"),
|
||||||
|
"auth-extra-groups": []byte("system:bootstrappers,system:bootstrappers:foo"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
actual := encodeTokenSecretData(rt.bt, refTime)
|
||||||
|
if !reflect.DeepEqual(actual, rt.data) {
|
||||||
|
t.Errorf(
|
||||||
|
"failed encodeTokenSecretData:\n\texpected: %v\n\t actual: %v",
|
||||||
|
rt.data,
|
||||||
|
actual,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustParseDuration(durationStr string, t *testing.T) time.Duration {
|
||||||
|
d, err := time.ParseDuration(durationStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("couldn't parse duration %q: %v", durationStr, err)
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBootstrapTokenFromSecret(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
data map[string][]byte
|
||||||
|
bt *BootstrapToken
|
||||||
|
expectedError bool
|
||||||
|
}{
|
||||||
|
{ // minimum information
|
||||||
|
"bootstrap-token-abcdef",
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
},
|
||||||
|
&BootstrapToken{
|
||||||
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{ // invalid token id
|
||||||
|
"bootstrap-token-abcdef",
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdeF"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{ // invalid secret naming
|
||||||
|
"foo",
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{ // invalid token secret
|
||||||
|
"bootstrap-token-abcdef",
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("ABCDEF0123456789"),
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{ // adds description
|
||||||
|
"bootstrap-token-abcdef",
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
"description": []byte("foo"),
|
||||||
|
},
|
||||||
|
&BootstrapToken{
|
||||||
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
||||||
|
Description: "foo",
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{ // adds expiration
|
||||||
|
"bootstrap-token-abcdef",
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
"expiration": []byte(refTime.Format(time.RFC3339)),
|
||||||
|
},
|
||||||
|
&BootstrapToken{
|
||||||
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
||||||
|
Expires: &metav1.Time{
|
||||||
|
Time: refTime,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{ // invalid expiration
|
||||||
|
"bootstrap-token-abcdef",
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
"expiration": []byte("invalid date"),
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{ // adds usages
|
||||||
|
"bootstrap-token-abcdef",
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
"usage-bootstrap-signing": []byte("true"),
|
||||||
|
"usage-bootstrap-authentication": []byte("true"),
|
||||||
|
},
|
||||||
|
&BootstrapToken{
|
||||||
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
||||||
|
Usages: []string{"authentication", "signing"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{ // should ignore usages that aren't set to true
|
||||||
|
"bootstrap-token-abcdef",
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
"usage-bootstrap-signing": []byte("true"),
|
||||||
|
"usage-bootstrap-authentication": []byte("true"),
|
||||||
|
"usage-bootstrap-foo": []byte("false"),
|
||||||
|
"usage-bootstrap-bar": []byte(""),
|
||||||
|
},
|
||||||
|
&BootstrapToken{
|
||||||
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
||||||
|
Usages: []string{"authentication", "signing"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{ // adds groups
|
||||||
|
"bootstrap-token-abcdef",
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
"auth-extra-groups": []byte("system:bootstrappers,system:bootstrappers:foo"),
|
||||||
|
},
|
||||||
|
&BootstrapToken{
|
||||||
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
||||||
|
Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{ // all fields set
|
||||||
|
"bootstrap-token-abcdef",
|
||||||
|
map[string][]byte{
|
||||||
|
"token-id": []byte("abcdef"),
|
||||||
|
"token-secret": []byte("abcdef0123456789"),
|
||||||
|
"description": []byte("foo"),
|
||||||
|
"expiration": []byte(refTime.Format(time.RFC3339)),
|
||||||
|
"usage-bootstrap-signing": []byte("true"),
|
||||||
|
"usage-bootstrap-authentication": []byte("true"),
|
||||||
|
"auth-extra-groups": []byte("system:bootstrappers,system:bootstrappers:foo"),
|
||||||
|
},
|
||||||
|
&BootstrapToken{
|
||||||
|
Token: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
||||||
|
Description: "foo",
|
||||||
|
Expires: &metav1.Time{
|
||||||
|
Time: refTime,
|
||||||
|
},
|
||||||
|
Usages: []string{"authentication", "signing"},
|
||||||
|
Groups: []string{"system:bootstrappers", "system:bootstrappers:foo"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
actual, err := BootstrapTokenFromSecret(&v1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: rt.name,
|
||||||
|
Namespace: "kube-system",
|
||||||
|
},
|
||||||
|
Type: v1.SecretType("bootstrap.kubernetes.io/token"),
|
||||||
|
Data: rt.data,
|
||||||
|
})
|
||||||
|
if (err != nil) != rt.expectedError {
|
||||||
|
t.Errorf(
|
||||||
|
"failed BootstrapTokenFromSecret\n\texpected error: %t\n\t actual error: %v",
|
||||||
|
rt.expectedError,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
if actual == nil && rt.bt == nil {
|
||||||
|
// if both pointers are nil, it's okay, just continue
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// If one of the pointers is defined but the other isn't, throw error. If both pointers are defined but unequal, throw error
|
||||||
|
if (actual == nil && rt.bt != nil) || (actual != nil && rt.bt == nil) || !reflect.DeepEqual(*actual, *rt.bt) {
|
||||||
|
t.Errorf(
|
||||||
|
"failed BootstrapTokenFromSecret\n\texpected: %s\n\t actual: %s",
|
||||||
|
jsonMarshal(rt.bt),
|
||||||
|
jsonMarshal(actual),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsonMarshal(bt *BootstrapToken) string {
|
||||||
|
b, _ := json.Marshal(*bt)
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetSecretString(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
secret *v1.Secret
|
||||||
|
key string
|
||||||
|
expectedVal string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
secret: &v1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"foo": []byte("bar"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
key: "foo",
|
||||||
|
expectedVal: "bar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
secret: &v1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"foo": []byte("bar"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
key: "baz",
|
||||||
|
expectedVal: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
secret: &v1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
|
||||||
|
},
|
||||||
|
key: "foo",
|
||||||
|
expectedVal: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
actual := getSecretString(rt.secret, rt.key)
|
||||||
|
if actual != rt.expectedVal {
|
||||||
|
t.Errorf(
|
||||||
|
"failed getSecretString:\n\texpected: %s\n\t actual: %s",
|
||||||
|
rt.expectedVal,
|
||||||
|
actual,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
90
cmd/kubeadm/app/apis/kubeadm/bootstraptokenstring.go
Normal file
90
cmd/kubeadm/app/apis/kubeadm/bootstraptokenstring.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
|
||||||
|
bootstrapapi "k8s.io/client-go/tools/bootstrap/token/api"
|
||||||
|
bootstraputil "k8s.io/client-go/tools/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, fmt.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))
|
||||||
|
}
|
236
cmd/kubeadm/app/apis/kubeadm/bootstraptokenstring_test.go
Normal file
236
cmd/kubeadm/app/apis/kubeadm/bootstraptokenstring_test.go
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kubeadm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMarshalJSON(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
bts BootstrapTokenString
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, `"abcdef.abcdef0123456789"`},
|
||||||
|
{BootstrapTokenString{ID: "foo", Secret: "bar"}, `"foo.bar"`},
|
||||||
|
{BootstrapTokenString{ID: "h", Secret: "b"}, `"h.b"`},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
b, err := json.Marshal(rt.bts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("json.Marshal returned an unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if string(b) != rt.expected {
|
||||||
|
t.Errorf(
|
||||||
|
"failed BootstrapTokenString.MarshalJSON:\n\texpected: %s\n\t actual: %s",
|
||||||
|
rt.expected,
|
||||||
|
string(b),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalJSON(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
input string
|
||||||
|
bts *BootstrapTokenString
|
||||||
|
expectedError bool
|
||||||
|
}{
|
||||||
|
{`"f.s"`, &BootstrapTokenString{}, true},
|
||||||
|
{`"abcdef."`, &BootstrapTokenString{}, true},
|
||||||
|
{`"abcdef:abcdef0123456789"`, &BootstrapTokenString{}, true},
|
||||||
|
{`abcdef.abcdef0123456789`, &BootstrapTokenString{}, true},
|
||||||
|
{`"abcdef.abcdef0123456789`, &BootstrapTokenString{}, true},
|
||||||
|
{`"abcdef.ABCDEF0123456789"`, &BootstrapTokenString{}, true},
|
||||||
|
{`"abcdef.abcdef0123456789"`, &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, false},
|
||||||
|
{`"123456.aabbccddeeffgghh"`, &BootstrapTokenString{ID: "123456", Secret: "aabbccddeeffgghh"}, false},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
newbts := &BootstrapTokenString{}
|
||||||
|
err := json.Unmarshal([]byte(rt.input), newbts)
|
||||||
|
if (err != nil) != rt.expectedError {
|
||||||
|
t.Errorf("failed BootstrapTokenString.UnmarshalJSON:\n\texpected error: %t\n\t actual error: %v", rt.expectedError, err)
|
||||||
|
} else if !reflect.DeepEqual(rt.bts, newbts) {
|
||||||
|
t.Errorf(
|
||||||
|
"failed BootstrapTokenString.UnmarshalJSON:\n\texpected: %v\n\t actual: %v",
|
||||||
|
rt.bts,
|
||||||
|
newbts,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONRoundtrip(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
input string
|
||||||
|
bts *BootstrapTokenString
|
||||||
|
}{
|
||||||
|
{`"abcdef.abcdef0123456789"`, nil},
|
||||||
|
{"", &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
if err := roundtrip(rt.input, rt.bts); err != nil {
|
||||||
|
t.Errorf("failed BootstrapTokenString JSON roundtrip with error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func roundtrip(input string, bts *BootstrapTokenString) error {
|
||||||
|
var b []byte
|
||||||
|
var err error
|
||||||
|
newbts := &BootstrapTokenString{}
|
||||||
|
// If string input was specified, roundtrip like this: string -> (unmarshal) -> object -> (marshal) -> string
|
||||||
|
if len(input) > 0 {
|
||||||
|
if err := json.Unmarshal([]byte(input), newbts); err != nil {
|
||||||
|
return fmt.Errorf("expected no unmarshal error, got error: %v", err)
|
||||||
|
}
|
||||||
|
if b, err = json.Marshal(newbts); err != nil {
|
||||||
|
return fmt.Errorf("expected no marshal error, got error: %v", err)
|
||||||
|
}
|
||||||
|
if input != string(b) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"expected token: %s\n\t actual: %s",
|
||||||
|
input,
|
||||||
|
string(b),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else { // Otherwise, roundtrip like this: object -> (marshal) -> string -> (unmarshal) -> object
|
||||||
|
if b, err = json.Marshal(bts); err != nil {
|
||||||
|
return fmt.Errorf("expected no marshal error, got error: %v", err)
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(b, newbts); err != nil {
|
||||||
|
return fmt.Errorf("expected no unmarshal error, got error: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(bts, newbts) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"expected object: %v\n\t actual: %v",
|
||||||
|
bts,
|
||||||
|
newbts,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTokenFromIDAndSecret(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
bts BootstrapTokenString
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{BootstrapTokenString{ID: "foo", Secret: "bar"}, "foo.bar"},
|
||||||
|
{BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, "abcdef.abcdef0123456789"},
|
||||||
|
{BootstrapTokenString{ID: "h", Secret: "b"}, "h.b"},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
actual := rt.bts.String()
|
||||||
|
if actual != rt.expected {
|
||||||
|
t.Errorf(
|
||||||
|
"failed BootstrapTokenString.String():\n\texpected: %s\n\t actual: %s",
|
||||||
|
rt.expected,
|
||||||
|
actual,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewBootstrapTokenString(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
token string
|
||||||
|
expectedError bool
|
||||||
|
bts *BootstrapTokenString
|
||||||
|
}{
|
||||||
|
{token: "", expectedError: true, bts: nil},
|
||||||
|
{token: ".", expectedError: true, bts: nil},
|
||||||
|
{token: "1234567890123456789012", expectedError: true, bts: nil}, // invalid parcel size
|
||||||
|
{token: "12345.1234567890123456", expectedError: true, bts: nil}, // invalid parcel size
|
||||||
|
{token: ".1234567890123456", expectedError: true, bts: nil}, // invalid parcel size
|
||||||
|
{token: "123456.", expectedError: true, bts: nil}, // invalid parcel size
|
||||||
|
{token: "123456:1234567890.123456", expectedError: true, bts: nil}, // invalid separation
|
||||||
|
{token: "abcdef:1234567890123456", expectedError: true, bts: nil}, // invalid separation
|
||||||
|
{token: "Abcdef.1234567890123456", expectedError: true, bts: nil}, // invalid token id
|
||||||
|
{token: "123456.AABBCCDDEEFFGGHH", expectedError: true, bts: nil}, // invalid token secret
|
||||||
|
{token: "123456.AABBCCD-EEFFGGHH", expectedError: true, bts: nil}, // invalid character
|
||||||
|
{token: "abc*ef.1234567890123456", expectedError: true, bts: nil}, // invalid character
|
||||||
|
{token: "abcdef.1234567890123456", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "1234567890123456"}},
|
||||||
|
{token: "123456.aabbccddeeffgghh", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "aabbccddeeffgghh"}},
|
||||||
|
{token: "abcdef.abcdef0123456789", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}},
|
||||||
|
{token: "123456.1234560123456789", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "1234560123456789"}},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
actual, err := NewBootstrapTokenString(rt.token)
|
||||||
|
if (err != nil) != rt.expectedError {
|
||||||
|
t.Errorf(
|
||||||
|
"failed NewBootstrapTokenString for the token %q\n\texpected error: %t\n\t actual error: %v",
|
||||||
|
rt.token,
|
||||||
|
rt.expectedError,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
} else if !reflect.DeepEqual(actual, rt.bts) {
|
||||||
|
t.Errorf(
|
||||||
|
"failed NewBootstrapTokenString for the token %q\n\texpected: %v\n\t actual: %v",
|
||||||
|
rt.token,
|
||||||
|
rt.bts,
|
||||||
|
actual,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewBootstrapTokenStringFromIDAndSecret(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
id, secret string
|
||||||
|
expectedError bool
|
||||||
|
bts *BootstrapTokenString
|
||||||
|
}{
|
||||||
|
{id: "", secret: "", expectedError: true, bts: nil},
|
||||||
|
{id: "1234567890123456789012", secret: "", expectedError: true, bts: nil}, // invalid parcel size
|
||||||
|
{id: "12345", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid parcel size
|
||||||
|
{id: "", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid parcel size
|
||||||
|
{id: "123456", secret: "", expectedError: true, bts: nil}, // invalid parcel size
|
||||||
|
{id: "Abcdef", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid token id
|
||||||
|
{id: "123456", secret: "AABBCCDDEEFFGGHH", expectedError: true, bts: nil}, // invalid token secret
|
||||||
|
{id: "123456", secret: "AABBCCD-EEFFGGHH", expectedError: true, bts: nil}, // invalid character
|
||||||
|
{id: "abc*ef", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid character
|
||||||
|
{id: "abcdef", secret: "1234567890123456", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "1234567890123456"}},
|
||||||
|
{id: "123456", secret: "aabbccddeeffgghh", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "aabbccddeeffgghh"}},
|
||||||
|
{id: "abcdef", secret: "abcdef0123456789", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}},
|
||||||
|
{id: "123456", secret: "1234560123456789", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "1234560123456789"}},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
actual, err := NewBootstrapTokenStringFromIDAndSecret(rt.id, rt.secret)
|
||||||
|
if (err != nil) != rt.expectedError {
|
||||||
|
t.Errorf(
|
||||||
|
"failed NewBootstrapTokenStringFromIDAndSecret for the token with id %q and secret %q\n\texpected error: %t\n\t actual error: %v",
|
||||||
|
rt.id,
|
||||||
|
rt.secret,
|
||||||
|
rt.expectedError,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
} else if !reflect.DeepEqual(actual, rt.bts) {
|
||||||
|
t.Errorf(
|
||||||
|
"failed NewBootstrapTokenStringFromIDAndSecret for the token with id %q and secret %q\n\texpected: %v\n\t actual: %v",
|
||||||
|
rt.id,
|
||||||
|
rt.secret,
|
||||||
|
rt.bts,
|
||||||
|
actual,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -43,10 +43,17 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
|
|||||||
obj.CertificatesDir = "foo"
|
obj.CertificatesDir = "foo"
|
||||||
obj.APIServerCertSANs = []string{"foo"}
|
obj.APIServerCertSANs = []string{"foo"}
|
||||||
|
|
||||||
obj.Token = "foo"
|
obj.BootstrapTokens = []kubeadm.BootstrapToken{
|
||||||
obj.TokenTTL = &metav1.Duration{Duration: 1 * time.Hour}
|
{
|
||||||
obj.TokenUsages = []string{"foo"}
|
Token: &kubeadm.BootstrapTokenString{
|
||||||
obj.TokenGroups = []string{"foo"}
|
ID: "abcdef",
|
||||||
|
Secret: "abcdef0123456789",
|
||||||
|
},
|
||||||
|
TTL: &metav1.Duration{Duration: 1 * time.Hour},
|
||||||
|
Usages: []string{"foo"},
|
||||||
|
Groups: []string{"foo"},
|
||||||
|
},
|
||||||
|
}
|
||||||
obj.ImageRepository = "foo"
|
obj.ImageRepository = "foo"
|
||||||
obj.CIImageRepository = ""
|
obj.CIImageRepository = ""
|
||||||
obj.UnifiedControlPlaneImage = "foo"
|
obj.UnifiedControlPlaneImage = "foo"
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
package(default_visibility = ["//visibility:public"])
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "package-srcs",
|
|
||||||
srcs = glob(["**"]),
|
|
||||||
tags = ["automanaged"],
|
|
||||||
visibility = ["//visibility:private"],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "all-srcs",
|
|
||||||
srcs = [":package-srcs"],
|
|
||||||
tags = ["automanaged"],
|
|
||||||
)
|
|
@ -48,15 +48,9 @@ type MasterConfiguration struct {
|
|||||||
// NodeRegistration holds fields that relate to registering the new master node to the cluster
|
// NodeRegistration holds fields that relate to registering the new master node to the cluster
|
||||||
NodeRegistration NodeRegistrationOptions
|
NodeRegistration NodeRegistrationOptions
|
||||||
|
|
||||||
// Token is used for establishing bidirectional trust between nodes and masters.
|
// BootstrapTokens is respected at `kubeadm init` time and describes a set of Bootstrap Tokens to create.
|
||||||
// Used for joining nodes in the cluster.
|
// This information IS NOT uploaded to the kubeadm cluster configmap, due to its sensitive nature
|
||||||
Token string
|
BootstrapTokens []BootstrapToken
|
||||||
// TokenTTL defines the ttl for Token. Defaults to 24h.
|
|
||||||
TokenTTL *metav1.Duration
|
|
||||||
// TokenUsages describes the ways in which this token can be used.
|
|
||||||
TokenUsages []string
|
|
||||||
// Extra groups that this token will authenticate as when used for authentication
|
|
||||||
TokenGroups []string
|
|
||||||
|
|
||||||
// APIServerExtraArgs is a set of extra flags to pass to the API Server or override
|
// APIServerExtraArgs is a set of extra flags to pass to the API Server or override
|
||||||
// default ones in form of <flagname>=<value>.
|
// default ones in form of <flagname>=<value>.
|
||||||
@ -153,18 +147,6 @@ type NodeRegistrationOptions struct {
|
|||||||
ExtraArgs map[string]string
|
ExtraArgs map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TokenDiscovery contains elements needed for token discovery.
|
|
||||||
type TokenDiscovery struct {
|
|
||||||
// ID is the first part of a bootstrap token. Considered public information.
|
|
||||||
// It is used when referring to a token without leaking the secret part.
|
|
||||||
ID string
|
|
||||||
// Secret is the second part of a bootstrap token. Should only be shared
|
|
||||||
// with trusted parties.
|
|
||||||
Secret string
|
|
||||||
// TODO: Seems unused. Remove?
|
|
||||||
// Addresses []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Networking contains elements describing cluster's networking configuration.
|
// Networking contains elements describing cluster's networking configuration.
|
||||||
type Networking struct {
|
type Networking struct {
|
||||||
// ServiceSubnet is the subnet used by k8s services. Defaults to "10.96.0.0/12".
|
// ServiceSubnet is the subnet used by k8s services. Defaults to "10.96.0.0/12".
|
||||||
@ -175,6 +157,30 @@ type Networking struct {
|
|||||||
DNSDomain string
|
DNSDomain string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BootstrapToken describes one bootstrap token, stored as a Secret in the cluster
|
||||||
|
// TODO: The BootstrapToken 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.
|
||||||
|
type BootstrapToken struct {
|
||||||
|
// Token is used for establishing bidirectional trust between nodes and masters.
|
||||||
|
// Used for joining nodes in the cluster.
|
||||||
|
Token *BootstrapTokenString
|
||||||
|
// Description sets a human-friendly message why this token exists and what it's used
|
||||||
|
// for, so other administrators can know its purpose.
|
||||||
|
Description string
|
||||||
|
// TTL defines the time to live for this token. Defaults to 24h.
|
||||||
|
// Expires and TTL are mutually exclusive.
|
||||||
|
TTL *metav1.Duration
|
||||||
|
// Expires specifies the timestamp when this token expires. Defaults to being set
|
||||||
|
// dynamically at runtime based on the TTL. Expires and TTL are mutually exclusive.
|
||||||
|
Expires *metav1.Time
|
||||||
|
// Usages describes the ways in which this token can be used. Can by default be used
|
||||||
|
// for establishing bidirectional trust, but that can be changed here.
|
||||||
|
Usages []string
|
||||||
|
// Groups specifies the extra groups that this token will authenticate as when/if
|
||||||
|
// used for authentication
|
||||||
|
Groups []string
|
||||||
|
}
|
||||||
|
|
||||||
// Etcd contains elements describing Etcd configuration.
|
// Etcd contains elements describing Etcd configuration.
|
||||||
type Etcd struct {
|
type Etcd struct {
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package v1alpha1
|
package v1alpha1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -54,6 +55,9 @@ func Convert_v1alpha1_MasterConfiguration_To_kubeadm_MasterConfiguration(in *Mas
|
|||||||
UpgradeCloudProvider(in, out)
|
UpgradeCloudProvider(in, out)
|
||||||
UpgradeAuthorizationModes(in, out)
|
UpgradeAuthorizationModes(in, out)
|
||||||
UpgradeNodeRegistrationOptionsForMaster(in, out)
|
UpgradeNodeRegistrationOptionsForMaster(in, out)
|
||||||
|
if err := UpgradeBootstrapTokens(in, out); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
// We don't support migrating information from the .PrivilegedPods field which was removed in v1alpha2
|
// We don't support migrating information from the .PrivilegedPods field which was removed in v1alpha2
|
||||||
// We don't support migrating information from the .ImagePullPolicy field which was removed in v1alpha2
|
// We don't support migrating information from the .ImagePullPolicy field which was removed in v1alpha2
|
||||||
|
|
||||||
@ -100,25 +104,6 @@ func Convert_v1alpha1_Etcd_To_kubeadm_Etcd(in *Etcd, out *kubeadm.Etcd, s conver
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// no-op, as we don't support converting from newer API to old alpha API
|
|
||||||
func Convert_kubeadm_Etcd_To_v1alpha1_Etcd(in *kubeadm.Etcd, out *Etcd, s conversion.Scope) error {
|
|
||||||
|
|
||||||
if in.External != nil {
|
|
||||||
out.Endpoints = in.External.Endpoints
|
|
||||||
out.CAFile = in.External.CAFile
|
|
||||||
out.CertFile = in.External.CertFile
|
|
||||||
out.KeyFile = in.External.KeyFile
|
|
||||||
} else {
|
|
||||||
out.Image = in.Local.Image
|
|
||||||
out.DataDir = in.Local.DataDir
|
|
||||||
out.ExtraArgs = in.Local.ExtraArgs
|
|
||||||
out.ServerCertSANs = in.Local.ServerCertSANs
|
|
||||||
out.PeerCertSANs = in.Local.PeerCertSANs
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpgradeCloudProvider handles the removal of .CloudProvider as smoothly as possible
|
// UpgradeCloudProvider handles the removal of .CloudProvider as smoothly as possible
|
||||||
func UpgradeCloudProvider(in *MasterConfiguration, out *kubeadm.MasterConfiguration) {
|
func UpgradeCloudProvider(in *MasterConfiguration, out *kubeadm.MasterConfiguration) {
|
||||||
if len(in.CloudProvider) != 0 {
|
if len(in.CloudProvider) != 0 {
|
||||||
@ -160,8 +145,30 @@ func UpgradeNodeRegistrationOptionsForMaster(in *MasterConfiguration, out *kubea
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UpgradeBootstrapTokens(in *MasterConfiguration, out *kubeadm.MasterConfiguration) error {
|
||||||
|
if len(in.Token) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bts, err := kubeadm.NewBootstrapTokenString(in.Token)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't parse .Token, and hence can't convert v1alpha1 API to a newer version: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out.BootstrapTokens = []kubeadm.BootstrapToken{
|
||||||
|
{
|
||||||
|
Token: bts,
|
||||||
|
TTL: in.TokenTTL,
|
||||||
|
Usages: in.TokenUsages,
|
||||||
|
Groups: in.TokenGroups,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Downgrades below
|
// Downgrades below
|
||||||
|
|
||||||
|
// This downgrade path IS NOT SUPPORTED. This is just here for roundtripping purposes at the moment.
|
||||||
func Convert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in *kubeadm.MasterConfiguration, out *MasterConfiguration, s conversion.Scope) error {
|
func Convert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in *kubeadm.MasterConfiguration, out *MasterConfiguration, s conversion.Scope) error {
|
||||||
if err := autoConvert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in, out, s); err != nil {
|
if err := autoConvert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in, out, s); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -172,9 +179,17 @@ func Convert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in *kub
|
|||||||
out.CRISocket = in.NodeRegistration.CRISocket
|
out.CRISocket = in.NodeRegistration.CRISocket
|
||||||
out.NoTaintMaster = in.NodeRegistration.Taints != nil && len(in.NodeRegistration.Taints) == 0
|
out.NoTaintMaster = in.NodeRegistration.Taints != nil && len(in.NodeRegistration.Taints) == 0
|
||||||
|
|
||||||
|
if len(in.BootstrapTokens) > 0 {
|
||||||
|
out.Token = in.BootstrapTokens[0].Token.String()
|
||||||
|
out.TokenTTL = in.BootstrapTokens[0].TTL
|
||||||
|
out.TokenUsages = in.BootstrapTokens[0].Usages
|
||||||
|
out.TokenGroups = in.BootstrapTokens[0].Groups
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This downgrade path IS NOT SUPPORTED. This is just here for roundtripping purposes at the moment.
|
||||||
func Convert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration(in *kubeadm.NodeConfiguration, out *NodeConfiguration, s conversion.Scope) error {
|
func Convert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration(in *kubeadm.NodeConfiguration, out *NodeConfiguration, s conversion.Scope) error {
|
||||||
if err := autoConvert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration(in, out, s); err != nil {
|
if err := autoConvert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration(in, out, s); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -183,6 +198,27 @@ func Convert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration(in *kubeadm
|
|||||||
// Converting from newer API version to an older API version isn't supported. This is here only for the roundtrip tests meanwhile.
|
// Converting from newer API version to an older API version isn't supported. This is here only for the roundtrip tests meanwhile.
|
||||||
out.NodeName = in.NodeRegistration.Name
|
out.NodeName = in.NodeRegistration.Name
|
||||||
out.CRISocket = in.NodeRegistration.CRISocket
|
out.CRISocket = in.NodeRegistration.CRISocket
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This downgrade path IS NOT SUPPORTED. This is just here for roundtripping purposes at the moment.
|
||||||
|
func Convert_kubeadm_Etcd_To_v1alpha1_Etcd(in *kubeadm.Etcd, out *Etcd, s conversion.Scope) error {
|
||||||
|
if err := autoConvert_kubeadm_Etcd_To_v1alpha1_Etcd(in, out, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.External != nil {
|
||||||
|
out.Endpoints = in.External.Endpoints
|
||||||
|
out.CAFile = in.External.CAFile
|
||||||
|
out.CertFile = in.External.CertFile
|
||||||
|
out.KeyFile = in.External.KeyFile
|
||||||
|
} else {
|
||||||
|
out.Image = in.Local.Image
|
||||||
|
out.DataDir = in.Local.DataDir
|
||||||
|
out.ExtraArgs = in.Local.ExtraArgs
|
||||||
|
out.ServerCertSANs = in.Local.ServerCertSANs
|
||||||
|
out.PeerCertSANs = in.Local.PeerCertSANs
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -58,8 +58,6 @@ func RegisterConversions(scheme *runtime.Scheme) error {
|
|||||||
Convert_kubeadm_Networking_To_v1alpha1_Networking,
|
Convert_kubeadm_Networking_To_v1alpha1_Networking,
|
||||||
Convert_v1alpha1_NodeConfiguration_To_kubeadm_NodeConfiguration,
|
Convert_v1alpha1_NodeConfiguration_To_kubeadm_NodeConfiguration,
|
||||||
Convert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration,
|
Convert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration,
|
||||||
Convert_v1alpha1_TokenDiscovery_To_kubeadm_TokenDiscovery,
|
|
||||||
Convert_kubeadm_TokenDiscovery_To_v1alpha1_TokenDiscovery,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,10 +219,10 @@ func autoConvert_v1alpha1_MasterConfiguration_To_kubeadm_MasterConfiguration(in
|
|||||||
// WARNING: in.AuthorizationModes requires manual conversion: does not exist in peer-type
|
// WARNING: in.AuthorizationModes requires manual conversion: does not exist in peer-type
|
||||||
// WARNING: in.NoTaintMaster requires manual conversion: does not exist in peer-type
|
// WARNING: in.NoTaintMaster requires manual conversion: does not exist in peer-type
|
||||||
// WARNING: in.PrivilegedPods requires manual conversion: does not exist in peer-type
|
// WARNING: in.PrivilegedPods requires manual conversion: does not exist in peer-type
|
||||||
out.Token = in.Token
|
// WARNING: in.Token requires manual conversion: does not exist in peer-type
|
||||||
out.TokenTTL = (*meta_v1.Duration)(unsafe.Pointer(in.TokenTTL))
|
// WARNING: in.TokenTTL requires manual conversion: does not exist in peer-type
|
||||||
out.TokenUsages = *(*[]string)(unsafe.Pointer(&in.TokenUsages))
|
// WARNING: in.TokenUsages requires manual conversion: does not exist in peer-type
|
||||||
out.TokenGroups = *(*[]string)(unsafe.Pointer(&in.TokenGroups))
|
// WARNING: in.TokenGroups requires manual conversion: does not exist in peer-type
|
||||||
// WARNING: in.CRISocket requires manual conversion: does not exist in peer-type
|
// WARNING: in.CRISocket requires manual conversion: does not exist in peer-type
|
||||||
out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs))
|
out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs))
|
||||||
out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs))
|
out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs))
|
||||||
@ -263,10 +261,7 @@ func autoConvert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in
|
|||||||
}
|
}
|
||||||
out.KubernetesVersion = in.KubernetesVersion
|
out.KubernetesVersion = in.KubernetesVersion
|
||||||
// WARNING: in.NodeRegistration requires manual conversion: does not exist in peer-type
|
// WARNING: in.NodeRegistration requires manual conversion: does not exist in peer-type
|
||||||
out.Token = in.Token
|
// WARNING: in.BootstrapTokens requires manual conversion: does not exist in peer-type
|
||||||
out.TokenTTL = (*meta_v1.Duration)(unsafe.Pointer(in.TokenTTL))
|
|
||||||
out.TokenUsages = *(*[]string)(unsafe.Pointer(&in.TokenUsages))
|
|
||||||
out.TokenGroups = *(*[]string)(unsafe.Pointer(&in.TokenGroups))
|
|
||||||
out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs))
|
out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs))
|
||||||
out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs))
|
out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs))
|
||||||
out.SchedulerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.SchedulerExtraArgs))
|
out.SchedulerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.SchedulerExtraArgs))
|
||||||
@ -342,25 +337,3 @@ func autoConvert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration(in *kub
|
|||||||
out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates))
|
out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func autoConvert_v1alpha1_TokenDiscovery_To_kubeadm_TokenDiscovery(in *TokenDiscovery, out *kubeadm.TokenDiscovery, s conversion.Scope) error {
|
|
||||||
out.ID = in.ID
|
|
||||||
out.Secret = in.Secret
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert_v1alpha1_TokenDiscovery_To_kubeadm_TokenDiscovery is an autogenerated conversion function.
|
|
||||||
func Convert_v1alpha1_TokenDiscovery_To_kubeadm_TokenDiscovery(in *TokenDiscovery, out *kubeadm.TokenDiscovery, s conversion.Scope) error {
|
|
||||||
return autoConvert_v1alpha1_TokenDiscovery_To_kubeadm_TokenDiscovery(in, out, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func autoConvert_kubeadm_TokenDiscovery_To_v1alpha1_TokenDiscovery(in *kubeadm.TokenDiscovery, out *TokenDiscovery, s conversion.Scope) error {
|
|
||||||
out.ID = in.ID
|
|
||||||
out.Secret = in.Secret
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert_kubeadm_TokenDiscovery_To_v1alpha1_TokenDiscovery is an autogenerated conversion function.
|
|
||||||
func Convert_kubeadm_TokenDiscovery_To_v1alpha1_TokenDiscovery(in *kubeadm.TokenDiscovery, out *TokenDiscovery, s conversion.Scope) error {
|
|
||||||
return autoConvert_kubeadm_TokenDiscovery_To_v1alpha1_TokenDiscovery(in, out, s)
|
|
||||||
}
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = [
|
srcs = [
|
||||||
|
"bootstraptokenstring.go",
|
||||||
"defaults.go",
|
"defaults.go",
|
||||||
"doc.go",
|
"doc.go",
|
||||||
"register.go",
|
"register.go",
|
||||||
@ -61,6 +62,8 @@ go_library(
|
|||||||
"//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/tools/bootstrap/token/api:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/tools/bootstrap/token/util:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -77,3 +80,9 @@ filegroup(
|
|||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = ["bootstraptokenstring_test.go"],
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
)
|
||||||
|
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package v1alpha2 holds the external kubeadm API types of version v1alpha2
|
||||||
|
// Note: This file should be kept in sync with the similar one for the internal 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 v1alpha2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
bootstrapapi "k8s.io/client-go/tools/bootstrap/token/api"
|
||||||
|
bootstraputil "k8s.io/client-go/tools/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, fmt.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))
|
||||||
|
}
|
@ -0,0 +1,236 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v1alpha2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMarshalJSON(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
bts BootstrapTokenString
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, `"abcdef.abcdef0123456789"`},
|
||||||
|
{BootstrapTokenString{ID: "foo", Secret: "bar"}, `"foo.bar"`},
|
||||||
|
{BootstrapTokenString{ID: "h", Secret: "b"}, `"h.b"`},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
b, err := json.Marshal(rt.bts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("json.Marshal returned an unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if string(b) != rt.expected {
|
||||||
|
t.Errorf(
|
||||||
|
"failed BootstrapTokenString.MarshalJSON:\n\texpected: %s\n\t actual: %s",
|
||||||
|
rt.expected,
|
||||||
|
string(b),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalJSON(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
input string
|
||||||
|
bts *BootstrapTokenString
|
||||||
|
expectedError bool
|
||||||
|
}{
|
||||||
|
{`"f.s"`, &BootstrapTokenString{}, true},
|
||||||
|
{`"abcdef."`, &BootstrapTokenString{}, true},
|
||||||
|
{`"abcdef:abcdef0123456789"`, &BootstrapTokenString{}, true},
|
||||||
|
{`abcdef.abcdef0123456789`, &BootstrapTokenString{}, true},
|
||||||
|
{`"abcdef.abcdef0123456789`, &BootstrapTokenString{}, true},
|
||||||
|
{`"abcdef.ABCDEF0123456789"`, &BootstrapTokenString{}, true},
|
||||||
|
{`"abcdef.abcdef0123456789"`, &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, false},
|
||||||
|
{`"123456.aabbccddeeffgghh"`, &BootstrapTokenString{ID: "123456", Secret: "aabbccddeeffgghh"}, false},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
newbts := &BootstrapTokenString{}
|
||||||
|
err := json.Unmarshal([]byte(rt.input), newbts)
|
||||||
|
if (err != nil) != rt.expectedError {
|
||||||
|
t.Errorf("failed BootstrapTokenString.UnmarshalJSON:\n\texpected error: %t\n\t actual error: %v", rt.expectedError, err)
|
||||||
|
} else if !reflect.DeepEqual(rt.bts, newbts) {
|
||||||
|
t.Errorf(
|
||||||
|
"failed BootstrapTokenString.UnmarshalJSON:\n\texpected: %v\n\t actual: %v",
|
||||||
|
rt.bts,
|
||||||
|
newbts,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONRoundtrip(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
input string
|
||||||
|
bts *BootstrapTokenString
|
||||||
|
}{
|
||||||
|
{`"abcdef.abcdef0123456789"`, nil},
|
||||||
|
{"", &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
if err := roundtrip(rt.input, rt.bts); err != nil {
|
||||||
|
t.Errorf("failed BootstrapTokenString JSON roundtrip with error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func roundtrip(input string, bts *BootstrapTokenString) error {
|
||||||
|
var b []byte
|
||||||
|
var err error
|
||||||
|
newbts := &BootstrapTokenString{}
|
||||||
|
// If string input was specified, roundtrip like this: string -> (unmarshal) -> object -> (marshal) -> string
|
||||||
|
if len(input) > 0 {
|
||||||
|
if err := json.Unmarshal([]byte(input), newbts); err != nil {
|
||||||
|
return fmt.Errorf("expected no unmarshal error, got error: %v", err)
|
||||||
|
}
|
||||||
|
if b, err = json.Marshal(newbts); err != nil {
|
||||||
|
return fmt.Errorf("expected no marshal error, got error: %v", err)
|
||||||
|
}
|
||||||
|
if input != string(b) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"expected token: %s\n\t actual: %s",
|
||||||
|
input,
|
||||||
|
string(b),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else { // Otherwise, roundtrip like this: object -> (marshal) -> string -> (unmarshal) -> object
|
||||||
|
if b, err = json.Marshal(bts); err != nil {
|
||||||
|
return fmt.Errorf("expected no marshal error, got error: %v", err)
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(b, newbts); err != nil {
|
||||||
|
return fmt.Errorf("expected no unmarshal error, got error: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(bts, newbts) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"expected object: %v\n\t actual: %v",
|
||||||
|
bts,
|
||||||
|
newbts,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTokenFromIDAndSecret(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
bts BootstrapTokenString
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{BootstrapTokenString{ID: "foo", Secret: "bar"}, "foo.bar"},
|
||||||
|
{BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}, "abcdef.abcdef0123456789"},
|
||||||
|
{BootstrapTokenString{ID: "h", Secret: "b"}, "h.b"},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
actual := rt.bts.String()
|
||||||
|
if actual != rt.expected {
|
||||||
|
t.Errorf(
|
||||||
|
"failed BootstrapTokenString.String():\n\texpected: %s\n\t actual: %s",
|
||||||
|
rt.expected,
|
||||||
|
actual,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewBootstrapTokenString(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
token string
|
||||||
|
expectedError bool
|
||||||
|
bts *BootstrapTokenString
|
||||||
|
}{
|
||||||
|
{token: "", expectedError: true, bts: nil},
|
||||||
|
{token: ".", expectedError: true, bts: nil},
|
||||||
|
{token: "1234567890123456789012", expectedError: true, bts: nil}, // invalid parcel size
|
||||||
|
{token: "12345.1234567890123456", expectedError: true, bts: nil}, // invalid parcel size
|
||||||
|
{token: ".1234567890123456", expectedError: true, bts: nil}, // invalid parcel size
|
||||||
|
{token: "123456.", expectedError: true, bts: nil}, // invalid parcel size
|
||||||
|
{token: "123456:1234567890.123456", expectedError: true, bts: nil}, // invalid separation
|
||||||
|
{token: "abcdef:1234567890123456", expectedError: true, bts: nil}, // invalid separation
|
||||||
|
{token: "Abcdef.1234567890123456", expectedError: true, bts: nil}, // invalid token id
|
||||||
|
{token: "123456.AABBCCDDEEFFGGHH", expectedError: true, bts: nil}, // invalid token secret
|
||||||
|
{token: "123456.AABBCCD-EEFFGGHH", expectedError: true, bts: nil}, // invalid character
|
||||||
|
{token: "abc*ef.1234567890123456", expectedError: true, bts: nil}, // invalid character
|
||||||
|
{token: "abcdef.1234567890123456", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "1234567890123456"}},
|
||||||
|
{token: "123456.aabbccddeeffgghh", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "aabbccddeeffgghh"}},
|
||||||
|
{token: "abcdef.abcdef0123456789", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}},
|
||||||
|
{token: "123456.1234560123456789", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "1234560123456789"}},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
actual, err := NewBootstrapTokenString(rt.token)
|
||||||
|
if (err != nil) != rt.expectedError {
|
||||||
|
t.Errorf(
|
||||||
|
"failed NewBootstrapTokenString for the token %q\n\texpected error: %t\n\t actual error: %v",
|
||||||
|
rt.token,
|
||||||
|
rt.expectedError,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
} else if !reflect.DeepEqual(actual, rt.bts) {
|
||||||
|
t.Errorf(
|
||||||
|
"failed NewBootstrapTokenString for the token %q\n\texpected: %v\n\t actual: %v",
|
||||||
|
rt.token,
|
||||||
|
rt.bts,
|
||||||
|
actual,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewBootstrapTokenStringFromIDAndSecret(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
id, secret string
|
||||||
|
expectedError bool
|
||||||
|
bts *BootstrapTokenString
|
||||||
|
}{
|
||||||
|
{id: "", secret: "", expectedError: true, bts: nil},
|
||||||
|
{id: "1234567890123456789012", secret: "", expectedError: true, bts: nil}, // invalid parcel size
|
||||||
|
{id: "12345", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid parcel size
|
||||||
|
{id: "", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid parcel size
|
||||||
|
{id: "123456", secret: "", expectedError: true, bts: nil}, // invalid parcel size
|
||||||
|
{id: "Abcdef", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid token id
|
||||||
|
{id: "123456", secret: "AABBCCDDEEFFGGHH", expectedError: true, bts: nil}, // invalid token secret
|
||||||
|
{id: "123456", secret: "AABBCCD-EEFFGGHH", expectedError: true, bts: nil}, // invalid character
|
||||||
|
{id: "abc*ef", secret: "1234567890123456", expectedError: true, bts: nil}, // invalid character
|
||||||
|
{id: "abcdef", secret: "1234567890123456", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "1234567890123456"}},
|
||||||
|
{id: "123456", secret: "aabbccddeeffgghh", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "aabbccddeeffgghh"}},
|
||||||
|
{id: "abcdef", secret: "abcdef0123456789", expectedError: false, bts: &BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"}},
|
||||||
|
{id: "123456", secret: "1234560123456789", expectedError: false, bts: &BootstrapTokenString{ID: "123456", Secret: "1234560123456789"}},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
actual, err := NewBootstrapTokenStringFromIDAndSecret(rt.id, rt.secret)
|
||||||
|
if (err != nil) != rt.expectedError {
|
||||||
|
t.Errorf(
|
||||||
|
"failed NewBootstrapTokenStringFromIDAndSecret for the token with id %q and secret %q\n\texpected error: %t\n\t actual error: %v",
|
||||||
|
rt.id,
|
||||||
|
rt.secret,
|
||||||
|
rt.expectedError,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
} else if !reflect.DeepEqual(actual, rt.bts) {
|
||||||
|
t.Errorf(
|
||||||
|
"failed NewBootstrapTokenStringFromIDAndSecret for the token with id %q and secret %q\n\texpected: %v\n\t actual: %v",
|
||||||
|
rt.id,
|
||||||
|
rt.secret,
|
||||||
|
rt.bts,
|
||||||
|
actual,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -97,20 +97,6 @@ func SetDefaults_MasterConfiguration(obj *MasterConfiguration) {
|
|||||||
obj.CertificatesDir = DefaultCertificatesDir
|
obj.CertificatesDir = DefaultCertificatesDir
|
||||||
}
|
}
|
||||||
|
|
||||||
if obj.TokenTTL == nil {
|
|
||||||
obj.TokenTTL = &metav1.Duration{
|
|
||||||
Duration: constants.DefaultTokenDuration,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(obj.TokenUsages) == 0 {
|
|
||||||
obj.TokenUsages = constants.DefaultTokenUsages
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(obj.TokenGroups) == 0 {
|
|
||||||
obj.TokenGroups = constants.DefaultTokenGroups
|
|
||||||
}
|
|
||||||
|
|
||||||
if obj.ImageRepository == "" {
|
if obj.ImageRepository == "" {
|
||||||
obj.ImageRepository = DefaultImageRepository
|
obj.ImageRepository = DefaultImageRepository
|
||||||
}
|
}
|
||||||
@ -120,6 +106,7 @@ func SetDefaults_MasterConfiguration(obj *MasterConfiguration) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SetDefaults_NodeRegistrationOptions(&obj.NodeRegistration)
|
SetDefaults_NodeRegistrationOptions(&obj.NodeRegistration)
|
||||||
|
SetDefaults_BootstrapTokens(obj)
|
||||||
SetDefaults_KubeletConfiguration(obj)
|
SetDefaults_KubeletConfiguration(obj)
|
||||||
SetDefaults_Etcd(obj)
|
SetDefaults_Etcd(obj)
|
||||||
SetDefaults_ProxyConfiguration(obj)
|
SetDefaults_ProxyConfiguration(obj)
|
||||||
@ -248,3 +235,35 @@ func SetDefaults_AuditPolicyConfiguration(obj *MasterConfiguration) {
|
|||||||
obj.AuditPolicyConfiguration.LogMaxAge = &DefaultAuditPolicyLogMaxAge
|
obj.AuditPolicyConfiguration.LogMaxAge = &DefaultAuditPolicyLogMaxAge
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetDefaults_BootstrapTokens sets the defaults for the .BootstrapTokens field
|
||||||
|
// If the slice is empty, it's defaulted with one token. Otherwise it just loops
|
||||||
|
// through the slice and sets the defaults for the omitempty fields that are TTL,
|
||||||
|
// Usages and Groups. Token is NOT defaulted with a random one in the API defaulting
|
||||||
|
// layer, but set to a random value later at runtime if not set before.
|
||||||
|
func SetDefaults_BootstrapTokens(obj *MasterConfiguration) {
|
||||||
|
|
||||||
|
if obj.BootstrapTokens == nil || len(obj.BootstrapTokens) == 0 {
|
||||||
|
obj.BootstrapTokens = []BootstrapToken{{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, bt := range obj.BootstrapTokens {
|
||||||
|
SetDefaults_BootstrapToken(&bt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDefaults_BootstrapToken sets the defaults for an individual Bootstrap Token
|
||||||
|
func SetDefaults_BootstrapToken(bt *BootstrapToken) {
|
||||||
|
if bt.TTL == nil {
|
||||||
|
bt.TTL = &metav1.Duration{
|
||||||
|
Duration: constants.DefaultTokenDuration,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(bt.Usages) == 0 {
|
||||||
|
bt.Usages = constants.DefaultTokenUsages
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(bt.Groups) == 0 {
|
||||||
|
bt.Groups = constants.DefaultTokenGroups
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -47,15 +47,9 @@ type MasterConfiguration struct {
|
|||||||
// KubernetesVersion is the target version of the control plane.
|
// KubernetesVersion is the target version of the control plane.
|
||||||
KubernetesVersion string `json:"kubernetesVersion"`
|
KubernetesVersion string `json:"kubernetesVersion"`
|
||||||
|
|
||||||
// Token is used for establishing bidirectional trust between nodes and masters.
|
// BootstrapTokens is respected at `kubeadm init` time and describes a set of Bootstrap Tokens to create.
|
||||||
// Used for joining nodes in the cluster.
|
// This information IS NOT uploaded to the kubeadm cluster configmap, due to its sensitive nature
|
||||||
Token string `json:"token"`
|
BootstrapTokens []BootstrapToken `json:"bootstrapTokens,omitempty"`
|
||||||
// TokenTTL defines the ttl for Token. Defaults to 24h.
|
|
||||||
TokenTTL *metav1.Duration `json:"tokenTTL,omitempty"`
|
|
||||||
// TokenUsages describes the ways in which this token can be used.
|
|
||||||
TokenUsages []string `json:"tokenUsages,omitempty"`
|
|
||||||
// Extra groups that this token will authenticate as when used for authentication
|
|
||||||
TokenGroups []string `json:"tokenGroups,omitempty"`
|
|
||||||
|
|
||||||
// APIServerExtraArgs is a set of extra flags to pass to the API Server or override
|
// APIServerExtraArgs is a set of extra flags to pass to the API Server or override
|
||||||
// default ones in form of <flagname>=<value>.
|
// default ones in form of <flagname>=<value>.
|
||||||
@ -145,18 +139,6 @@ type NodeRegistrationOptions struct {
|
|||||||
ExtraArgs map[string]string `json:"kubeletExtraArgs,omitempty"`
|
ExtraArgs map[string]string `json:"kubeletExtraArgs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TokenDiscovery contains elements needed for token discovery.
|
|
||||||
type TokenDiscovery struct {
|
|
||||||
// ID is the first part of a bootstrap token. Considered public information.
|
|
||||||
// It is used when referring to a token without leaking the secret part.
|
|
||||||
ID string `json:"id"`
|
|
||||||
// Secret is the second part of a bootstrap token. Should only be shared
|
|
||||||
// with trusted parties.
|
|
||||||
Secret string `json:"secret"`
|
|
||||||
// TODO: Seems unused. Remove?
|
|
||||||
// Addresses []string `json:"addresses"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Networking contains elements describing cluster's networking configuration
|
// Networking contains elements describing cluster's networking configuration
|
||||||
type Networking struct {
|
type Networking struct {
|
||||||
// ServiceSubnet is the subnet used by k8s services. Defaults to "10.96.0.0/12".
|
// ServiceSubnet is the subnet used by k8s services. Defaults to "10.96.0.0/12".
|
||||||
@ -167,6 +149,28 @@ type Networking struct {
|
|||||||
DNSDomain string `json:"dnsDomain"`
|
DNSDomain string `json:"dnsDomain"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BootstrapToken describes one bootstrap token, stored as a Secret in the cluster
|
||||||
|
type BootstrapToken struct {
|
||||||
|
// Token is used for establishing bidirectional trust between nodes and masters.
|
||||||
|
// Used for joining nodes in the cluster.
|
||||||
|
Token *BootstrapTokenString `json:"token"`
|
||||||
|
// Description sets a human-friendly message why this token exists and what it's used
|
||||||
|
// for, so other administrators can know its purpose.
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
// TTL defines the time to live for this token. Defaults to 24h.
|
||||||
|
// Expires and TTL are mutually exclusive.
|
||||||
|
TTL *metav1.Duration `json:"ttl,omitempty"`
|
||||||
|
// Expires specifies the timestamp when this token expires. Defaults to being set
|
||||||
|
// dynamically at runtime based on the TTL. Expires and TTL are mutually exclusive.
|
||||||
|
Expires *metav1.Time `json:"expires,omitempty"`
|
||||||
|
// Usages describes the ways in which this token can be used. Can by default be used
|
||||||
|
// for establishing bidirectional trust, but that can be changed here.
|
||||||
|
Usages []string `json:"usages,omitempty"`
|
||||||
|
// Groups specifies the extra groups that this token will authenticate as when/if
|
||||||
|
// used for authentication
|
||||||
|
Groups []string `json:"groups,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// Etcd contains elements describing Etcd configuration.
|
// Etcd contains elements describing Etcd configuration.
|
||||||
type Etcd struct {
|
type Etcd struct {
|
||||||
|
|
||||||
|
@ -23,8 +23,8 @@ package v1alpha2
|
|||||||
import (
|
import (
|
||||||
unsafe "unsafe"
|
unsafe "unsafe"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
core_v1 "k8s.io/api/core/v1"
|
||||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
@ -44,6 +44,10 @@ func RegisterConversions(scheme *runtime.Scheme) error {
|
|||||||
Convert_kubeadm_API_To_v1alpha2_API,
|
Convert_kubeadm_API_To_v1alpha2_API,
|
||||||
Convert_v1alpha2_AuditPolicyConfiguration_To_kubeadm_AuditPolicyConfiguration,
|
Convert_v1alpha2_AuditPolicyConfiguration_To_kubeadm_AuditPolicyConfiguration,
|
||||||
Convert_kubeadm_AuditPolicyConfiguration_To_v1alpha2_AuditPolicyConfiguration,
|
Convert_kubeadm_AuditPolicyConfiguration_To_v1alpha2_AuditPolicyConfiguration,
|
||||||
|
Convert_v1alpha2_BootstrapToken_To_kubeadm_BootstrapToken,
|
||||||
|
Convert_kubeadm_BootstrapToken_To_v1alpha2_BootstrapToken,
|
||||||
|
Convert_v1alpha2_BootstrapTokenString_To_kubeadm_BootstrapTokenString,
|
||||||
|
Convert_kubeadm_BootstrapTokenString_To_v1alpha2_BootstrapTokenString,
|
||||||
Convert_v1alpha2_Etcd_To_kubeadm_Etcd,
|
Convert_v1alpha2_Etcd_To_kubeadm_Etcd,
|
||||||
Convert_kubeadm_Etcd_To_v1alpha2_Etcd,
|
Convert_kubeadm_Etcd_To_v1alpha2_Etcd,
|
||||||
Convert_v1alpha2_ExternalEtcd_To_kubeadm_ExternalEtcd,
|
Convert_v1alpha2_ExternalEtcd_To_kubeadm_ExternalEtcd,
|
||||||
@ -64,8 +68,6 @@ func RegisterConversions(scheme *runtime.Scheme) error {
|
|||||||
Convert_kubeadm_NodeConfiguration_To_v1alpha2_NodeConfiguration,
|
Convert_kubeadm_NodeConfiguration_To_v1alpha2_NodeConfiguration,
|
||||||
Convert_v1alpha2_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOptions,
|
Convert_v1alpha2_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOptions,
|
||||||
Convert_kubeadm_NodeRegistrationOptions_To_v1alpha2_NodeRegistrationOptions,
|
Convert_kubeadm_NodeRegistrationOptions_To_v1alpha2_NodeRegistrationOptions,
|
||||||
Convert_v1alpha2_TokenDiscovery_To_kubeadm_TokenDiscovery,
|
|
||||||
Convert_kubeadm_TokenDiscovery_To_v1alpha2_TokenDiscovery,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,6 +119,58 @@ func Convert_kubeadm_AuditPolicyConfiguration_To_v1alpha2_AuditPolicyConfigurati
|
|||||||
return autoConvert_kubeadm_AuditPolicyConfiguration_To_v1alpha2_AuditPolicyConfiguration(in, out, s)
|
return autoConvert_kubeadm_AuditPolicyConfiguration_To_v1alpha2_AuditPolicyConfiguration(in, out, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func autoConvert_v1alpha2_BootstrapToken_To_kubeadm_BootstrapToken(in *BootstrapToken, out *kubeadm.BootstrapToken, s conversion.Scope) error {
|
||||||
|
out.Token = (*kubeadm.BootstrapTokenString)(unsafe.Pointer(in.Token))
|
||||||
|
out.Description = in.Description
|
||||||
|
out.TTL = (*v1.Duration)(unsafe.Pointer(in.TTL))
|
||||||
|
out.Expires = (*v1.Time)(unsafe.Pointer(in.Expires))
|
||||||
|
out.Usages = *(*[]string)(unsafe.Pointer(&in.Usages))
|
||||||
|
out.Groups = *(*[]string)(unsafe.Pointer(&in.Groups))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert_v1alpha2_BootstrapToken_To_kubeadm_BootstrapToken is an autogenerated conversion function.
|
||||||
|
func Convert_v1alpha2_BootstrapToken_To_kubeadm_BootstrapToken(in *BootstrapToken, out *kubeadm.BootstrapToken, s conversion.Scope) error {
|
||||||
|
return autoConvert_v1alpha2_BootstrapToken_To_kubeadm_BootstrapToken(in, out, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func autoConvert_kubeadm_BootstrapToken_To_v1alpha2_BootstrapToken(in *kubeadm.BootstrapToken, out *BootstrapToken, s conversion.Scope) error {
|
||||||
|
out.Token = (*BootstrapTokenString)(unsafe.Pointer(in.Token))
|
||||||
|
out.Description = in.Description
|
||||||
|
out.TTL = (*v1.Duration)(unsafe.Pointer(in.TTL))
|
||||||
|
out.Expires = (*v1.Time)(unsafe.Pointer(in.Expires))
|
||||||
|
out.Usages = *(*[]string)(unsafe.Pointer(&in.Usages))
|
||||||
|
out.Groups = *(*[]string)(unsafe.Pointer(&in.Groups))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert_kubeadm_BootstrapToken_To_v1alpha2_BootstrapToken is an autogenerated conversion function.
|
||||||
|
func Convert_kubeadm_BootstrapToken_To_v1alpha2_BootstrapToken(in *kubeadm.BootstrapToken, out *BootstrapToken, s conversion.Scope) error {
|
||||||
|
return autoConvert_kubeadm_BootstrapToken_To_v1alpha2_BootstrapToken(in, out, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func autoConvert_v1alpha2_BootstrapTokenString_To_kubeadm_BootstrapTokenString(in *BootstrapTokenString, out *kubeadm.BootstrapTokenString, s conversion.Scope) error {
|
||||||
|
out.ID = in.ID
|
||||||
|
out.Secret = in.Secret
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert_v1alpha2_BootstrapTokenString_To_kubeadm_BootstrapTokenString is an autogenerated conversion function.
|
||||||
|
func Convert_v1alpha2_BootstrapTokenString_To_kubeadm_BootstrapTokenString(in *BootstrapTokenString, out *kubeadm.BootstrapTokenString, s conversion.Scope) error {
|
||||||
|
return autoConvert_v1alpha2_BootstrapTokenString_To_kubeadm_BootstrapTokenString(in, out, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func autoConvert_kubeadm_BootstrapTokenString_To_v1alpha2_BootstrapTokenString(in *kubeadm.BootstrapTokenString, out *BootstrapTokenString, s conversion.Scope) error {
|
||||||
|
out.ID = in.ID
|
||||||
|
out.Secret = in.Secret
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert_kubeadm_BootstrapTokenString_To_v1alpha2_BootstrapTokenString is an autogenerated conversion function.
|
||||||
|
func Convert_kubeadm_BootstrapTokenString_To_v1alpha2_BootstrapTokenString(in *kubeadm.BootstrapTokenString, out *BootstrapTokenString, s conversion.Scope) error {
|
||||||
|
return autoConvert_kubeadm_BootstrapTokenString_To_v1alpha2_BootstrapTokenString(in, out, s)
|
||||||
|
}
|
||||||
|
|
||||||
func autoConvert_v1alpha2_Etcd_To_kubeadm_Etcd(in *Etcd, out *kubeadm.Etcd, s conversion.Scope) error {
|
func autoConvert_v1alpha2_Etcd_To_kubeadm_Etcd(in *Etcd, out *kubeadm.Etcd, s conversion.Scope) error {
|
||||||
out.Local = (*kubeadm.LocalEtcd)(unsafe.Pointer(in.Local))
|
out.Local = (*kubeadm.LocalEtcd)(unsafe.Pointer(in.Local))
|
||||||
out.External = (*kubeadm.ExternalEtcd)(unsafe.Pointer(in.External))
|
out.External = (*kubeadm.ExternalEtcd)(unsafe.Pointer(in.External))
|
||||||
@ -170,7 +224,7 @@ func autoConvert_v1alpha2_HostPathMount_To_kubeadm_HostPathMount(in *HostPathMou
|
|||||||
out.HostPath = in.HostPath
|
out.HostPath = in.HostPath
|
||||||
out.MountPath = in.MountPath
|
out.MountPath = in.MountPath
|
||||||
out.Writable = in.Writable
|
out.Writable = in.Writable
|
||||||
out.PathType = v1.HostPathType(in.PathType)
|
out.PathType = core_v1.HostPathType(in.PathType)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +238,7 @@ func autoConvert_kubeadm_HostPathMount_To_v1alpha2_HostPathMount(in *kubeadm.Hos
|
|||||||
out.HostPath = in.HostPath
|
out.HostPath = in.HostPath
|
||||||
out.MountPath = in.MountPath
|
out.MountPath = in.MountPath
|
||||||
out.Writable = in.Writable
|
out.Writable = in.Writable
|
||||||
out.PathType = v1.HostPathType(in.PathType)
|
out.PathType = core_v1.HostPathType(in.PathType)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,10 +335,7 @@ func autoConvert_v1alpha2_MasterConfiguration_To_kubeadm_MasterConfiguration(in
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
out.KubernetesVersion = in.KubernetesVersion
|
out.KubernetesVersion = in.KubernetesVersion
|
||||||
out.Token = in.Token
|
out.BootstrapTokens = *(*[]kubeadm.BootstrapToken)(unsafe.Pointer(&in.BootstrapTokens))
|
||||||
out.TokenTTL = (*meta_v1.Duration)(unsafe.Pointer(in.TokenTTL))
|
|
||||||
out.TokenUsages = *(*[]string)(unsafe.Pointer(&in.TokenUsages))
|
|
||||||
out.TokenGroups = *(*[]string)(unsafe.Pointer(&in.TokenGroups))
|
|
||||||
out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs))
|
out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs))
|
||||||
out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs))
|
out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs))
|
||||||
out.SchedulerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.SchedulerExtraArgs))
|
out.SchedulerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.SchedulerExtraArgs))
|
||||||
@ -328,10 +379,7 @@ func autoConvert_kubeadm_MasterConfiguration_To_v1alpha2_MasterConfiguration(in
|
|||||||
if err := Convert_kubeadm_NodeRegistrationOptions_To_v1alpha2_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil {
|
if err := Convert_kubeadm_NodeRegistrationOptions_To_v1alpha2_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
out.Token = in.Token
|
out.BootstrapTokens = *(*[]BootstrapToken)(unsafe.Pointer(&in.BootstrapTokens))
|
||||||
out.TokenTTL = (*meta_v1.Duration)(unsafe.Pointer(in.TokenTTL))
|
|
||||||
out.TokenUsages = *(*[]string)(unsafe.Pointer(&in.TokenUsages))
|
|
||||||
out.TokenGroups = *(*[]string)(unsafe.Pointer(&in.TokenGroups))
|
|
||||||
out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs))
|
out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs))
|
||||||
out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs))
|
out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs))
|
||||||
out.SchedulerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.SchedulerExtraArgs))
|
out.SchedulerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.SchedulerExtraArgs))
|
||||||
@ -388,7 +436,7 @@ func autoConvert_v1alpha2_NodeConfiguration_To_kubeadm_NodeConfiguration(in *Nod
|
|||||||
out.DiscoveryFile = in.DiscoveryFile
|
out.DiscoveryFile = in.DiscoveryFile
|
||||||
out.DiscoveryToken = in.DiscoveryToken
|
out.DiscoveryToken = in.DiscoveryToken
|
||||||
out.DiscoveryTokenAPIServers = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenAPIServers))
|
out.DiscoveryTokenAPIServers = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenAPIServers))
|
||||||
out.DiscoveryTimeout = (*meta_v1.Duration)(unsafe.Pointer(in.DiscoveryTimeout))
|
out.DiscoveryTimeout = (*v1.Duration)(unsafe.Pointer(in.DiscoveryTimeout))
|
||||||
out.TLSBootstrapToken = in.TLSBootstrapToken
|
out.TLSBootstrapToken = in.TLSBootstrapToken
|
||||||
out.Token = in.Token
|
out.Token = in.Token
|
||||||
out.ClusterName = in.ClusterName
|
out.ClusterName = in.ClusterName
|
||||||
@ -411,7 +459,7 @@ func autoConvert_kubeadm_NodeConfiguration_To_v1alpha2_NodeConfiguration(in *kub
|
|||||||
out.DiscoveryFile = in.DiscoveryFile
|
out.DiscoveryFile = in.DiscoveryFile
|
||||||
out.DiscoveryToken = in.DiscoveryToken
|
out.DiscoveryToken = in.DiscoveryToken
|
||||||
out.DiscoveryTokenAPIServers = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenAPIServers))
|
out.DiscoveryTokenAPIServers = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenAPIServers))
|
||||||
out.DiscoveryTimeout = (*meta_v1.Duration)(unsafe.Pointer(in.DiscoveryTimeout))
|
out.DiscoveryTimeout = (*v1.Duration)(unsafe.Pointer(in.DiscoveryTimeout))
|
||||||
out.TLSBootstrapToken = in.TLSBootstrapToken
|
out.TLSBootstrapToken = in.TLSBootstrapToken
|
||||||
out.Token = in.Token
|
out.Token = in.Token
|
||||||
out.ClusterName = in.ClusterName
|
out.ClusterName = in.ClusterName
|
||||||
@ -429,7 +477,7 @@ func Convert_kubeadm_NodeConfiguration_To_v1alpha2_NodeConfiguration(in *kubeadm
|
|||||||
func autoConvert_v1alpha2_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOptions(in *NodeRegistrationOptions, out *kubeadm.NodeRegistrationOptions, s conversion.Scope) error {
|
func autoConvert_v1alpha2_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOptions(in *NodeRegistrationOptions, out *kubeadm.NodeRegistrationOptions, s conversion.Scope) error {
|
||||||
out.Name = in.Name
|
out.Name = in.Name
|
||||||
out.CRISocket = in.CRISocket
|
out.CRISocket = in.CRISocket
|
||||||
out.Taints = *(*[]v1.Taint)(unsafe.Pointer(&in.Taints))
|
out.Taints = *(*[]core_v1.Taint)(unsafe.Pointer(&in.Taints))
|
||||||
out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs))
|
out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -442,7 +490,7 @@ func Convert_v1alpha2_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOptions
|
|||||||
func autoConvert_kubeadm_NodeRegistrationOptions_To_v1alpha2_NodeRegistrationOptions(in *kubeadm.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error {
|
func autoConvert_kubeadm_NodeRegistrationOptions_To_v1alpha2_NodeRegistrationOptions(in *kubeadm.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error {
|
||||||
out.Name = in.Name
|
out.Name = in.Name
|
||||||
out.CRISocket = in.CRISocket
|
out.CRISocket = in.CRISocket
|
||||||
out.Taints = *(*[]v1.Taint)(unsafe.Pointer(&in.Taints))
|
out.Taints = *(*[]core_v1.Taint)(unsafe.Pointer(&in.Taints))
|
||||||
out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs))
|
out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -451,25 +499,3 @@ func autoConvert_kubeadm_NodeRegistrationOptions_To_v1alpha2_NodeRegistrationOpt
|
|||||||
func Convert_kubeadm_NodeRegistrationOptions_To_v1alpha2_NodeRegistrationOptions(in *kubeadm.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error {
|
func Convert_kubeadm_NodeRegistrationOptions_To_v1alpha2_NodeRegistrationOptions(in *kubeadm.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error {
|
||||||
return autoConvert_kubeadm_NodeRegistrationOptions_To_v1alpha2_NodeRegistrationOptions(in, out, s)
|
return autoConvert_kubeadm_NodeRegistrationOptions_To_v1alpha2_NodeRegistrationOptions(in, out, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func autoConvert_v1alpha2_TokenDiscovery_To_kubeadm_TokenDiscovery(in *TokenDiscovery, out *kubeadm.TokenDiscovery, s conversion.Scope) error {
|
|
||||||
out.ID = in.ID
|
|
||||||
out.Secret = in.Secret
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert_v1alpha2_TokenDiscovery_To_kubeadm_TokenDiscovery is an autogenerated conversion function.
|
|
||||||
func Convert_v1alpha2_TokenDiscovery_To_kubeadm_TokenDiscovery(in *TokenDiscovery, out *kubeadm.TokenDiscovery, s conversion.Scope) error {
|
|
||||||
return autoConvert_v1alpha2_TokenDiscovery_To_kubeadm_TokenDiscovery(in, out, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func autoConvert_kubeadm_TokenDiscovery_To_v1alpha2_TokenDiscovery(in *kubeadm.TokenDiscovery, out *TokenDiscovery, s conversion.Scope) error {
|
|
||||||
out.ID = in.ID
|
|
||||||
out.Secret = in.Secret
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert_kubeadm_TokenDiscovery_To_v1alpha2_TokenDiscovery is an autogenerated conversion function.
|
|
||||||
func Convert_kubeadm_TokenDiscovery_To_v1alpha2_TokenDiscovery(in *kubeadm.TokenDiscovery, out *TokenDiscovery, s conversion.Scope) error {
|
|
||||||
return autoConvert_kubeadm_TokenDiscovery_To_v1alpha2_TokenDiscovery(in, out, s)
|
|
||||||
}
|
|
||||||
|
@ -69,6 +69,74 @@ func (in *AuditPolicyConfiguration) DeepCopy() *AuditPolicyConfiguration {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *BootstrapToken) DeepCopyInto(out *BootstrapToken) {
|
||||||
|
*out = *in
|
||||||
|
if in.Token != nil {
|
||||||
|
in, out := &in.Token, &out.Token
|
||||||
|
if *in == nil {
|
||||||
|
*out = nil
|
||||||
|
} else {
|
||||||
|
*out = new(BootstrapTokenString)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.TTL != nil {
|
||||||
|
in, out := &in.TTL, &out.TTL
|
||||||
|
if *in == nil {
|
||||||
|
*out = nil
|
||||||
|
} else {
|
||||||
|
*out = new(v1.Duration)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.Expires != nil {
|
||||||
|
in, out := &in.Expires, &out.Expires
|
||||||
|
if *in == nil {
|
||||||
|
*out = nil
|
||||||
|
} else {
|
||||||
|
*out = (*in).DeepCopy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.Usages != nil {
|
||||||
|
in, out := &in.Usages, &out.Usages
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
if in.Groups != nil {
|
||||||
|
in, out := &in.Groups, &out.Groups
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BootstrapToken.
|
||||||
|
func (in *BootstrapToken) DeepCopy() *BootstrapToken {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(BootstrapToken)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *BootstrapTokenString) DeepCopyInto(out *BootstrapTokenString) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BootstrapTokenString.
|
||||||
|
func (in *BootstrapTokenString) DeepCopy() *BootstrapTokenString {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(BootstrapTokenString)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Etcd) DeepCopyInto(out *Etcd) {
|
func (in *Etcd) DeepCopyInto(out *Etcd) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@ -233,25 +301,13 @@ func (in *MasterConfiguration) DeepCopyInto(out *MasterConfiguration) {
|
|||||||
in.KubeletConfiguration.DeepCopyInto(&out.KubeletConfiguration)
|
in.KubeletConfiguration.DeepCopyInto(&out.KubeletConfiguration)
|
||||||
out.Networking = in.Networking
|
out.Networking = in.Networking
|
||||||
in.NodeRegistration.DeepCopyInto(&out.NodeRegistration)
|
in.NodeRegistration.DeepCopyInto(&out.NodeRegistration)
|
||||||
if in.TokenTTL != nil {
|
if in.BootstrapTokens != nil {
|
||||||
in, out := &in.TokenTTL, &out.TokenTTL
|
in, out := &in.BootstrapTokens, &out.BootstrapTokens
|
||||||
if *in == nil {
|
*out = make([]BootstrapToken, len(*in))
|
||||||
*out = nil
|
for i := range *in {
|
||||||
} else {
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
*out = new(v1.Duration)
|
|
||||||
**out = **in
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if in.TokenUsages != nil {
|
|
||||||
in, out := &in.TokenUsages, &out.TokenUsages
|
|
||||||
*out = make([]string, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
if in.TokenGroups != nil {
|
|
||||||
in, out := &in.TokenGroups, &out.TokenGroups
|
|
||||||
*out = make([]string, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
if in.APIServerExtraArgs != nil {
|
if in.APIServerExtraArgs != nil {
|
||||||
in, out := &in.APIServerExtraArgs, &out.APIServerExtraArgs
|
in, out := &in.APIServerExtraArgs, &out.APIServerExtraArgs
|
||||||
*out = make(map[string]string, len(*in))
|
*out = make(map[string]string, len(*in))
|
||||||
@ -419,19 +475,3 @@ func (in *NodeRegistrationOptions) DeepCopy() *NodeRegistrationOptions {
|
|||||||
in.DeepCopyInto(out)
|
in.DeepCopyInto(out)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *TokenDiscovery) DeepCopyInto(out *TokenDiscovery) {
|
|
||||||
*out = *in
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TokenDiscovery.
|
|
||||||
func (in *TokenDiscovery) DeepCopy() *TokenDiscovery {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(TokenDiscovery)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
@ -44,6 +44,10 @@ func SetObjectDefaults_MasterConfiguration(in *MasterConfiguration) {
|
|||||||
v1beta1.SetDefaults_KubeletConfiguration(in.KubeletConfiguration.BaseConfig)
|
v1beta1.SetDefaults_KubeletConfiguration(in.KubeletConfiguration.BaseConfig)
|
||||||
}
|
}
|
||||||
SetDefaults_NodeRegistrationOptions(&in.NodeRegistration)
|
SetDefaults_NodeRegistrationOptions(&in.NodeRegistration)
|
||||||
|
for i := range in.BootstrapTokens {
|
||||||
|
a := &in.BootstrapTokens[i]
|
||||||
|
SetDefaults_BootstrapToken(a)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetObjectDefaults_NodeConfiguration(in *NodeConfiguration) {
|
func SetObjectDefaults_NodeConfiguration(in *NodeConfiguration) {
|
||||||
|
@ -10,7 +10,6 @@ go_library(
|
|||||||
"//cmd/kubeadm/app/constants:go_default_library",
|
"//cmd/kubeadm/app/constants:go_default_library",
|
||||||
"//cmd/kubeadm/app/features:go_default_library",
|
"//cmd/kubeadm/app/features:go_default_library",
|
||||||
"//cmd/kubeadm/app/util:go_default_library",
|
"//cmd/kubeadm/app/util:go_default_library",
|
||||||
"//cmd/kubeadm/app/util/token:go_default_library",
|
|
||||||
"//pkg/apis/core/validation:go_default_library",
|
"//pkg/apis/core/validation:go_default_library",
|
||||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||||
"//pkg/kubelet/apis/kubeletconfig/scheme:go_default_library",
|
"//pkg/kubelet/apis/kubeletconfig/scheme:go_default_library",
|
||||||
|
@ -35,7 +35,6 @@ import (
|
|||||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/features"
|
"k8s.io/kubernetes/cmd/kubeadm/app/features"
|
||||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||||
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
|
|
||||||
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
|
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
|
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
|
||||||
kubeletscheme "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/scheme"
|
kubeletscheme "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/scheme"
|
||||||
@ -55,9 +54,7 @@ func ValidateMasterConfiguration(c *kubeadm.MasterConfiguration) field.ErrorList
|
|||||||
allErrs = append(allErrs, ValidateCertSANs(c.APIServerCertSANs, field.NewPath("apiServerCertSANs"))...)
|
allErrs = append(allErrs, ValidateCertSANs(c.APIServerCertSANs, field.NewPath("apiServerCertSANs"))...)
|
||||||
allErrs = append(allErrs, ValidateAbsolutePath(c.CertificatesDir, field.NewPath("certificatesDir"))...)
|
allErrs = append(allErrs, ValidateAbsolutePath(c.CertificatesDir, field.NewPath("certificatesDir"))...)
|
||||||
allErrs = append(allErrs, ValidateNodeRegistrationOptions(&c.NodeRegistration, field.NewPath("nodeRegistration"))...)
|
allErrs = append(allErrs, ValidateNodeRegistrationOptions(&c.NodeRegistration, field.NewPath("nodeRegistration"))...)
|
||||||
allErrs = append(allErrs, ValidateToken(c.Token, field.NewPath("token"))...)
|
allErrs = append(allErrs, ValidateBootstrapTokens(c.BootstrapTokens, field.NewPath("bootstrapTokens"))...)
|
||||||
allErrs = append(allErrs, ValidateTokenUsages(c.TokenUsages, field.NewPath("tokenUsages"))...)
|
|
||||||
allErrs = append(allErrs, ValidateTokenGroups(c.TokenUsages, c.TokenGroups, field.NewPath("tokenGroups"))...)
|
|
||||||
allErrs = append(allErrs, ValidateFeatureGates(c.FeatureGates, field.NewPath("featureGates"))...)
|
allErrs = append(allErrs, ValidateFeatureGates(c.FeatureGates, field.NewPath("featureGates"))...)
|
||||||
allErrs = append(allErrs, ValidateAPIEndpoint(&c.API, field.NewPath("api"))...)
|
allErrs = append(allErrs, ValidateAPIEndpoint(&c.API, field.NewPath("api"))...)
|
||||||
allErrs = append(allErrs, ValidateProxy(c.KubeProxy.Config, field.NewPath("kubeProxy").Child("config"))...)
|
allErrs = append(allErrs, ValidateProxy(c.KubeProxy.Config, field.NewPath("kubeProxy").Child("config"))...)
|
||||||
@ -181,18 +178,30 @@ func ValidateDiscoveryFile(discoveryFile string, fldPath *field.Path) field.Erro
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateToken validates token
|
// ValidateBootstrapTokens validates a slice of BootstrapToken objects
|
||||||
func ValidateToken(t string, fldPath *field.Path) field.ErrorList {
|
func ValidateBootstrapTokens(bts []kubeadm.BootstrapToken, fldPath *field.Path) field.ErrorList {
|
||||||
|
allErrs := field.ErrorList{}
|
||||||
|
for i, bt := range bts {
|
||||||
|
btPath := fldPath.Child(fmt.Sprintf("%d", i))
|
||||||
|
allErrs = append(allErrs, ValidateToken(bt.Token.String(), btPath.Child("token"))...)
|
||||||
|
allErrs = append(allErrs, ValidateTokenUsages(bt.Usages, btPath.Child("usages"))...)
|
||||||
|
allErrs = append(allErrs, ValidateTokenGroups(bt.Usages, bt.Groups, btPath.Child("groups"))...)
|
||||||
|
|
||||||
|
if bt.Expires != nil && bt.TTL != nil {
|
||||||
|
allErrs = append(allErrs, field.Invalid(btPath, "", "the BootstrapToken .TTL and .Expires fields are mutually exclusive"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateToken validates a Bootstrap Token
|
||||||
|
func ValidateToken(token string, fldPath *field.Path) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
id, secret, err := tokenutil.ParseToken(t)
|
if !bootstraputil.IsValidBootstrapToken(token) {
|
||||||
if err != nil {
|
allErrs = append(allErrs, field.Invalid(fldPath, token, "the bootstrap token is invalid"))
|
||||||
allErrs = append(allErrs, field.Invalid(fldPath, t, err.Error()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(id) == 0 || len(secret) == 0 {
|
|
||||||
allErrs = append(allErrs, field.Invalid(fldPath, t, "token must be of form '[a-z0-9]{6}.[a-z0-9]{16}'"))
|
|
||||||
}
|
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ import (
|
|||||||
utilpointer "k8s.io/kubernetes/pkg/util/pointer"
|
utilpointer "k8s.io/kubernetes/pkg/util/pointer"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestValidateTokenDiscovery(t *testing.T) {
|
func TestValidateToken(t *testing.T) {
|
||||||
var tests = []struct {
|
var tests = []struct {
|
||||||
c *kubeadm.NodeConfiguration
|
c *kubeadm.NodeConfiguration
|
||||||
f *field.Path
|
f *field.Path
|
||||||
@ -51,7 +51,7 @@ func TestValidateTokenDiscovery(t *testing.T) {
|
|||||||
err := ValidateToken(rt.c.Token, rt.f).ToAggregate()
|
err := ValidateToken(rt.c.Token, rt.f).ToAggregate()
|
||||||
if (err == nil) != rt.expected {
|
if (err == nil) != rt.expected {
|
||||||
t.Errorf(
|
t.Errorf(
|
||||||
"failed ValidateTokenDiscovery:\n\texpected: %t\n\t actual: %t",
|
"failed ValidateToken:\n\texpected: %t\n\t actual: %t",
|
||||||
rt.expected,
|
rt.expected,
|
||||||
(err == nil),
|
(err == nil),
|
||||||
)
|
)
|
||||||
@ -434,7 +434,6 @@ func TestValidateMasterConfiguration(t *testing.T) {
|
|||||||
DNSDomain: "cluster.local",
|
DNSDomain: "cluster.local",
|
||||||
},
|
},
|
||||||
CertificatesDir: "/some/other/cert/dir",
|
CertificatesDir: "/some/other/cert/dir",
|
||||||
Token: "abcdef.0123456789abcdef",
|
|
||||||
}, false},
|
}, false},
|
||||||
{"valid master configuration with incorrect IPv4 pod subnet",
|
{"valid master configuration with incorrect IPv4 pod subnet",
|
||||||
&kubeadm.MasterConfiguration{
|
&kubeadm.MasterConfiguration{
|
||||||
@ -448,7 +447,6 @@ func TestValidateMasterConfiguration(t *testing.T) {
|
|||||||
PodSubnet: "10.0.1.15",
|
PodSubnet: "10.0.1.15",
|
||||||
},
|
},
|
||||||
CertificatesDir: "/some/other/cert/dir",
|
CertificatesDir: "/some/other/cert/dir",
|
||||||
Token: "abcdef.0123456789abcdef",
|
|
||||||
NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"},
|
NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"},
|
||||||
}, false},
|
}, false},
|
||||||
{"valid master configuration with IPv4 service subnet",
|
{"valid master configuration with IPv4 service subnet",
|
||||||
@ -494,7 +492,6 @@ func TestValidateMasterConfiguration(t *testing.T) {
|
|||||||
PodSubnet: "10.0.1.15/16",
|
PodSubnet: "10.0.1.15/16",
|
||||||
},
|
},
|
||||||
CertificatesDir: "/some/other/cert/dir",
|
CertificatesDir: "/some/other/cert/dir",
|
||||||
Token: "abcdef.0123456789abcdef",
|
|
||||||
NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"},
|
NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"},
|
||||||
}, true},
|
}, true},
|
||||||
{"valid master configuration using IPv6 service subnet",
|
{"valid master configuration using IPv6 service subnet",
|
||||||
@ -539,7 +536,6 @@ func TestValidateMasterConfiguration(t *testing.T) {
|
|||||||
DNSDomain: "cluster.local",
|
DNSDomain: "cluster.local",
|
||||||
},
|
},
|
||||||
CertificatesDir: "/some/other/cert/dir",
|
CertificatesDir: "/some/other/cert/dir",
|
||||||
Token: "abcdef.0123456789abcdef",
|
|
||||||
NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"},
|
NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"},
|
||||||
}, true},
|
}, true},
|
||||||
}
|
}
|
||||||
|
106
cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go
generated
106
cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go
generated
@ -69,6 +69,74 @@ func (in *AuditPolicyConfiguration) DeepCopy() *AuditPolicyConfiguration {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *BootstrapToken) DeepCopyInto(out *BootstrapToken) {
|
||||||
|
*out = *in
|
||||||
|
if in.Token != nil {
|
||||||
|
in, out := &in.Token, &out.Token
|
||||||
|
if *in == nil {
|
||||||
|
*out = nil
|
||||||
|
} else {
|
||||||
|
*out = new(BootstrapTokenString)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.TTL != nil {
|
||||||
|
in, out := &in.TTL, &out.TTL
|
||||||
|
if *in == nil {
|
||||||
|
*out = nil
|
||||||
|
} else {
|
||||||
|
*out = new(v1.Duration)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.Expires != nil {
|
||||||
|
in, out := &in.Expires, &out.Expires
|
||||||
|
if *in == nil {
|
||||||
|
*out = nil
|
||||||
|
} else {
|
||||||
|
*out = (*in).DeepCopy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.Usages != nil {
|
||||||
|
in, out := &in.Usages, &out.Usages
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
if in.Groups != nil {
|
||||||
|
in, out := &in.Groups, &out.Groups
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BootstrapToken.
|
||||||
|
func (in *BootstrapToken) DeepCopy() *BootstrapToken {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(BootstrapToken)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *BootstrapTokenString) DeepCopyInto(out *BootstrapTokenString) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BootstrapTokenString.
|
||||||
|
func (in *BootstrapTokenString) DeepCopy() *BootstrapTokenString {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(BootstrapTokenString)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Etcd) DeepCopyInto(out *Etcd) {
|
func (in *Etcd) DeepCopyInto(out *Etcd) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@ -233,25 +301,13 @@ func (in *MasterConfiguration) DeepCopyInto(out *MasterConfiguration) {
|
|||||||
in.KubeletConfiguration.DeepCopyInto(&out.KubeletConfiguration)
|
in.KubeletConfiguration.DeepCopyInto(&out.KubeletConfiguration)
|
||||||
out.Networking = in.Networking
|
out.Networking = in.Networking
|
||||||
in.NodeRegistration.DeepCopyInto(&out.NodeRegistration)
|
in.NodeRegistration.DeepCopyInto(&out.NodeRegistration)
|
||||||
if in.TokenTTL != nil {
|
if in.BootstrapTokens != nil {
|
||||||
in, out := &in.TokenTTL, &out.TokenTTL
|
in, out := &in.BootstrapTokens, &out.BootstrapTokens
|
||||||
if *in == nil {
|
*out = make([]BootstrapToken, len(*in))
|
||||||
*out = nil
|
for i := range *in {
|
||||||
} else {
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
*out = new(v1.Duration)
|
|
||||||
**out = **in
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if in.TokenUsages != nil {
|
|
||||||
in, out := &in.TokenUsages, &out.TokenUsages
|
|
||||||
*out = make([]string, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
if in.TokenGroups != nil {
|
|
||||||
in, out := &in.TokenGroups, &out.TokenGroups
|
|
||||||
*out = make([]string, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
if in.APIServerExtraArgs != nil {
|
if in.APIServerExtraArgs != nil {
|
||||||
in, out := &in.APIServerExtraArgs, &out.APIServerExtraArgs
|
in, out := &in.APIServerExtraArgs, &out.APIServerExtraArgs
|
||||||
*out = make(map[string]string, len(*in))
|
*out = make(map[string]string, len(*in))
|
||||||
@ -419,19 +475,3 @@ func (in *NodeRegistrationOptions) DeepCopy() *NodeRegistrationOptions {
|
|||||||
in.DeepCopyInto(out)
|
in.DeepCopyInto(out)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *TokenDiscovery) DeepCopyInto(out *TokenDiscovery) {
|
|
||||||
*out = *in
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TokenDiscovery.
|
|
||||||
func (in *TokenDiscovery) DeepCopy() *TokenDiscovery {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(TokenDiscovery)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
@ -25,6 +25,7 @@ go_library(
|
|||||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
|
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
|
||||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha2:go_default_library",
|
"//cmd/kubeadm/app/apis/kubeadm/v1alpha2:go_default_library",
|
||||||
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
|
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
|
||||||
|
"//cmd/kubeadm/app/cmd/options:go_default_library",
|
||||||
"//cmd/kubeadm/app/cmd/phases:go_default_library",
|
"//cmd/kubeadm/app/cmd/phases:go_default_library",
|
||||||
"//cmd/kubeadm/app/cmd/upgrade:go_default_library",
|
"//cmd/kubeadm/app/cmd/upgrade:go_default_library",
|
||||||
"//cmd/kubeadm/app/cmd/util:go_default_library",
|
"//cmd/kubeadm/app/cmd/util:go_default_library",
|
||||||
@ -51,8 +52,6 @@ go_library(
|
|||||||
"//cmd/kubeadm/app/util/config:go_default_library",
|
"//cmd/kubeadm/app/util/config:go_default_library",
|
||||||
"//cmd/kubeadm/app/util/dryrun:go_default_library",
|
"//cmd/kubeadm/app/util/dryrun:go_default_library",
|
||||||
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
|
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
|
||||||
"//cmd/kubeadm/app/util/token:go_default_library",
|
|
||||||
"//pkg/apis/core:go_default_library",
|
|
||||||
"//pkg/kubectl/util/i18n:go_default_library",
|
"//pkg/kubectl/util/i18n:go_default_library",
|
||||||
"//pkg/util/initsystem:go_default_library",
|
"//pkg/util/initsystem:go_default_library",
|
||||||
"//pkg/version:go_default_library",
|
"//pkg/version:go_default_library",
|
||||||
@ -61,7 +60,6 @@ go_library(
|
|||||||
"//vendor/github.com/renstrom/dedent:go_default_library",
|
"//vendor/github.com/renstrom/dedent:go_default_library",
|
||||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/duration:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/duration:go_default_library",
|
||||||
@ -70,6 +68,7 @@ go_library(
|
|||||||
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/tools/bootstrap/token/api:go_default_library",
|
"//vendor/k8s.io/client-go/tools/bootstrap/token/api:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/tools/bootstrap/token/util:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/util/cert:go_default_library",
|
"//vendor/k8s.io/client-go/util/cert:go_default_library",
|
||||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||||
@ -119,6 +118,7 @@ filegroup(
|
|||||||
name = "all-srcs",
|
name = "all-srcs",
|
||||||
srcs = [
|
srcs = [
|
||||||
":package-srcs",
|
":package-srcs",
|
||||||
|
"//cmd/kubeadm/app/cmd/options:all-srcs",
|
||||||
"//cmd/kubeadm/app/cmd/phases:all-srcs",
|
"//cmd/kubeadm/app/cmd/phases:all-srcs",
|
||||||
"//cmd/kubeadm/app/cmd/upgrade:all-srcs",
|
"//cmd/kubeadm/app/cmd/upgrade:all-srcs",
|
||||||
"//cmd/kubeadm/app/cmd/util:all-srcs",
|
"//cmd/kubeadm/app/cmd/util:all-srcs",
|
||||||
|
@ -48,10 +48,18 @@ const (
|
|||||||
// TODO: Figure out how to get these constants from the API machinery
|
// TODO: Figure out how to get these constants from the API machinery
|
||||||
masterConfig = "MasterConfiguration"
|
masterConfig = "MasterConfiguration"
|
||||||
nodeConfig = "NodeConfiguration"
|
nodeConfig = "NodeConfiguration"
|
||||||
sillyToken = "abcdef.0123456789abcdef"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var availableAPIObjects = []string{masterConfig, nodeConfig}
|
var (
|
||||||
|
availableAPIObjects = []string{masterConfig, nodeConfig}
|
||||||
|
// sillyToken is only set statically to make kubeadm not randomize the token on every run
|
||||||
|
sillyToken = kubeadmapiv1alpha2.BootstrapToken{
|
||||||
|
Token: &kubeadmapiv1alpha2.BootstrapTokenString{
|
||||||
|
ID: "abcdef",
|
||||||
|
Secret: "0123456789abcdef",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// NewCmdConfig returns cobra.Command for "kubeadm config" command
|
// NewCmdConfig returns cobra.Command for "kubeadm config" command
|
||||||
func NewCmdConfig(out io.Writer) *cobra.Command {
|
func NewCmdConfig(out io.Writer) *cobra.Command {
|
||||||
@ -123,7 +131,7 @@ func getDefaultAPIObjectBytes(apiObject string) ([]byte, error) {
|
|||||||
if apiObject == masterConfig {
|
if apiObject == masterConfig {
|
||||||
|
|
||||||
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig("", &kubeadmapiv1alpha2.MasterConfiguration{
|
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig("", &kubeadmapiv1alpha2.MasterConfiguration{
|
||||||
Token: sillyToken,
|
BootstrapTokens: []kubeadmapiv1alpha2.BootstrapToken{sillyToken},
|
||||||
})
|
})
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
@ -131,7 +139,7 @@ func getDefaultAPIObjectBytes(apiObject string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
if apiObject == nodeConfig {
|
if apiObject == nodeConfig {
|
||||||
internalcfg, err := configutil.NodeConfigFileAndDefaultsToInternalConfig("", &kubeadmapiv1alpha2.NodeConfiguration{
|
internalcfg, err := configutil.NodeConfigFileAndDefaultsToInternalConfig("", &kubeadmapiv1alpha2.NodeConfiguration{
|
||||||
Token: sillyToken,
|
Token: sillyToken.Token.String(),
|
||||||
DiscoveryTokenAPIServers: []string{"kube-apiserver:6443"},
|
DiscoveryTokenAPIServers: []string{"kube-apiserver:6443"},
|
||||||
DiscoveryTokenUnsafeSkipCAVerification: true,
|
DiscoveryTokenUnsafeSkipCAVerification: true,
|
||||||
})
|
})
|
||||||
|
@ -37,6 +37,7 @@ import (
|
|||||||
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
||||||
kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
|
kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
|
||||||
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
||||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/features"
|
"k8s.io/kubernetes/cmd/kubeadm/app/features"
|
||||||
@ -123,6 +124,9 @@ func NewCmdInit(out io.Writer) *cobra.Command {
|
|||||||
var dryRun bool
|
var dryRun bool
|
||||||
var featureGatesString string
|
var featureGatesString string
|
||||||
var ignorePreflightErrors []string
|
var ignorePreflightErrors []string
|
||||||
|
// Create the options object for the bootstrap token-related flags, and override the default value for .Description
|
||||||
|
bto := options.NewBootstrapTokenOptions()
|
||||||
|
bto.Description = "The default bootstrap token generated by 'kubeadm init'."
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "init",
|
Use: "init",
|
||||||
@ -142,6 +146,9 @@ func NewCmdInit(out io.Writer) *cobra.Command {
|
|||||||
err = validation.ValidateMixedArguments(cmd.Flags())
|
err = validation.ValidateMixedArguments(cmd.Flags())
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
|
err = bto.ApplyTo(externalcfg)
|
||||||
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
i, err := NewInit(cfgPath, externalcfg, ignorePreflightErrorsSet, skipTokenPrint, dryRun)
|
i, err := NewInit(cfgPath, externalcfg, ignorePreflightErrorsSet, skipTokenPrint, dryRun)
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
kubeadmutil.CheckErr(i.Run(out))
|
kubeadmutil.CheckErr(i.Run(out))
|
||||||
@ -150,6 +157,8 @@ func NewCmdInit(out io.Writer) *cobra.Command {
|
|||||||
|
|
||||||
AddInitConfigFlags(cmd.PersistentFlags(), externalcfg, &featureGatesString)
|
AddInitConfigFlags(cmd.PersistentFlags(), externalcfg, &featureGatesString)
|
||||||
AddInitOtherFlags(cmd.PersistentFlags(), &cfgPath, &skipPreFlight, &skipTokenPrint, &dryRun, &ignorePreflightErrors)
|
AddInitOtherFlags(cmd.PersistentFlags(), &cfgPath, &skipPreFlight, &skipTokenPrint, &dryRun, &ignorePreflightErrors)
|
||||||
|
bto.AddTokenFlag(cmd.PersistentFlags())
|
||||||
|
bto.AddTTLFlag(cmd.PersistentFlags())
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
@ -192,21 +201,12 @@ func AddInitConfigFlags(flagSet *flag.FlagSet, cfg *kubeadmapiv1alpha2.MasterCon
|
|||||||
&cfg.NodeRegistration.Name, "node-name", cfg.NodeRegistration.Name,
|
&cfg.NodeRegistration.Name, "node-name", cfg.NodeRegistration.Name,
|
||||||
`Specify the node name.`,
|
`Specify the node name.`,
|
||||||
)
|
)
|
||||||
flagSet.StringVar(
|
|
||||||
&cfg.Token, "token", cfg.Token,
|
|
||||||
"The token to use for establishing bidirectional trust between nodes and masters. The format is [a-z0-9]{6}\\.[a-z0-9]{16} - e.g. abcdef.0123456789abcdef",
|
|
||||||
)
|
|
||||||
flagSet.DurationVar(
|
|
||||||
&cfg.TokenTTL.Duration, "token-ttl", cfg.TokenTTL.Duration,
|
|
||||||
"The duration before the bootstrap token is automatically deleted. If set to '0', the token will never expire.",
|
|
||||||
)
|
|
||||||
flagSet.StringVar(
|
flagSet.StringVar(
|
||||||
&cfg.NodeRegistration.CRISocket, "cri-socket", cfg.NodeRegistration.CRISocket,
|
&cfg.NodeRegistration.CRISocket, "cri-socket", cfg.NodeRegistration.CRISocket,
|
||||||
`Specify the CRI socket to connect to.`,
|
`Specify the CRI socket to connect to.`,
|
||||||
)
|
)
|
||||||
flagSet.StringVar(featureGatesString, "feature-gates", *featureGatesString, "A set of key=value pairs that describe feature gates for various features. "+
|
flagSet.StringVar(featureGatesString, "feature-gates", *featureGatesString, "A set of key=value pairs that describe feature gates for various features. "+
|
||||||
"Options are:\n"+strings.Join(features.KnownFeatures(&features.InitFeatureGates), "\n"))
|
"Options are:\n"+strings.Join(features.KnownFeatures(&features.InitFeatureGates), "\n"))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddInitOtherFlags adds init flags that are not bound to a configuration file to the given flagset
|
// AddInitOtherFlags adds init flags that are not bound to a configuration file to the given flagset
|
||||||
@ -427,14 +427,21 @@ func (i *Init) Run(out io.Writer) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PHASE 5: Set up the node bootstrap tokens
|
// PHASE 5: Set up the node bootstrap tokens
|
||||||
|
tokens := []string{}
|
||||||
|
for _, bt := range i.cfg.BootstrapTokens {
|
||||||
|
tokens = append(tokens, bt.Token.String())
|
||||||
|
}
|
||||||
if !i.skipTokenPrint {
|
if !i.skipTokenPrint {
|
||||||
glog.Infof("[bootstraptoken] using token: %s\n", i.cfg.Token)
|
if len(tokens) == 1 {
|
||||||
|
glog.Infof("[bootstraptoken] using token: %s\n", tokens[0])
|
||||||
|
} else if len(tokens) > 1 {
|
||||||
|
glog.Infof("[bootstraptoken] using tokens: %v\n", tokens)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the default node bootstrap token
|
// Create the default node bootstrap token
|
||||||
glog.V(1).Infof("[init] creating RBAC rules to generate default bootstrap token")
|
glog.V(1).Infof("[init] creating RBAC rules to generate default bootstrap token")
|
||||||
tokenDescription := "The default bootstrap token generated by 'kubeadm init'."
|
if err := nodebootstraptokenphase.UpdateOrCreateTokens(client, false, i.cfg.BootstrapTokens); err != nil {
|
||||||
if err := nodebootstraptokenphase.UpdateOrCreateToken(client, i.cfg.Token, false, i.cfg.TokenTTL.Duration, i.cfg.TokenUsages, i.cfg.TokenGroups, tokenDescription); err != nil {
|
|
||||||
return fmt.Errorf("error updating or creating token: %v", err)
|
return fmt.Errorf("error updating or creating token: %v", err)
|
||||||
}
|
}
|
||||||
// Create RBAC rules that makes the bootstrap tokens able to post CSRs
|
// Create RBAC rules that makes the bootstrap tokens able to post CSRs
|
||||||
@ -491,10 +498,19 @@ func (i *Init) Run(out io.Writer) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the join command
|
// Prints the join command, multiple times in case the user has multiple tokens
|
||||||
joinCommand, err := cmdutil.GetJoinCommand(kubeadmconstants.GetAdminKubeConfigPath(), i.cfg.Token, i.skipTokenPrint)
|
for _, token := range tokens {
|
||||||
|
if err := printJoinCommand(out, adminKubeConfigPath, token, i.skipTokenPrint); err != nil {
|
||||||
|
return fmt.Errorf("failed to print join command: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func printJoinCommand(out io.Writer, adminKubeConfigPath, token string, skipTokenPrint bool) error {
|
||||||
|
joinCommand, err := cmdutil.GetJoinCommand(adminKubeConfigPath, token, skipTokenPrint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get join command: %v", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := map[string]string{
|
ctx := map[string]string{
|
||||||
|
28
cmd/kubeadm/app/cmd/options/BUILD
Normal file
28
cmd/kubeadm/app/cmd/options/BUILD
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["token.go"],
|
||||||
|
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/cmd/options",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//cmd/kubeadm/app/apis/kubeadm/v1alpha2:go_default_library",
|
||||||
|
"//cmd/kubeadm/app/constants:go_default_library",
|
||||||
|
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/tools/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"],
|
||||||
|
)
|
99
cmd/kubeadm/app/cmd/options/token.go
Normal file
99
cmd/kubeadm/app/cmd/options/token.go
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
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 options
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
|
bootstrapapi "k8s.io/client-go/tools/bootstrap/token/api"
|
||||||
|
kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
|
||||||
|
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewBootstrapTokenOptions creates a new BootstrapTokenOptions object with the default values
|
||||||
|
func NewBootstrapTokenOptions() *BootstrapTokenOptions {
|
||||||
|
bto := &BootstrapTokenOptions{&kubeadmapiv1alpha2.BootstrapToken{}, ""}
|
||||||
|
kubeadmapiv1alpha2.SetDefaults_BootstrapToken(bto.BootstrapToken)
|
||||||
|
return bto
|
||||||
|
}
|
||||||
|
|
||||||
|
// BootstrapTokenOptions is a wrapper struct for adding bootstrap token-related flags to a FlagSet
|
||||||
|
// and applying the parsed flags to a MasterConfiguration object later at runtime
|
||||||
|
// TODO: In the future, we might want to group the flags in a better way than adding them all individually like this
|
||||||
|
type BootstrapTokenOptions struct {
|
||||||
|
*kubeadmapiv1alpha2.BootstrapToken
|
||||||
|
TokenStr string
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTokenFlag adds the --token flag to the given flagset
|
||||||
|
func (bto *BootstrapTokenOptions) AddTokenFlag(fs *pflag.FlagSet) {
|
||||||
|
fs.StringVar(
|
||||||
|
&bto.TokenStr, "token", "",
|
||||||
|
"The token to use for establishing bidirectional trust between nodes and masters. The format is [a-z0-9]{6}\\.[a-z0-9]{16} - e.g. abcdef.0123456789abcdef",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTTLFlag adds the --token-ttl flag to the given flagset
|
||||||
|
func (bto *BootstrapTokenOptions) AddTTLFlag(fs *pflag.FlagSet) {
|
||||||
|
fs.DurationVar(
|
||||||
|
&bto.TTL.Duration, "token-ttl", bto.TTL.Duration,
|
||||||
|
"The duration before the token is automatically deleted (e.g. 1s, 2m, 3h). If set to '0', the token will never expire",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddUsagesFlag adds the --usages flag to the given flagset
|
||||||
|
func (bto *BootstrapTokenOptions) AddUsagesFlag(fs *pflag.FlagSet) {
|
||||||
|
fs.StringSliceVar(
|
||||||
|
&bto.Usages, "usages", bto.Usages,
|
||||||
|
fmt.Sprintf("Describes the ways in which this token can be used. You can pass --usages multiple times or provide a comma separated list of options. Valid options: [%s]", strings.Join(kubeadmconstants.DefaultTokenUsages, ",")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddGroupsFlag adds the --groups flag to the given flagset
|
||||||
|
func (bto *BootstrapTokenOptions) AddGroupsFlag(fs *pflag.FlagSet) {
|
||||||
|
fs.StringSliceVar(
|
||||||
|
&bto.Groups, "groups", bto.Groups,
|
||||||
|
fmt.Sprintf("Extra groups that this token will authenticate as when used for authentication. Must match %q", bootstrapapi.BootstrapGroupPattern),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddDescriptionFlag adds the --description flag to the given flagset
|
||||||
|
func (bto *BootstrapTokenOptions) AddDescriptionFlag(fs *pflag.FlagSet) {
|
||||||
|
fs.StringVar(
|
||||||
|
&bto.Description, "description", bto.Description,
|
||||||
|
"A human friendly description of how this token is used.",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyTo applies the values set internally in the BootstrapTokenOptions object to a MasterConfiguration object at runtime
|
||||||
|
// If --token was specified in the CLI (as a string), it's parsed and validated before it's added to the BootstrapToken object.
|
||||||
|
func (bto *BootstrapTokenOptions) ApplyTo(cfg *kubeadmapiv1alpha2.MasterConfiguration) error {
|
||||||
|
if len(bto.TokenStr) > 0 {
|
||||||
|
var err error
|
||||||
|
bto.Token, err = kubeadmapiv1alpha2.NewBootstrapTokenString(bto.TokenStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the token specified by the flags as the first and only token to create in case --config is not specified
|
||||||
|
cfg.BootstrapTokens = []kubeadmapiv1alpha2.BootstrapToken{*bto.BootstrapToken}
|
||||||
|
return nil
|
||||||
|
}
|
@ -24,6 +24,7 @@ go_library(
|
|||||||
"//cmd/kubeadm/app/apis/kubeadm/scheme:go_default_library",
|
"//cmd/kubeadm/app/apis/kubeadm/scheme:go_default_library",
|
||||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha2:go_default_library",
|
"//cmd/kubeadm/app/apis/kubeadm/v1alpha2:go_default_library",
|
||||||
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
|
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
|
||||||
|
"//cmd/kubeadm/app/cmd/options:go_default_library",
|
||||||
"//cmd/kubeadm/app/cmd/util:go_default_library",
|
"//cmd/kubeadm/app/cmd/util:go_default_library",
|
||||||
"//cmd/kubeadm/app/constants:go_default_library",
|
"//cmd/kubeadm/app/constants:go_default_library",
|
||||||
"//cmd/kubeadm/app/features:go_default_library",
|
"//cmd/kubeadm/app/features:go_default_library",
|
||||||
|
@ -18,7 +18,6 @@ package phases
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -30,8 +29,8 @@ import (
|
|||||||
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
||||||
kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
|
kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
|
||||||
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
||||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/clusterinfo"
|
"k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/clusterinfo"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node"
|
"k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node"
|
||||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||||
@ -111,14 +110,15 @@ func NewSubCmdBootstrapTokenAll(kubeConfigFile *string) *cobra.Command {
|
|||||||
cfg := &kubeadmapiv1alpha2.MasterConfiguration{
|
cfg := &kubeadmapiv1alpha2.MasterConfiguration{
|
||||||
// KubernetesVersion is not used by bootstrap-token, but we set this explicitly to avoid
|
// KubernetesVersion is not used by bootstrap-token, but we set this explicitly to avoid
|
||||||
// the lookup of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig
|
// the lookup of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig
|
||||||
KubernetesVersion: "v1.9.0",
|
KubernetesVersion: "v1.10.0",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default values for the cobra help text
|
// Default values for the cobra help text
|
||||||
kubeadmscheme.Scheme.Default(cfg)
|
kubeadmscheme.Scheme.Default(cfg)
|
||||||
|
|
||||||
var cfgPath, description string
|
var cfgPath string
|
||||||
var skipTokenPrint bool
|
var skipTokenPrint bool
|
||||||
|
bto := options.NewBootstrapTokenOptions()
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "all",
|
Use: "all",
|
||||||
@ -129,11 +129,14 @@ func NewSubCmdBootstrapTokenAll(kubeConfigFile *string) *cobra.Command {
|
|||||||
err := validation.ValidateMixedArguments(cmd.Flags())
|
err := validation.ValidateMixedArguments(cmd.Flags())
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
|
err = bto.ApplyTo(cfg)
|
||||||
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
|
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
// Creates the bootstap token
|
// Creates the bootstap token
|
||||||
err = createBootstrapToken(*kubeConfigFile, client, cfgPath, cfg, description, skipTokenPrint)
|
err = createBootstrapToken(*kubeConfigFile, client, cfgPath, cfg, skipTokenPrint)
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
// Create the cluster-info ConfigMap or update if it already exists
|
// Create the cluster-info ConfigMap or update if it already exists
|
||||||
@ -159,7 +162,12 @@ func NewSubCmdBootstrapTokenAll(kubeConfigFile *string) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Adds flags to the command
|
// Adds flags to the command
|
||||||
addBootstrapTokenFlags(cmd.Flags(), cfg, &cfgPath, &description, &skipTokenPrint)
|
addGenericFlags(cmd.Flags(), &cfgPath, &skipTokenPrint)
|
||||||
|
bto.AddTokenFlag(cmd.Flags())
|
||||||
|
bto.AddTTLFlag(cmd.Flags())
|
||||||
|
bto.AddUsagesFlag(cmd.Flags())
|
||||||
|
bto.AddGroupsFlag(cmd.Flags())
|
||||||
|
bto.AddDescriptionFlag(cmd.Flags())
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
@ -169,14 +177,15 @@ func NewSubCmdBootstrapToken(kubeConfigFile *string) *cobra.Command {
|
|||||||
cfg := &kubeadmapiv1alpha2.MasterConfiguration{
|
cfg := &kubeadmapiv1alpha2.MasterConfiguration{
|
||||||
// KubernetesVersion is not used by bootstrap-token, but we set this explicitly to avoid
|
// KubernetesVersion is not used by bootstrap-token, but we set this explicitly to avoid
|
||||||
// the lookup of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig
|
// the lookup of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig
|
||||||
KubernetesVersion: "v1.9.0",
|
KubernetesVersion: "v1.10.0",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default values for the cobra help text
|
// Default values for the cobra help text
|
||||||
kubeadmscheme.Scheme.Default(cfg)
|
kubeadmscheme.Scheme.Default(cfg)
|
||||||
|
|
||||||
var cfgPath, description string
|
var cfgPath string
|
||||||
var skipTokenPrint bool
|
var skipTokenPrint bool
|
||||||
|
bto := options.NewBootstrapTokenOptions()
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "create",
|
Use: "create",
|
||||||
@ -186,16 +195,24 @@ func NewSubCmdBootstrapToken(kubeConfigFile *string) *cobra.Command {
|
|||||||
err := validation.ValidateMixedArguments(cmd.Flags())
|
err := validation.ValidateMixedArguments(cmd.Flags())
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
|
err = bto.ApplyTo(cfg)
|
||||||
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
|
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
err = createBootstrapToken(*kubeConfigFile, client, cfgPath, cfg, description, skipTokenPrint)
|
err = createBootstrapToken(*kubeConfigFile, client, cfgPath, cfg, skipTokenPrint)
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds flags to the command
|
// Adds flags to the command
|
||||||
addBootstrapTokenFlags(cmd.Flags(), cfg, &cfgPath, &description, &skipTokenPrint)
|
addGenericFlags(cmd.Flags(), &cfgPath, &skipTokenPrint)
|
||||||
|
bto.AddTokenFlag(cmd.Flags())
|
||||||
|
bto.AddTTLFlag(cmd.Flags())
|
||||||
|
bto.AddUsagesFlag(cmd.Flags())
|
||||||
|
bto.AddGroupsFlag(cmd.Flags())
|
||||||
|
bto.AddDescriptionFlag(cmd.Flags())
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
@ -278,38 +295,18 @@ func NewSubCmdNodeBootstrapTokenAutoApprove(kubeConfigFile *string) *cobra.Comma
|
|||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func addBootstrapTokenFlags(flagSet *pflag.FlagSet, cfg *kubeadmapiv1alpha2.MasterConfiguration, cfgPath, description *string, skipTokenPrint *bool) {
|
func addGenericFlags(flagSet *pflag.FlagSet, cfgPath *string, skipTokenPrint *bool) {
|
||||||
flagSet.StringVar(
|
flagSet.StringVar(
|
||||||
cfgPath, "config", *cfgPath,
|
cfgPath, "config", *cfgPath,
|
||||||
"Path to kubeadm config file. WARNING: Usage of a configuration file is experimental",
|
"Path to kubeadm config file. WARNING: Usage of a configuration file is experimental",
|
||||||
)
|
)
|
||||||
flagSet.StringVar(
|
|
||||||
&cfg.Token, "token", cfg.Token,
|
|
||||||
"The token to use for establishing bidirectional trust between nodes and masters",
|
|
||||||
)
|
|
||||||
flagSet.DurationVar(
|
|
||||||
&cfg.TokenTTL.Duration, "ttl", cfg.TokenTTL.Duration,
|
|
||||||
"The duration before the token is automatically deleted (e.g. 1s, 2m, 3h). If set to '0', the token will never expire",
|
|
||||||
)
|
|
||||||
flagSet.StringSliceVar(
|
|
||||||
&cfg.TokenUsages, "usages", cfg.TokenUsages,
|
|
||||||
fmt.Sprintf("Describes the ways in which this token can be used. You can pass --usages multiple times or provide a comma separated list of options. Valid options: [%s]", strings.Join(kubeadmconstants.DefaultTokenUsages, ",")),
|
|
||||||
)
|
|
||||||
flagSet.StringSliceVar(
|
|
||||||
&cfg.TokenGroups, "groups", cfg.TokenGroups,
|
|
||||||
fmt.Sprintf("Extra groups that this token will authenticate as when used for authentication. Must match %q", bootstrapapi.BootstrapGroupPattern),
|
|
||||||
)
|
|
||||||
flagSet.StringVar(
|
|
||||||
description, "description", "The default bootstrap token generated by 'kubeadm init'.",
|
|
||||||
"A human friendly description of how this token is used.",
|
|
||||||
)
|
|
||||||
flagSet.BoolVar(
|
flagSet.BoolVar(
|
||||||
skipTokenPrint, "skip-token-print", *skipTokenPrint,
|
skipTokenPrint, "skip-token-print", *skipTokenPrint,
|
||||||
"Skip printing of the bootstrap token",
|
"Skip printing of the bootstrap token",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createBootstrapToken(kubeConfigFile string, client clientset.Interface, cfgPath string, cfg *kubeadmapiv1alpha2.MasterConfiguration, description string, skipTokenPrint bool) error {
|
func createBootstrapToken(kubeConfigFile string, client clientset.Interface, cfgPath string, cfg *kubeadmapiv1alpha2.MasterConfiguration, skipTokenPrint bool) error {
|
||||||
|
|
||||||
// This call returns the ready-to-use configuration based on the configuration file that might or might not exist and the default cfg populated by flags
|
// This call returns the ready-to-use configuration based on the configuration file that might or might not exist and the default cfg populated by flags
|
||||||
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, cfg)
|
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, cfg)
|
||||||
@ -319,18 +316,19 @@ func createBootstrapToken(kubeConfigFile string, client clientset.Interface, cfg
|
|||||||
|
|
||||||
glog.V(1).Infoln("[bootstraptoken] creating/updating token")
|
glog.V(1).Infoln("[bootstraptoken] creating/updating token")
|
||||||
// Creates or updates the token
|
// Creates or updates the token
|
||||||
if err := node.UpdateOrCreateToken(client, internalcfg.Token, false, internalcfg.TokenTTL.Duration, internalcfg.TokenUsages, internalcfg.TokenGroups, description); err != nil {
|
if err := node.UpdateOrCreateTokens(client, false, internalcfg.BootstrapTokens); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
glog.Infoln("[bootstraptoken] bootstrap token created")
|
glog.Infoln("[bootstraptoken] bootstrap token created")
|
||||||
glog.Infoln("[bootstraptoken] you can now join any number of machines by running:")
|
glog.Infoln("[bootstraptoken] you can now join any number of machines by running:")
|
||||||
|
|
||||||
joinCommand, err := cmdutil.GetJoinCommand(kubeConfigFile, internalcfg.Token, skipTokenPrint)
|
if len(internalcfg.BootstrapTokens) > 0 {
|
||||||
if err != nil {
|
joinCommand, err := cmdutil.GetJoinCommand(kubeConfigFile, internalcfg.BootstrapTokens[0].Token.String(), skipTokenPrint)
|
||||||
return fmt.Errorf("failed to get join command: %v", err)
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get join command: %v", err)
|
||||||
|
}
|
||||||
|
glog.Infoln(joinCommand)
|
||||||
}
|
}
|
||||||
glog.Infoln(joinCommand)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ func NewCmdMarkMaster() *cobra.Command {
|
|||||||
cfg := &kubeadmapiv1alpha2.MasterConfiguration{
|
cfg := &kubeadmapiv1alpha2.MasterConfiguration{
|
||||||
// KubernetesVersion is not used by mark master, but we set this explicitly to avoid
|
// KubernetesVersion is not used by mark master, but we set this explicitly to avoid
|
||||||
// the lookup of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig
|
// the lookup of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig
|
||||||
KubernetesVersion: "v1.9.0",
|
KubernetesVersion: "v1.10.0",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default values for the cobra help text
|
// Default values for the cobra help text
|
||||||
|
@ -20,7 +20,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
"time"
|
"time"
|
||||||
@ -29,26 +28,24 @@ import (
|
|||||||
"github.com/renstrom/dedent"
|
"github.com/renstrom/dedent"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
"k8s.io/apimachinery/pkg/util/duration"
|
"k8s.io/apimachinery/pkg/util/duration"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
bootstrapapi "k8s.io/client-go/tools/bootstrap/token/api"
|
bootstrapapi "k8s.io/client-go/tools/bootstrap/token/api"
|
||||||
|
bootstraputil "k8s.io/client-go/tools/bootstrap/token/util"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
||||||
kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
|
kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
|
||||||
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
||||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
|
||||||
tokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node"
|
tokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node"
|
||||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
|
||||||
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
|
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
|
||||||
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
|
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
|
||||||
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
|
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultKubeConfig = "/etc/kubernetes/admin.conf"
|
const defaultKubeConfig = "/etc/kubernetes/admin.conf"
|
||||||
@ -95,14 +92,16 @@ func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
|
|||||||
cfg := &kubeadmapiv1alpha2.MasterConfiguration{
|
cfg := &kubeadmapiv1alpha2.MasterConfiguration{
|
||||||
// KubernetesVersion is not used by bootstrap-token, but we set this explicitly to avoid
|
// KubernetesVersion is not used by bootstrap-token, but we set this explicitly to avoid
|
||||||
// the lookup of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig
|
// the lookup of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig
|
||||||
KubernetesVersion: "v1.9.0",
|
KubernetesVersion: "v1.10.0",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default values for the cobra help text
|
// Default values for the cobra help text
|
||||||
kubeadmscheme.Scheme.Default(cfg)
|
kubeadmscheme.Scheme.Default(cfg)
|
||||||
|
|
||||||
var cfgPath, description string
|
var cfgPath string
|
||||||
var printJoinCommand bool
|
var printJoinCommand bool
|
||||||
|
bto := options.NewBootstrapTokenOptions()
|
||||||
|
|
||||||
createCmd := &cobra.Command{
|
createCmd := &cobra.Command{
|
||||||
Use: "create [token]",
|
Use: "create [token]",
|
||||||
DisableFlagsInUseLine: true,
|
DisableFlagsInUseLine: true,
|
||||||
@ -116,37 +115,35 @@ func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
|
|||||||
If no [token] is given, kubeadm will generate a random token instead.
|
If no [token] is given, kubeadm will generate a random token instead.
|
||||||
`),
|
`),
|
||||||
Run: func(tokenCmd *cobra.Command, args []string) {
|
Run: func(tokenCmd *cobra.Command, args []string) {
|
||||||
if len(args) != 0 {
|
if len(args) > 0 {
|
||||||
cfg.Token = args[0]
|
bto.TokenStr = args[0]
|
||||||
}
|
}
|
||||||
glog.V(1).Infoln("[token] validating mixed arguments")
|
glog.V(1).Infoln("[token] validating mixed arguments")
|
||||||
err := validation.ValidateMixedArguments(tokenCmd.Flags())
|
err := validation.ValidateMixedArguments(tokenCmd.Flags())
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
|
err = bto.ApplyTo(cfg)
|
||||||
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
glog.V(1).Infoln("[token] getting Clientsets from KubeConfig file")
|
glog.V(1).Infoln("[token] getting Clientsets from KubeConfig file")
|
||||||
kubeConfigFile = findExistingKubeConfig(kubeConfigFile)
|
kubeConfigFile = findExistingKubeConfig(kubeConfigFile)
|
||||||
client, err := getClientset(kubeConfigFile, dryRun)
|
client, err := getClientset(kubeConfigFile, dryRun)
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
err = RunCreateToken(out, client, cfgPath, cfg, description, printJoinCommand, kubeConfigFile)
|
err = RunCreateToken(out, client, cfgPath, cfg, printJoinCommand, kubeConfigFile)
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
createCmd.Flags().StringVar(&cfgPath,
|
createCmd.Flags().StringVar(&cfgPath,
|
||||||
"config", cfgPath, "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)")
|
"config", cfgPath, "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)")
|
||||||
createCmd.Flags().DurationVar(&cfg.TokenTTL.Duration,
|
|
||||||
"ttl", cfg.TokenTTL.Duration, "The duration before the token is automatically deleted (e.g. 1s, 2m, 3h). If set to '0', the token will never expire.")
|
|
||||||
createCmd.Flags().StringSliceVar(&cfg.TokenUsages,
|
|
||||||
"usages", cfg.TokenUsages, fmt.Sprintf("Describes the ways in which this token can be used. You can pass --usages multiple times or provide a comma separated list of options. Valid options: [%s].", strings.Join(kubeadmconstants.DefaultTokenUsages, ",")))
|
|
||||||
createCmd.Flags().StringSliceVar(&cfg.TokenGroups,
|
|
||||||
"groups", cfg.TokenGroups,
|
|
||||||
fmt.Sprintf("Extra groups that this token will authenticate as when used for authentication. Must match %q.", bootstrapapi.BootstrapGroupPattern))
|
|
||||||
createCmd.Flags().StringVar(&description,
|
|
||||||
"description", "", "A human friendly description of how this token is used.")
|
|
||||||
createCmd.Flags().BoolVar(&printJoinCommand,
|
createCmd.Flags().BoolVar(&printJoinCommand,
|
||||||
"print-join-command", false, "Instead of printing only the token, print the full 'kubeadm join' flag needed to join the cluster using the token.")
|
"print-join-command", false, "Instead of printing only the token, print the full 'kubeadm join' flag needed to join the cluster using the token.")
|
||||||
tokenCmd.AddCommand(createCmd)
|
bto.AddTTLFlag(createCmd.Flags())
|
||||||
|
bto.AddUsagesFlag(createCmd.Flags())
|
||||||
|
bto.AddGroupsFlag(createCmd.Flags())
|
||||||
|
bto.AddDescriptionFlag(createCmd.Flags())
|
||||||
|
|
||||||
|
tokenCmd.AddCommand(createCmd)
|
||||||
tokenCmd.AddCommand(NewCmdTokenGenerate(out))
|
tokenCmd.AddCommand(NewCmdTokenGenerate(out))
|
||||||
|
|
||||||
listCmd := &cobra.Command{
|
listCmd := &cobra.Command{
|
||||||
@ -178,7 +175,7 @@ func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
|
|||||||
`),
|
`),
|
||||||
Run: func(tokenCmd *cobra.Command, args []string) {
|
Run: func(tokenCmd *cobra.Command, args []string) {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
kubeadmutil.CheckErr(fmt.Errorf("missing subcommand; 'token delete' is missing token of form [%q]", tokenutil.TokenIDRegexpString))
|
kubeadmutil.CheckErr(fmt.Errorf("missing subcommand; 'token delete' is missing token of form %q", bootstrapapi.BootstrapTokenIDPattern))
|
||||||
}
|
}
|
||||||
kubeConfigFile = findExistingKubeConfig(kubeConfigFile)
|
kubeConfigFile = findExistingKubeConfig(kubeConfigFile)
|
||||||
client, err := getClientset(kubeConfigFile, dryRun)
|
client, err := getClientset(kubeConfigFile, dryRun)
|
||||||
@ -217,7 +214,7 @@ func NewCmdTokenGenerate(out io.Writer) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RunCreateToken generates a new bootstrap token and stores it as a secret on the server.
|
// RunCreateToken generates a new bootstrap token and stores it as a secret on the server.
|
||||||
func RunCreateToken(out io.Writer, client clientset.Interface, cfgPath string, cfg *kubeadmapiv1alpha2.MasterConfiguration, description string, printJoinCommand bool, kubeConfigFile string) error {
|
func RunCreateToken(out io.Writer, client clientset.Interface, cfgPath string, cfg *kubeadmapiv1alpha2.MasterConfiguration, printJoinCommand bool, kubeConfigFile string) error {
|
||||||
// This call returns the ready-to-use configuration based on the configuration file that might or might not exist and the default cfg populated by flags
|
// This call returns the ready-to-use configuration based on the configuration file that might or might not exist and the default cfg populated by flags
|
||||||
glog.V(1).Infoln("[token] loading configurations")
|
glog.V(1).Infoln("[token] loading configurations")
|
||||||
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, cfg)
|
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, cfg)
|
||||||
@ -226,21 +223,20 @@ func RunCreateToken(out io.Writer, client clientset.Interface, cfgPath string, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
glog.V(1).Infoln("[token] creating token")
|
glog.V(1).Infoln("[token] creating token")
|
||||||
err = tokenphase.CreateNewToken(client, internalcfg.Token, internalcfg.TokenTTL.Duration, internalcfg.TokenUsages, internalcfg.TokenGroups, description)
|
if err := tokenphase.CreateNewTokens(client, internalcfg.BootstrapTokens); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// if --print-join-command was specified, print the full `kubeadm join` command
|
// if --print-join-command was specified, print the full `kubeadm join` command
|
||||||
// otherwise, just print the token
|
// otherwise, just print the token
|
||||||
if printJoinCommand {
|
if printJoinCommand {
|
||||||
joinCommand, err := cmdutil.GetJoinCommand(kubeConfigFile, internalcfg.Token, false)
|
joinCommand, err := cmdutil.GetJoinCommand(kubeConfigFile, internalcfg.BootstrapTokens[0].Token.String(), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get join command: %v", err)
|
return fmt.Errorf("failed to get join command: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Fprintln(out, joinCommand)
|
fmt.Fprintln(out, joinCommand)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintln(out, internalcfg.Token)
|
fmt.Fprintln(out, internalcfg.BootstrapTokens[0].Token.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -248,8 +244,8 @@ func RunCreateToken(out io.Writer, client clientset.Interface, cfgPath string, c
|
|||||||
|
|
||||||
// RunGenerateToken just generates a random token for the user
|
// RunGenerateToken just generates a random token for the user
|
||||||
func RunGenerateToken(out io.Writer) error {
|
func RunGenerateToken(out io.Writer) error {
|
||||||
glog.V(1).Infoln("[token] generating randodm token")
|
glog.V(1).Infoln("[token] generating random token")
|
||||||
token, err := tokenutil.GenerateToken()
|
token, err := bootstraputil.GenerateBootstrapToken()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -264,7 +260,10 @@ func RunListTokens(out io.Writer, errW io.Writer, client clientset.Interface) er
|
|||||||
glog.V(1).Infoln("[token] preparing selector for bootstrap token")
|
glog.V(1).Infoln("[token] preparing selector for bootstrap token")
|
||||||
tokenSelector := fields.SelectorFromSet(
|
tokenSelector := fields.SelectorFromSet(
|
||||||
map[string]string{
|
map[string]string{
|
||||||
api.SecretTypeField: string(bootstrapapi.SecretTypeBootstrapToken),
|
// TODO: We hard-code "type" here until `field_constants.go` that is
|
||||||
|
// currently in `pkg/apis/core/` exists in the external API, i.e.
|
||||||
|
// k8s.io/api/v1. Should be v1.SecretTypeField
|
||||||
|
"type": string(bootstrapapi.SecretTypeBootstrapToken),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
listOptions := metav1.ListOptions{
|
listOptions := metav1.ListOptions{
|
||||||
@ -280,68 +279,17 @@ func RunListTokens(out io.Writer, errW io.Writer, client clientset.Interface) er
|
|||||||
w := tabwriter.NewWriter(out, 10, 4, 3, ' ', 0)
|
w := tabwriter.NewWriter(out, 10, 4, 3, ' ', 0)
|
||||||
fmt.Fprintln(w, "TOKEN\tTTL\tEXPIRES\tUSAGES\tDESCRIPTION\tEXTRA GROUPS")
|
fmt.Fprintln(w, "TOKEN\tTTL\tEXPIRES\tUSAGES\tDESCRIPTION\tEXTRA GROUPS")
|
||||||
for _, secret := range secrets.Items {
|
for _, secret := range secrets.Items {
|
||||||
tokenID := getSecretString(&secret, bootstrapapi.BootstrapTokenIDKey)
|
|
||||||
if len(tokenID) == 0 {
|
// Get the BootstrapToken struct representation from the Secret object
|
||||||
fmt.Fprintf(errW, "bootstrap token has no token-id data: %s\n", secret.Name)
|
token, err := kubeadmapi.BootstrapTokenFromSecret(&secret)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(errW, "%v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// enforce the right naming convention
|
// Get the human-friendly string representation for the token
|
||||||
if secret.Name != fmt.Sprintf("%s%s", bootstrapapi.BootstrapTokenSecretPrefix, tokenID) {
|
humanFriendlyTokenOutput := humanReadableBootstrapToken(token)
|
||||||
fmt.Fprintf(errW, "bootstrap token name is not of the form '%s(token-id)': %s\n", bootstrapapi.BootstrapTokenSecretPrefix, secret.Name)
|
fmt.Fprintln(w, humanFriendlyTokenOutput)
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenSecret := getSecretString(&secret, bootstrapapi.BootstrapTokenSecretKey)
|
|
||||||
if len(tokenSecret) == 0 {
|
|
||||||
fmt.Fprintf(errW, "bootstrap token has no token-secret data: %s\n", secret.Name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
td := &kubeadmapi.TokenDiscovery{ID: tokenID, Secret: tokenSecret}
|
|
||||||
|
|
||||||
// Expiration time is optional, if not specified this implies the token
|
|
||||||
// never expires.
|
|
||||||
ttl := "<forever>"
|
|
||||||
expires := "<never>"
|
|
||||||
secretExpiration := getSecretString(&secret, bootstrapapi.BootstrapTokenExpirationKey)
|
|
||||||
if len(secretExpiration) > 0 {
|
|
||||||
expireTime, err := time.Parse(time.RFC3339, secretExpiration)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(errW, "can't parse expiration time of bootstrap token %s\n", secret.Name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ttl = duration.ShortHumanDuration(expireTime.Sub(time.Now()))
|
|
||||||
expires = expireTime.Format(time.RFC3339)
|
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
sort.Strings(usages)
|
|
||||||
usageString := strings.Join(usages, ",")
|
|
||||||
if len(usageString) == 0 {
|
|
||||||
usageString = "<none>"
|
|
||||||
}
|
|
||||||
|
|
||||||
description := getSecretString(&secret, bootstrapapi.BootstrapTokenDescriptionKey)
|
|
||||||
if len(description) == 0 {
|
|
||||||
description = "<none>"
|
|
||||||
}
|
|
||||||
|
|
||||||
groups := getSecretString(&secret, bootstrapapi.BootstrapTokenExtraGroupsKey)
|
|
||||||
if len(groups) == 0 {
|
|
||||||
groups = "<none>"
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", tokenutil.BearerToken(td), ttl, expires, usageString, description, groups)
|
|
||||||
}
|
}
|
||||||
w.Flush()
|
w.Flush()
|
||||||
return nil
|
return nil
|
||||||
@ -352,13 +300,16 @@ func RunDeleteToken(out io.Writer, client clientset.Interface, tokenIDOrToken st
|
|||||||
// Assume the given first argument is a token id and try to parse it
|
// Assume the given first argument is a token id and try to parse it
|
||||||
tokenID := tokenIDOrToken
|
tokenID := tokenIDOrToken
|
||||||
glog.V(1).Infoln("[token] parsing token ID")
|
glog.V(1).Infoln("[token] parsing token ID")
|
||||||
if err := tokenutil.ParseTokenID(tokenIDOrToken); err != nil {
|
if !bootstraputil.IsValidBootstrapTokenID(tokenIDOrToken) {
|
||||||
if tokenID, _, err = tokenutil.ParseToken(tokenIDOrToken); err != nil {
|
// Okay, the full token with both id and secret was probably passed. Parse it and extract the ID only
|
||||||
return fmt.Errorf("given token or token id %q didn't match pattern [%q] or [%q]", tokenIDOrToken, tokenutil.TokenIDRegexpString, tokenutil.TokenRegexpString)
|
bts, err := kubeadmapiv1alpha2.NewBootstrapTokenString(tokenIDOrToken)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("given token or token id %q didn't match pattern %q or %q", tokenIDOrToken, bootstrapapi.BootstrapTokenIDPattern, bootstrapapi.BootstrapTokenIDPattern)
|
||||||
}
|
}
|
||||||
|
tokenID = bts.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenSecretName := fmt.Sprintf("%s%s", bootstrapapi.BootstrapTokenSecretPrefix, tokenID)
|
tokenSecretName := bootstraputil.BootstrapTokenSecretName(tokenID)
|
||||||
glog.V(1).Infoln("[token] deleting token")
|
glog.V(1).Infoln("[token] deleting token")
|
||||||
if err := client.CoreV1().Secrets(metav1.NamespaceSystem).Delete(tokenSecretName, nil); err != nil {
|
if err := client.CoreV1().Secrets(metav1.NamespaceSystem).Delete(tokenSecretName, nil); err != nil {
|
||||||
return fmt.Errorf("failed to delete bootstrap token [%v]", err)
|
return fmt.Errorf("failed to delete bootstrap token [%v]", err)
|
||||||
@ -367,14 +318,30 @@ func RunDeleteToken(out io.Writer, client clientset.Interface, tokenIDOrToken st
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSecretString(secret *v1.Secret, key string) string {
|
func humanReadableBootstrapToken(token *kubeadmapi.BootstrapToken) string {
|
||||||
if secret.Data == nil {
|
description := token.Description
|
||||||
return ""
|
if len(description) == 0 {
|
||||||
|
description = "<none>"
|
||||||
}
|
}
|
||||||
if val, ok := secret.Data[key]; ok {
|
|
||||||
return string(val)
|
ttl := "<forever>"
|
||||||
|
expires := "<never>"
|
||||||
|
if token.Expires != nil {
|
||||||
|
ttl = duration.ShortHumanDuration(token.Expires.Sub(time.Now()))
|
||||||
|
expires = token.Expires.Format(time.RFC3339)
|
||||||
}
|
}
|
||||||
return ""
|
|
||||||
|
usagesString := strings.Join(token.Usages, ",")
|
||||||
|
if len(usagesString) == 0 {
|
||||||
|
usagesString = "<none>"
|
||||||
|
}
|
||||||
|
|
||||||
|
groupsString := strings.Join(token.Groups, ",")
|
||||||
|
if len(groupsString) == 0 {
|
||||||
|
groupsString = "<none>"
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t%s\n", token.Token.String(), ttl, expires, usagesString, description, groupsString)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getClientset(file string, dryRun bool) (clientset.Interface, error) {
|
func getClientset(file string, dryRun bool) (clientset.Interface, error) {
|
||||||
|
@ -135,13 +135,6 @@ func TestRunCreateToken(t *testing.T) {
|
|||||||
extraGroups: []string{},
|
extraGroups: []string{},
|
||||||
expectedError: false,
|
expectedError: false,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "invalid: incorrect token",
|
|
||||||
token: "123456.AABBCCDDEEFFGGHH",
|
|
||||||
usages: []string{"signing", "authentication"},
|
|
||||||
extraGroups: []string{},
|
|
||||||
expectedError: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "invalid: incorrect extraGroups",
|
name: "invalid: incorrect extraGroups",
|
||||||
token: "abcdef.1234567890123456",
|
token: "abcdef.1234567890123456",
|
||||||
@ -180,18 +173,27 @@ func TestRunCreateToken(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
bts, err := kubeadmapiv1alpha2.NewBootstrapTokenString(tc.token)
|
||||||
|
if err != nil && len(tc.token) != 0 { // if tc.token is "" it's okay as it will be generated later at runtime
|
||||||
|
t.Fatalf("token couldn't be parsed for testing: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
cfg := &kubeadmapiv1alpha2.MasterConfiguration{
|
cfg := &kubeadmapiv1alpha2.MasterConfiguration{
|
||||||
|
|
||||||
// KubernetesVersion is not used by bootstrap-token, but we set this explicitly to avoid
|
// KubernetesVersion is not used by bootstrap-token, but we set this explicitly to avoid
|
||||||
// the lookup of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig
|
// the lookup of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig
|
||||||
KubernetesVersion: "v1.10.0",
|
KubernetesVersion: "v1.10.0",
|
||||||
Token: tc.token,
|
BootstrapTokens: []kubeadmapiv1alpha2.BootstrapToken{
|
||||||
TokenTTL: &metav1.Duration{Duration: 0},
|
{
|
||||||
TokenUsages: tc.usages,
|
Token: bts,
|
||||||
TokenGroups: tc.extraGroups,
|
TTL: &metav1.Duration{Duration: 0},
|
||||||
|
Usages: tc.usages,
|
||||||
|
Groups: tc.extraGroups,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
err := RunCreateToken(&buf, fakeClient, "", cfg, "", tc.printJoin, "")
|
err = RunCreateToken(&buf, fakeClient, "", cfg, tc.printJoin, "")
|
||||||
if (err != nil) != tc.expectedError {
|
if (err != nil) != tc.expectedError {
|
||||||
t.Errorf("Test case %s: RunCreateToken expected error: %v, saw: %v", tc.name, tc.expectedError, (err != nil))
|
t.Errorf("Test case %s: RunCreateToken expected error: %v, saw: %v", tc.name, tc.expectedError, (err != nil))
|
||||||
}
|
}
|
||||||
@ -277,22 +279,6 @@ func TestNewCmdToken(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSecretString(t *testing.T) {
|
|
||||||
secret := v1.Secret{}
|
|
||||||
key := "test-key"
|
|
||||||
if str := getSecretString(&secret, key); str != "" {
|
|
||||||
t.Errorf("getSecretString() did not return empty string for a nil v1.Secret.Data")
|
|
||||||
}
|
|
||||||
secret.Data = make(map[string][]byte)
|
|
||||||
if str := getSecretString(&secret, key); str != "" {
|
|
||||||
t.Errorf("getSecretString() did not return empty string for missing v1.Secret.Data key")
|
|
||||||
}
|
|
||||||
secret.Data[key] = []byte("test-value")
|
|
||||||
if str := getSecretString(&secret, key); str == "" {
|
|
||||||
t.Errorf("getSecretString() failed for a valid v1.Secret.Data key")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetClientset(t *testing.T) {
|
func TestGetClientset(t *testing.T) {
|
||||||
testConfigTokenFile := "test-config-file"
|
testConfigTokenFile := "test-config-file"
|
||||||
|
|
||||||
|
@ -68,7 +68,6 @@ func TestPrintConfiguration(t *testing.T) {
|
|||||||
nodeRegistration:
|
nodeRegistration:
|
||||||
criSocket: ""
|
criSocket: ""
|
||||||
name: ""
|
name: ""
|
||||||
token: ""
|
|
||||||
unifiedControlPlaneImage: ""
|
unifiedControlPlaneImage: ""
|
||||||
`),
|
`),
|
||||||
},
|
},
|
||||||
@ -113,7 +112,6 @@ func TestPrintConfiguration(t *testing.T) {
|
|||||||
nodeRegistration:
|
nodeRegistration:
|
||||||
criSocket: ""
|
criSocket: ""
|
||||||
name: ""
|
name: ""
|
||||||
token: ""
|
|
||||||
unifiedControlPlaneImage: ""
|
unifiedControlPlaneImage: ""
|
||||||
`),
|
`),
|
||||||
},
|
},
|
||||||
|
@ -15,7 +15,6 @@ go_library(
|
|||||||
"//cmd/kubeadm/app/constants:go_default_library",
|
"//cmd/kubeadm/app/constants:go_default_library",
|
||||||
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
|
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
|
||||||
"//cmd/kubeadm/app/util/pubkeypin:go_default_library",
|
"//cmd/kubeadm/app/util/pubkeypin:go_default_library",
|
||||||
"//cmd/kubeadm/app/util/token:go_default_library",
|
|
||||||
"//pkg/controller/bootstrap:go_default_library",
|
"//pkg/controller/bootstrap:go_default_library",
|
||||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
@ -34,7 +34,6 @@ import (
|
|||||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
|
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin"
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin"
|
||||||
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
|
|
||||||
"k8s.io/kubernetes/pkg/controller/bootstrap"
|
"k8s.io/kubernetes/pkg/controller/bootstrap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -45,7 +44,7 @@ const BootstrapUser = "token-bootstrap-client"
|
|||||||
// It then makes sure it can trust the API Server by looking at the JWS-signed tokens and (if cfg.DiscoveryTokenCACertHashes is not empty)
|
// It then makes sure it can trust the API Server by looking at the JWS-signed tokens and (if cfg.DiscoveryTokenCACertHashes is not empty)
|
||||||
// validating the cluster CA against a set of pinned public keys
|
// validating the cluster CA against a set of pinned public keys
|
||||||
func RetrieveValidatedClusterInfo(cfg *kubeadmapi.NodeConfiguration) (*clientcmdapi.Cluster, error) {
|
func RetrieveValidatedClusterInfo(cfg *kubeadmapi.NodeConfiguration) (*clientcmdapi.Cluster, error) {
|
||||||
tokenID, tokenSecret, err := tokenutil.ParseToken(cfg.DiscoveryToken)
|
token, err := kubeadmapi.NewBootstrapTokenString(cfg.DiscoveryToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -88,11 +87,11 @@ func RetrieveValidatedClusterInfo(cfg *kubeadmapi.NodeConfiguration) (*clientcmd
|
|||||||
if !ok || len(insecureKubeconfigString) == 0 {
|
if !ok || len(insecureKubeconfigString) == 0 {
|
||||||
return nil, fmt.Errorf("there is no %s key in the %s ConfigMap. This API Server isn't set up for token bootstrapping, can't connect", bootstrapapi.KubeConfigKey, bootstrapapi.ConfigMapClusterInfo)
|
return nil, fmt.Errorf("there is no %s key in the %s ConfigMap. This API Server isn't set up for token bootstrapping, can't connect", bootstrapapi.KubeConfigKey, bootstrapapi.ConfigMapClusterInfo)
|
||||||
}
|
}
|
||||||
detachedJWSToken, ok := insecureClusterInfo.Data[bootstrapapi.JWSSignatureKeyPrefix+tokenID]
|
detachedJWSToken, ok := insecureClusterInfo.Data[bootstrapapi.JWSSignatureKeyPrefix+token.ID]
|
||||||
if !ok || len(detachedJWSToken) == 0 {
|
if !ok || len(detachedJWSToken) == 0 {
|
||||||
return nil, fmt.Errorf("token id %q is invalid for this cluster or it has expired. Use \"kubeadm token create\" on the master node to creating a new valid token", tokenID)
|
return nil, fmt.Errorf("token id %q is invalid for this cluster or it has expired. Use \"kubeadm token create\" on the master node to creating a new valid token", token.ID)
|
||||||
}
|
}
|
||||||
if !bootstrap.DetachedTokenIsValid(detachedJWSToken, insecureKubeconfigString, tokenID, tokenSecret) {
|
if !bootstrap.DetachedTokenIsValid(detachedJWSToken, insecureKubeconfigString, token.ID, token.Secret) {
|
||||||
return nil, fmt.Errorf("failed to verify JWS signature of received cluster info object, can't trust this API Server")
|
return nil, fmt.Errorf("failed to verify JWS signature of received cluster info object, can't trust this API Server")
|
||||||
}
|
}
|
||||||
insecureKubeconfigBytes := []byte(insecureKubeconfigString)
|
insecureKubeconfigBytes := []byte(insecureKubeconfigString)
|
||||||
|
@ -3,14 +3,6 @@ package(default_visibility = ["//visibility:public"])
|
|||||||
load(
|
load(
|
||||||
"@io_bazel_rules_go//go:def.bzl",
|
"@io_bazel_rules_go//go:def.bzl",
|
||||||
"go_library",
|
"go_library",
|
||||||
"go_test",
|
|
||||||
)
|
|
||||||
|
|
||||||
go_test(
|
|
||||||
name = "go_default_test",
|
|
||||||
srcs = ["token_test.go"],
|
|
||||||
embed = [":go_default_library"],
|
|
||||||
deps = ["//cmd/kubeadm/app/apis/kubeadm:go_default_library"],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
@ -21,16 +13,13 @@ go_library(
|
|||||||
],
|
],
|
||||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node",
|
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node",
|
||||||
deps = [
|
deps = [
|
||||||
|
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||||
"//cmd/kubeadm/app/constants:go_default_library",
|
"//cmd/kubeadm/app/constants:go_default_library",
|
||||||
"//cmd/kubeadm/app/util/apiclient:go_default_library",
|
"//cmd/kubeadm/app/util/apiclient:go_default_library",
|
||||||
"//cmd/kubeadm/app/util/token:go_default_library",
|
|
||||||
"//vendor/github.com/golang/glog:go_default_library",
|
"//vendor/github.com/golang/glog:go_default_library",
|
||||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
|
||||||
"//vendor/k8s.io/api/rbac/v1:go_default_library",
|
"//vendor/k8s.io/api/rbac/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/tools/bootstrap/token/api:go_default_library",
|
|
||||||
"//vendor/k8s.io/client-go/tools/bootstrap/token/util:go_default_library",
|
"//vendor/k8s.io/client-go/tools/bootstrap/token/util:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -18,109 +18,43 @@ package node
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
bootstrapapi "k8s.io/client-go/tools/bootstrap/token/api"
|
|
||||||
bootstraputil "k8s.io/client-go/tools/bootstrap/token/util"
|
bootstraputil "k8s.io/client-go/tools/bootstrap/token/util"
|
||||||
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
|
||||||
)
|
)
|
||||||
|
|
||||||
const tokenCreateRetries = 5
|
// TODO(mattmoyer): Move CreateNewTokens, UpdateOrCreateTokens out of this package to client-go for a generic abstraction and client for a Bootstrap Token
|
||||||
|
|
||||||
// TODO(mattmoyer): Move CreateNewToken, UpdateOrCreateToken and encodeTokenSecretData out of this package to client-go for a generic abstraction and client for a Bootstrap Token
|
// CreateNewTokens tries to create a token and fails if one with the same ID already exists
|
||||||
|
func CreateNewTokens(client clientset.Interface, tokens []kubeadmapi.BootstrapToken) error {
|
||||||
// CreateNewToken tries to create a token and fails if one with the same ID already exists
|
return UpdateOrCreateTokens(client, true, tokens)
|
||||||
func CreateNewToken(client clientset.Interface, token string, tokenDuration time.Duration, usages []string, extraGroups []string, description string) error {
|
|
||||||
return UpdateOrCreateToken(client, token, true, tokenDuration, usages, extraGroups, description)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateOrCreateToken attempts to update a token with the given ID, or create if it does not already exist.
|
// UpdateOrCreateTokens attempts to update a token with the given ID, or create if it does not already exist.
|
||||||
func UpdateOrCreateToken(client clientset.Interface, token string, failIfExists bool, tokenDuration time.Duration, usages []string, extraGroups []string, description string) error {
|
func UpdateOrCreateTokens(client clientset.Interface, failIfExists bool, tokens []kubeadmapi.BootstrapToken) error {
|
||||||
tokenID, tokenSecret, err := tokenutil.ParseToken(token)
|
|
||||||
if err != nil {
|
for _, token := range tokens {
|
||||||
return err
|
|
||||||
}
|
secretName := bootstraputil.BootstrapTokenSecretName(token.Token.ID)
|
||||||
secretName := fmt.Sprintf("%s%s", bootstrapapi.BootstrapTokenSecretPrefix, tokenID)
|
|
||||||
var lastErr error
|
|
||||||
for i := 0; i < tokenCreateRetries; i++ {
|
|
||||||
secret, err := client.CoreV1().Secrets(metav1.NamespaceSystem).Get(secretName, metav1.GetOptions{})
|
secret, err := client.CoreV1().Secrets(metav1.NamespaceSystem).Get(secretName, metav1.GetOptions{})
|
||||||
if err == nil {
|
if secret != nil && err == nil && failIfExists {
|
||||||
if failIfExists {
|
return fmt.Errorf("a token with id %q already exists", token.Token.ID)
|
||||||
return fmt.Errorf("a token with id %q already exists", tokenID)
|
|
||||||
}
|
|
||||||
// Secret with this ID already exists, update it:
|
|
||||||
tokenSecretData, err := encodeTokenSecretData(tokenID, tokenSecret, tokenDuration, usages, extraGroups, description)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
secret.Data = tokenSecretData
|
|
||||||
if _, err := client.CoreV1().Secrets(metav1.NamespaceSystem).Update(secret); err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
lastErr = err
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Secret does not already exist:
|
updatedOrNewSecret := token.ToSecret()
|
||||||
if apierrors.IsNotFound(err) {
|
// Try to create or update the token with an exponential backoff
|
||||||
tokenSecretData, err := encodeTokenSecretData(tokenID, tokenSecret, tokenDuration, usages, extraGroups, description)
|
err = apiclient.TryRunCommand(func() error {
|
||||||
if err != nil {
|
if err := apiclient.CreateOrUpdateSecret(client, updatedOrNewSecret); err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to create or update bootstrap token with name %s: %v", secretName, err)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
secret = &v1.Secret{
|
}, 5)
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
if err != nil {
|
||||||
Name: secretName,
|
return err
|
||||||
},
|
|
||||||
Type: v1.SecretType(bootstrapapi.SecretTypeBootstrapToken),
|
|
||||||
Data: tokenSecretData,
|
|
||||||
}
|
|
||||||
if _, err := client.CoreV1().Secrets(metav1.NamespaceSystem).Create(secret); err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
lastErr = err
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return fmt.Errorf(
|
return nil
|
||||||
"unable to create bootstrap token after %d attempts [%v]",
|
|
||||||
tokenCreateRetries,
|
|
||||||
lastErr,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// encodeTokenSecretData takes the token discovery object and an optional duration and returns the .Data for the Secret
|
|
||||||
func encodeTokenSecretData(tokenID, tokenSecret string, duration time.Duration, usages []string, extraGroups []string, description string) (map[string][]byte, error) {
|
|
||||||
data := map[string][]byte{
|
|
||||||
bootstrapapi.BootstrapTokenIDKey: []byte(tokenID),
|
|
||||||
bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret),
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(extraGroups) > 0 {
|
|
||||||
data[bootstrapapi.BootstrapTokenExtraGroupsKey] = []byte(strings.Join(extraGroups, ","))
|
|
||||||
}
|
|
||||||
|
|
||||||
if duration > 0 {
|
|
||||||
// Get the current time, add the specified duration, and format it accordingly
|
|
||||||
durationString := time.Now().Add(duration).Format(time.RFC3339)
|
|
||||||
data[bootstrapapi.BootstrapTokenExpirationKey] = []byte(durationString)
|
|
||||||
}
|
|
||||||
if len(description) > 0 {
|
|
||||||
data[bootstrapapi.BootstrapTokenDescriptionKey] = []byte(description)
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate usages
|
|
||||||
if err := bootstraputil.ValidateUsages(usages); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, usage := range usages {
|
|
||||||
data[bootstrapapi.BootstrapTokenUsagePrefix+usage] = []byte("true")
|
|
||||||
}
|
|
||||||
return data, nil
|
|
||||||
}
|
}
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package node
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEncodeTokenSecretData(t *testing.T) {
|
|
||||||
var tests = []struct {
|
|
||||||
token *kubeadmapi.TokenDiscovery
|
|
||||||
t time.Duration
|
|
||||||
}{
|
|
||||||
{token: &kubeadmapi.TokenDiscovery{ID: "foo", Secret: "bar"}}, // should use default
|
|
||||||
{token: &kubeadmapi.TokenDiscovery{ID: "foo", Secret: "bar"}, t: time.Second}, // should use default
|
|
||||||
}
|
|
||||||
for _, rt := range tests {
|
|
||||||
actual, _ := encodeTokenSecretData(rt.token.ID, rt.token.Secret, rt.t, []string{}, []string{}, "")
|
|
||||||
if !bytes.Equal(actual["token-id"], []byte(rt.token.ID)) {
|
|
||||||
t.Errorf(
|
|
||||||
"failed EncodeTokenSecretData:\n\texpected: %s\n\t actual: %s",
|
|
||||||
rt.token.ID,
|
|
||||||
actual["token-id"],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(actual["token-secret"], []byte(rt.token.Secret)) {
|
|
||||||
t.Errorf(
|
|
||||||
"failed EncodeTokenSecretData:\n\texpected: %s\n\t actual: %s",
|
|
||||||
rt.token.Secret,
|
|
||||||
actual["token-secret"],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if rt.t > 0 {
|
|
||||||
if actual["expiration"] == nil {
|
|
||||||
t.Errorf(
|
|
||||||
"failed EncodeTokenSecretData, duration was not added to time",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -41,7 +41,7 @@ func UploadConfiguration(cfg *kubeadmapi.MasterConfiguration, client clientset.I
|
|||||||
kubeadmscheme.Scheme.Convert(cfg, externalcfg, nil)
|
kubeadmscheme.Scheme.Convert(cfg, externalcfg, nil)
|
||||||
|
|
||||||
// Removes sensitive info from the data that will be stored in the config map
|
// Removes sensitive info from the data that will be stored in the config map
|
||||||
externalcfg.Token = ""
|
externalcfg.BootstrapTokens = nil
|
||||||
|
|
||||||
cfgYaml, err := util.MarshalToYamlForCodecs(externalcfg, kubeadmapiv1alpha2.SchemeGroupVersion, scheme.Codecs)
|
cfgYaml, err := util.MarshalToYamlForCodecs(externalcfg, kubeadmapiv1alpha2.SchemeGroupVersion, scheme.Codecs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -63,8 +63,15 @@ func TestUploadConfiguration(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
cfg := &kubeadmapi.MasterConfiguration{
|
cfg := &kubeadmapi.MasterConfiguration{
|
||||||
KubernetesVersion: "1.7.3",
|
KubernetesVersion: "v1.10.3",
|
||||||
Token: "1234567",
|
BootstrapTokens: []kubeadmapi.BootstrapToken{
|
||||||
|
{
|
||||||
|
Token: &kubeadmapi.BootstrapTokenString{
|
||||||
|
ID: "abcdef",
|
||||||
|
Secret: "abcdef0123456789",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
client := clientsetfake.NewSimpleClientset()
|
client := clientsetfake.NewSimpleClientset()
|
||||||
if tt.errOnCreate != nil {
|
if tt.errOnCreate != nil {
|
||||||
@ -110,8 +117,9 @@ func TestUploadConfiguration(t *testing.T) {
|
|||||||
t.Errorf("Decoded value doesn't match, decoded = %#v, expected = %#v", decodedCfg.KubernetesVersion, cfg.KubernetesVersion)
|
t.Errorf("Decoded value doesn't match, decoded = %#v, expected = %#v", decodedCfg.KubernetesVersion, cfg.KubernetesVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
if decodedCfg.Token != "" {
|
// If the decoded cfg has a BootstrapTokens array, verify the sensitive information we had isn't still there.
|
||||||
t.Errorf("Decoded value contains token (sensitive info), decoded = %#v, expected = empty", decodedCfg.Token)
|
if len(decodedCfg.BootstrapTokens) > 0 && decodedCfg.BootstrapTokens[0].Token != nil && decodedCfg.BootstrapTokens[0].Token.String() == cfg.BootstrapTokens[0].Token.String() {
|
||||||
|
t.Errorf("Decoded value contains .BootstrapTokens (sensitive info), decoded = %#v, expected = empty", decodedCfg.BootstrapTokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
if decodedExtCfg.Kind != "MasterConfiguration" {
|
if decodedExtCfg.Kind != "MasterConfiguration" {
|
||||||
|
@ -74,7 +74,6 @@ filegroup(
|
|||||||
"//cmd/kubeadm/app/util/kubeconfig:all-srcs",
|
"//cmd/kubeadm/app/util/kubeconfig:all-srcs",
|
||||||
"//cmd/kubeadm/app/util/pubkeypin:all-srcs",
|
"//cmd/kubeadm/app/util/pubkeypin:all-srcs",
|
||||||
"//cmd/kubeadm/app/util/staticpod:all-srcs",
|
"//cmd/kubeadm/app/util/staticpod:all-srcs",
|
||||||
"//cmd/kubeadm/app/util/token:all-srcs",
|
|
||||||
],
|
],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
)
|
)
|
||||||
|
@ -22,7 +22,6 @@ go_library(
|
|||||||
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
|
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
|
||||||
"//cmd/kubeadm/app/constants:go_default_library",
|
"//cmd/kubeadm/app/constants:go_default_library",
|
||||||
"//cmd/kubeadm/app/util:go_default_library",
|
"//cmd/kubeadm/app/util:go_default_library",
|
||||||
"//cmd/kubeadm/app/util/token:go_default_library",
|
|
||||||
"//pkg/util/node:go_default_library",
|
"//pkg/util/node:go_default_library",
|
||||||
"//pkg/util/version:go_default_library",
|
"//pkg/util/version:go_default_library",
|
||||||
"//vendor/github.com/golang/glog:go_default_library",
|
"//vendor/github.com/golang/glog:go_default_library",
|
||||||
@ -32,6 +31,7 @@ go_library(
|
|||||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/tools/bootstrap/token/util:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
netutil "k8s.io/apimachinery/pkg/util/net"
|
netutil "k8s.io/apimachinery/pkg/util/net"
|
||||||
|
bootstraputil "k8s.io/client-go/tools/bootstrap/token/util"
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
||||||
kubeadmapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
kubeadmapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
||||||
@ -33,7 +34,6 @@ import (
|
|||||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
||||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||||
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
|
|
||||||
"k8s.io/kubernetes/pkg/util/node"
|
"k8s.io/kubernetes/pkg/util/node"
|
||||||
"k8s.io/kubernetes/pkg/util/version"
|
"k8s.io/kubernetes/pkg/util/version"
|
||||||
)
|
)
|
||||||
@ -60,17 +60,29 @@ func SetInitDynamicDefaults(cfg *kubeadmapi.MasterConfiguration) error {
|
|||||||
cfg.KubeProxy.Config.BindAddress = kubeadmapiv1alpha2.DefaultProxyBindAddressv6
|
cfg.KubeProxy.Config.BindAddress = kubeadmapiv1alpha2.DefaultProxyBindAddressv6
|
||||||
}
|
}
|
||||||
// Resolve possible version labels and validate version string
|
// Resolve possible version labels and validate version string
|
||||||
err = NormalizeKubernetesVersion(cfg)
|
if err := NormalizeKubernetesVersion(cfg); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Token == "" {
|
// Populate the .Token field with a random value if unset
|
||||||
var err error
|
// We do this at this layer, and not the API defaulting layer
|
||||||
cfg.Token, err = tokenutil.GenerateToken()
|
// because of possible security concerns, and more practically
|
||||||
|
// because we can't return errors in the API object defaulting
|
||||||
|
// process but here we can.
|
||||||
|
for i, bt := range cfg.BootstrapTokens {
|
||||||
|
if bt.Token != nil && len(bt.Token.String()) > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenStr, err := bootstraputil.GenerateBootstrapToken()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("couldn't generate random token: %v", err)
|
return fmt.Errorf("couldn't generate random token: %v", err)
|
||||||
}
|
}
|
||||||
|
token, err := kubeadmapi.NewBootstrapTokenString(tokenStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cfg.BootstrapTokens[i].Token = token
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.NodeRegistration.Name = node.GetHostname(cfg.NodeRegistration.Name)
|
cfg.NodeRegistration.Name = node.GetHostname(cfg.NodeRegistration.Name)
|
||||||
|
@ -10,6 +10,16 @@ AuditPolicyConfiguration:
|
|||||||
LogDir: /var/log/kubernetes/audit
|
LogDir: /var/log/kubernetes/audit
|
||||||
LogMaxAge: 2
|
LogMaxAge: 2
|
||||||
Path: ""
|
Path: ""
|
||||||
|
BootstrapTokens:
|
||||||
|
- Description: ""
|
||||||
|
Expires: null
|
||||||
|
Groups:
|
||||||
|
- system:bootstrappers:kubeadm:default-node-token
|
||||||
|
TTL: 24h0m0s
|
||||||
|
Token: s73ybu.6tw6wnqgp5z0wb77
|
||||||
|
Usages:
|
||||||
|
- signing
|
||||||
|
- authentication
|
||||||
CIImageRepository: ""
|
CIImageRepository: ""
|
||||||
CertificatesDir: /etc/kubernetes/pki
|
CertificatesDir: /etc/kubernetes/pki
|
||||||
ClusterName: kubernetes
|
ClusterName: kubernetes
|
||||||
@ -148,11 +158,4 @@ NodeRegistration:
|
|||||||
key: node-role.kubernetes.io/master
|
key: node-role.kubernetes.io/master
|
||||||
SchedulerExtraArgs: null
|
SchedulerExtraArgs: null
|
||||||
SchedulerExtraVolumes: null
|
SchedulerExtraVolumes: null
|
||||||
Token: s73ybu.6tw6wnqgp5z0wb77
|
|
||||||
TokenGroups:
|
|
||||||
- system:bootstrappers:kubeadm:default-node-token
|
|
||||||
TokenTTL: 24h0m0s
|
|
||||||
TokenUsages:
|
|
||||||
- signing
|
|
||||||
- authentication
|
|
||||||
UnifiedControlPlaneImage: ""
|
UnifiedControlPlaneImage: ""
|
||||||
|
@ -9,6 +9,14 @@ auditPolicy:
|
|||||||
logDir: /var/log/kubernetes/audit
|
logDir: /var/log/kubernetes/audit
|
||||||
logMaxAge: 2
|
logMaxAge: 2
|
||||||
path: ""
|
path: ""
|
||||||
|
bootstrapTokens:
|
||||||
|
- groups:
|
||||||
|
- system:bootstrappers:kubeadm:default-node-token
|
||||||
|
token: s73ybu.6tw6wnqgp5z0wb77
|
||||||
|
ttl: 24h0m0s
|
||||||
|
usages:
|
||||||
|
- signing
|
||||||
|
- authentication
|
||||||
certificatesDir: /etc/kubernetes/pki
|
certificatesDir: /etc/kubernetes/pki
|
||||||
clusterName: kubernetes
|
clusterName: kubernetes
|
||||||
etcd:
|
etcd:
|
||||||
@ -137,11 +145,4 @@ nodeRegistration:
|
|||||||
taints:
|
taints:
|
||||||
- effect: NoSchedule
|
- effect: NoSchedule
|
||||||
key: node-role.kubernetes.io/master
|
key: node-role.kubernetes.io/master
|
||||||
token: s73ybu.6tw6wnqgp5z0wb77
|
|
||||||
tokenGroups:
|
|
||||||
- system:bootstrappers:kubeadm:default-node-token
|
|
||||||
tokenTTL: 24h0m0s
|
|
||||||
tokenUsages:
|
|
||||||
- signing
|
|
||||||
- authentication
|
|
||||||
unifiedControlPlaneImage: ""
|
unifiedControlPlaneImage: ""
|
||||||
|
@ -7,6 +7,14 @@ auditPolicy:
|
|||||||
logDir: /var/log/kubernetes/audit
|
logDir: /var/log/kubernetes/audit
|
||||||
logMaxAge: 2
|
logMaxAge: 2
|
||||||
path: ""
|
path: ""
|
||||||
|
bootstrapTokens:
|
||||||
|
- groups:
|
||||||
|
- system:bootstrappers:kubeadm:default-node-token
|
||||||
|
token: s73ybu.6tw6wnqgp5z0wb77
|
||||||
|
ttl: 24h0m0s
|
||||||
|
usages:
|
||||||
|
- signing
|
||||||
|
- authentication
|
||||||
certificatesDir: /var/lib/kubernetes/pki
|
certificatesDir: /var/lib/kubernetes/pki
|
||||||
clusterName: kubernetes
|
clusterName: kubernetes
|
||||||
etcd:
|
etcd:
|
||||||
@ -132,11 +140,4 @@ nodeRegistration:
|
|||||||
taints:
|
taints:
|
||||||
- effect: NoSchedule
|
- effect: NoSchedule
|
||||||
key: node-role.kubernetes.io/master
|
key: node-role.kubernetes.io/master
|
||||||
token: s73ybu.6tw6wnqgp5z0wb77
|
|
||||||
tokenGroups:
|
|
||||||
- system:bootstrappers:kubeadm:default-node-token
|
|
||||||
tokenTTL: 24h0m0s
|
|
||||||
tokenUsages:
|
|
||||||
- signing
|
|
||||||
- authentication
|
|
||||||
unifiedControlPlaneImage: ""
|
unifiedControlPlaneImage: ""
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
# This file _should_ set TypeMeta, but at some point earlier we supported deserializing MasterConfigurations without TypeMeta, so we need to support that as long as we
|
||||||
|
# support the v1alpha1 API. In the meantime kubeadm will treat this as v1alpha1 automatically when unmarshalling.
|
||||||
api:
|
api:
|
||||||
advertiseAddress: 192.168.2.2
|
advertiseAddress: 192.168.2.2
|
||||||
bindPort: 6443
|
bindPort: 6443
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
package(default_visibility = ["//visibility:public"])
|
|
||||||
|
|
||||||
load(
|
|
||||||
"@io_bazel_rules_go//go:def.bzl",
|
|
||||||
"go_library",
|
|
||||||
"go_test",
|
|
||||||
)
|
|
||||||
|
|
||||||
go_test(
|
|
||||||
name = "go_default_test",
|
|
||||||
srcs = ["tokens_test.go"],
|
|
||||||
embed = [":go_default_library"],
|
|
||||||
deps = ["//cmd/kubeadm/app/apis/kubeadm:go_default_library"],
|
|
||||||
)
|
|
||||||
|
|
||||||
go_library(
|
|
||||||
name = "go_default_library",
|
|
||||||
srcs = ["tokens.go"],
|
|
||||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/token",
|
|
||||||
deps = ["//cmd/kubeadm/app/apis/kubeadm:go_default_library"],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "package-srcs",
|
|
||||||
srcs = glob(["**"]),
|
|
||||||
tags = ["automanaged"],
|
|
||||||
visibility = ["//visibility:private"],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "all-srcs",
|
|
||||||
srcs = [":package-srcs"],
|
|
||||||
tags = ["automanaged"],
|
|
||||||
)
|
|
@ -1,125 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package token
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"crypto/rand"
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// TokenIDBytes defines a number of bytes used for a token id
|
|
||||||
TokenIDBytes = 6
|
|
||||||
// TokenSecretBytes defines a number of bytes used for a secret
|
|
||||||
TokenSecretBytes = 16
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// TokenIDRegexpString defines token's id regular expression pattern
|
|
||||||
TokenIDRegexpString = "^([a-z0-9]{6})$"
|
|
||||||
// TokenIDRegexp is a compiled regular expression of TokenIDRegexpString
|
|
||||||
TokenIDRegexp = regexp.MustCompile(TokenIDRegexpString)
|
|
||||||
// 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)
|
|
||||||
)
|
|
||||||
|
|
||||||
const validBootstrapTokenChars = "0123456789abcdefghijklmnopqrstuvwxyz"
|
|
||||||
|
|
||||||
func randBytes(length int) (string, error) {
|
|
||||||
// len("0123456789abcdefghijklmnopqrstuvwxyz") = 36 which doesn't evenly divide
|
|
||||||
// the possible values of a byte: 256 mod 36 = 4. Discard any random bytes we
|
|
||||||
// read that are >= 252 so the bytes we evenly divide the character set.
|
|
||||||
const maxByteValue = 252
|
|
||||||
|
|
||||||
var (
|
|
||||||
b byte
|
|
||||||
err error
|
|
||||||
token = make([]byte, length)
|
|
||||||
)
|
|
||||||
|
|
||||||
reader := bufio.NewReaderSize(rand.Reader, length*2)
|
|
||||||
for i := range token {
|
|
||||||
for {
|
|
||||||
if b, err = reader.ReadByte(); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if b < maxByteValue {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
token[i] = validBootstrapTokenChars[int(b)%len(validBootstrapTokenChars)]
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(token), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateToken generates a new token with a token ID that is valid as a
|
|
||||||
// Kubernetes DNS label.
|
|
||||||
// For more info, see kubernetes/pkg/util/validation/validation.go.
|
|
||||||
func GenerateToken() (string, error) {
|
|
||||||
tokenID, err := randBytes(TokenIDBytes)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenSecret, err := randBytes(TokenSecretBytes)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%s.%s", tokenID, tokenSecret), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseTokenID tries and parse a valid token ID from a string.
|
|
||||||
// An error is returned in case of failure.
|
|
||||||
func ParseTokenID(s string) error {
|
|
||||||
if !TokenIDRegexp.MatchString(s) {
|
|
||||||
return fmt.Errorf("token ID [%q] was not of form [%q]", s, TokenIDRegexpString)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// BearerToken returns a string representation of the passed token.
|
|
||||||
func BearerToken(d *kubeadmapi.TokenDiscovery) string {
|
|
||||||
return fmt.Sprintf("%s.%s", d.ID, d.Secret)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateToken validates whether a token is well-formed.
|
|
||||||
// In case it's not, the corresponding error is returned as well.
|
|
||||||
func ValidateToken(d *kubeadmapi.TokenDiscovery) (bool, error) {
|
|
||||||
if _, _, err := ParseToken(d.ID + "." + d.Secret); err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
@ -1,173 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package token
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTokenParse(t *testing.T) {
|
|
||||||
var tests = []struct {
|
|
||||||
token string
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{token: "1234567890123456789012", expected: false}, // invalid parcel size
|
|
||||||
{token: "12345.1234567890123456", expected: false}, // invalid parcel size
|
|
||||||
{token: ".1234567890123456", expected: false}, // invalid parcel size
|
|
||||||
{token: "123456:1234567890.123456", expected: false}, // invalid separation
|
|
||||||
{token: "abcdef:1234567890123456", expected: false}, // invalid separation
|
|
||||||
{token: "Abcdef.1234567890123456", expected: false}, // invalid token id
|
|
||||||
{token: "123456.AABBCCDDEEFFGGHH", expected: false}, // invalid token secret
|
|
||||||
{token: "abcdef.1234567890123456", expected: true},
|
|
||||||
{token: "123456.aabbccddeeffgghh", expected: true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rt := range tests {
|
|
||||||
_, _, actual := ParseToken(rt.token)
|
|
||||||
if (actual == nil) != rt.expected {
|
|
||||||
t.Errorf(
|
|
||||||
"failed ParseToken for this token: [%s]\n\texpected: %t\n\t actual: %t",
|
|
||||||
rt.token,
|
|
||||||
rt.expected,
|
|
||||||
(actual == nil),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseTokenID(t *testing.T) {
|
|
||||||
var tests = []struct {
|
|
||||||
tokenID string
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{tokenID: "", expected: false},
|
|
||||||
{tokenID: "1234567890123456789012", expected: false},
|
|
||||||
{tokenID: "12345", expected: false},
|
|
||||||
{tokenID: "Abcdef", expected: false},
|
|
||||||
{tokenID: "abcdef", expected: true},
|
|
||||||
{tokenID: "123456", expected: true},
|
|
||||||
}
|
|
||||||
for _, rt := range tests {
|
|
||||||
actual := ParseTokenID(rt.tokenID)
|
|
||||||
if (actual == nil) != rt.expected {
|
|
||||||
t.Errorf(
|
|
||||||
"failed ParseTokenID for this token ID: [%s]\n\texpected: %t\n\t actual: %t",
|
|
||||||
rt.tokenID,
|
|
||||||
rt.expected,
|
|
||||||
(actual == nil),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValidateToken(t *testing.T) {
|
|
||||||
var tests = []struct {
|
|
||||||
token *kubeadmapi.TokenDiscovery
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{token: &kubeadmapi.TokenDiscovery{ID: "", Secret: ""}, expected: false},
|
|
||||||
{token: &kubeadmapi.TokenDiscovery{ID: "1234567890123456789012", Secret: ""}, expected: false},
|
|
||||||
{token: &kubeadmapi.TokenDiscovery{ID: "", Secret: "1234567890123456789012"}, expected: false},
|
|
||||||
{token: &kubeadmapi.TokenDiscovery{ID: "12345", Secret: "1234567890123456"}, expected: false},
|
|
||||||
{token: &kubeadmapi.TokenDiscovery{ID: "Abcdef", Secret: "1234567890123456"}, expected: false},
|
|
||||||
{token: &kubeadmapi.TokenDiscovery{ID: "123456", Secret: "AABBCCDDEEFFGGHH"}, expected: false},
|
|
||||||
{token: &kubeadmapi.TokenDiscovery{ID: "abc*ef", Secret: "1234567890123456"}, expected: false},
|
|
||||||
{token: &kubeadmapi.TokenDiscovery{ID: "abcdef", Secret: "123456789*123456"}, expected: false},
|
|
||||||
{token: &kubeadmapi.TokenDiscovery{ID: "abcdef", Secret: "1234567890123456"}, expected: true},
|
|
||||||
{token: &kubeadmapi.TokenDiscovery{ID: "123456", Secret: "aabbccddeeffgghh"}, expected: true},
|
|
||||||
{token: &kubeadmapi.TokenDiscovery{ID: "abc456", Secret: "1234567890123456"}, expected: true},
|
|
||||||
{token: &kubeadmapi.TokenDiscovery{ID: "abcdef", Secret: "123456ddeeffgghh"}, expected: true},
|
|
||||||
}
|
|
||||||
for _, rt := range tests {
|
|
||||||
valid, actual := ValidateToken(rt.token)
|
|
||||||
if (actual == nil) != rt.expected {
|
|
||||||
t.Errorf(
|
|
||||||
"failed ValidateToken for this token ID: [%s]\n\texpected: %t\n\t actual: %t",
|
|
||||||
rt.token,
|
|
||||||
rt.expected,
|
|
||||||
(actual == nil),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (valid == true) != rt.expected {
|
|
||||||
t.Errorf(
|
|
||||||
"failed ValidateToken for this token ID: [%s]\n\texpected: %t\n\t actual: %t",
|
|
||||||
rt.token,
|
|
||||||
rt.expected,
|
|
||||||
(actual == nil),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGenerateToken(t *testing.T) {
|
|
||||||
token, err := GenerateToken()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("GenerateToken returned an unexpected error: %+v", err)
|
|
||||||
}
|
|
||||||
tokenID, tokenSecret, err := ParseToken(token)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("GenerateToken returned an unexpected error: %+v", err)
|
|
||||||
}
|
|
||||||
if len(tokenID) != 6 {
|
|
||||||
t.Errorf("failed GenerateToken first part length:\n\texpected: 6\n\t actual: %d", len(tokenID))
|
|
||||||
}
|
|
||||||
if len(tokenSecret) != 16 {
|
|
||||||
t.Errorf("failed GenerateToken second part length:\n\texpected: 16\n\t actual: %d", len(tokenSecret))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRandBytes(t *testing.T) {
|
|
||||||
var randTest = []int{
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
3,
|
|
||||||
100,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rt := range randTest {
|
|
||||||
actual, err := randBytes(rt)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed randBytes: %v", err)
|
|
||||||
}
|
|
||||||
if len(actual) != rt {
|
|
||||||
t.Errorf("failed randBytes:\n\texpected: %d\n\t actual: %d\n", rt, len(actual))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBearerToken(t *testing.T) {
|
|
||||||
var tests = []struct {
|
|
||||||
token *kubeadmapi.TokenDiscovery
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{token: &kubeadmapi.TokenDiscovery{ID: "foo", Secret: "bar"}, expected: "foo.bar"}, // should use default
|
|
||||||
}
|
|
||||||
for _, rt := range tests {
|
|
||||||
actual := BearerToken(rt.token)
|
|
||||||
if actual != rt.expected {
|
|
||||||
t.Errorf(
|
|
||||||
"failed BearerToken:\n\texpected: %s\n\t actual: %s",
|
|
||||||
rt.expected,
|
|
||||||
actual,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Package api (pkg/bootstrap/token/api) contains constants and types needed for
|
// Package api (k8s.io/client-go/tools/bootstrap/token/api) contains constants and types needed for
|
||||||
// bootstrap tokens as maintained by the BootstrapSigner and TokenCleaner
|
// bootstrap tokens as maintained by the BootstrapSigner and TokenCleaner
|
||||||
// controllers (in pkg/controller/bootstrap)
|
// controllers (in k8s.io/kubernetes/pkg/controller/bootstrap)
|
||||||
package api // import "k8s.io/client-go/tools/bootstrap/token/api"
|
package api // import "k8s.io/client-go/tools/bootstrap/token/api"
|
||||||
|
@ -86,14 +86,26 @@ const (
|
|||||||
// authenticate as. The full username given is "system:bootstrap:<token-id>".
|
// authenticate as. The full username given is "system:bootstrap:<token-id>".
|
||||||
BootstrapUserPrefix = "system:bootstrap:"
|
BootstrapUserPrefix = "system:bootstrap:"
|
||||||
|
|
||||||
// BootstrapGroupPattern is the valid regex pattern that all groups
|
|
||||||
// assigned to a bootstrap token by BootstrapTokenExtraGroupsKey must match.
|
|
||||||
// See also ValidateBootstrapGroupName().
|
|
||||||
BootstrapGroupPattern = "system:bootstrappers:[a-z0-9:-]{0,255}[a-z0-9]"
|
|
||||||
|
|
||||||
// BootstrapDefaultGroup is the default group for bootstrapping bearer
|
// BootstrapDefaultGroup is the default group for bootstrapping bearer
|
||||||
// tokens (in addition to any groups from BootstrapTokenExtraGroupsKey).
|
// tokens (in addition to any groups from BootstrapTokenExtraGroupsKey).
|
||||||
BootstrapDefaultGroup = "system:bootstrappers"
|
BootstrapDefaultGroup = "system:bootstrappers"
|
||||||
|
|
||||||
|
// BootstrapGroupPattern is the valid regex pattern that all groups
|
||||||
|
// assigned to a bootstrap token by BootstrapTokenExtraGroupsKey must match.
|
||||||
|
// See also util.ValidateBootstrapGroupName()
|
||||||
|
BootstrapGroupPattern = `\Asystem:bootstrappers:[a-z0-9:-]{0,255}[a-z0-9]\z`
|
||||||
|
|
||||||
|
// BootstrapTokenPattern defines the {id}.{secret} regular expression pattern
|
||||||
|
BootstrapTokenPattern = `\A([a-z0-9]{6})\.([a-z0-9]{16})\z`
|
||||||
|
|
||||||
|
// BootstrapTokenIDPattern defines token's id regular expression pattern
|
||||||
|
BootstrapTokenIDPattern = `\A([a-z0-9]{6})\z`
|
||||||
|
|
||||||
|
// BootstrapTokenIDBytes defines the number of bytes used for the Bootstrap Token's ID field
|
||||||
|
BootstrapTokenIDBytes = 6
|
||||||
|
|
||||||
|
// BootstrapTokenSecretBytes defines the number of bytes used the Bootstrap Token's Secret field
|
||||||
|
BootstrapTokenSecretBytes = 16
|
||||||
)
|
)
|
||||||
|
|
||||||
// KnownTokenUsages specifies the known functions a token will get.
|
// KnownTokenUsages specifies the known functions a token will get.
|
||||||
|
@ -17,20 +17,101 @@ limitations under the License.
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
|
||||||
"k8s.io/client-go/tools/bootstrap/token/api"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
|
"k8s.io/client-go/tools/bootstrap/token/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bootstrapGroupRegexp = regexp.MustCompile(`\A` + api.BootstrapGroupPattern + `\z`)
|
// validBootstrapTokenChars defines the characters a bootstrap token can consist of
|
||||||
|
const validBootstrapTokenChars = "0123456789abcdefghijklmnopqrstuvwxyz"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// BootstrapTokenRegexp is a compiled regular expression of TokenRegexpString
|
||||||
|
BootstrapTokenRegexp = regexp.MustCompile(api.BootstrapTokenPattern)
|
||||||
|
// BootstrapTokenIDRegexp is a compiled regular expression of TokenIDRegexpString
|
||||||
|
BootstrapTokenIDRegexp = regexp.MustCompile(api.BootstrapTokenIDPattern)
|
||||||
|
// BootstrapGroupRegexp is a compiled regular expression of BootstrapGroupPattern
|
||||||
|
BootstrapGroupRegexp = regexp.MustCompile(api.BootstrapGroupPattern)
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateBootstrapToken generates a new, random Bootstrap Token.
|
||||||
|
func GenerateBootstrapToken() (string, error) {
|
||||||
|
tokenID, err := randBytes(api.BootstrapTokenIDBytes)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenSecret, err := randBytes(api.BootstrapTokenSecretBytes)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return TokenFromIDAndSecret(tokenID, tokenSecret), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// randBytes returns a random string consisting of the characters in
|
||||||
|
// validBootstrapTokenChars, with the length customized by the parameter
|
||||||
|
func randBytes(length int) (string, error) {
|
||||||
|
// len("0123456789abcdefghijklmnopqrstuvwxyz") = 36 which doesn't evenly divide
|
||||||
|
// the possible values of a byte: 256 mod 36 = 4. Discard any random bytes we
|
||||||
|
// read that are >= 252 so the bytes we evenly divide the character set.
|
||||||
|
const maxByteValue = 252
|
||||||
|
|
||||||
|
var (
|
||||||
|
b byte
|
||||||
|
err error
|
||||||
|
token = make([]byte, length)
|
||||||
|
)
|
||||||
|
|
||||||
|
reader := bufio.NewReaderSize(rand.Reader, length*2)
|
||||||
|
for i := range token {
|
||||||
|
for {
|
||||||
|
if b, err = reader.ReadByte(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if b < maxByteValue {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token[i] = validBootstrapTokenChars[int(b)%len(validBootstrapTokenChars)]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(token), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokenFromIDAndSecret returns the full token which is of the form "{id}.{secret}"
|
||||||
|
func TokenFromIDAndSecret(id, secret string) string {
|
||||||
|
return fmt.Sprintf("%s.%s", id, secret)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValidBootstrapToken returns whether the given string is valid as a Bootstrap Token and
|
||||||
|
// in other words satisfies the BootstrapTokenRegexp
|
||||||
|
func IsValidBootstrapToken(token string) bool {
|
||||||
|
return BootstrapTokenRegexp.MatchString(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValidBootstrapTokenID returns whether the given string is valid as a Bootstrap Token ID and
|
||||||
|
// in other words satisfies the BootstrapTokenIDRegexp
|
||||||
|
func IsValidBootstrapTokenID(tokenID string) bool {
|
||||||
|
return BootstrapTokenIDRegexp.MatchString(tokenID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BootstrapTokenSecretName returns the expected name for the Secret storing the
|
||||||
|
// Bootstrap Token in the Kubernetes API.
|
||||||
|
func BootstrapTokenSecretName(tokenID string) string {
|
||||||
|
return fmt.Sprintf("%s%s", api.BootstrapTokenSecretPrefix, tokenID)
|
||||||
|
}
|
||||||
|
|
||||||
// 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(mattmoyer): this validation should migrate out to client-go (see https://github.com/kubernetes/client-go/issues/114)
|
|
||||||
func ValidateBootstrapGroupName(name string) error {
|
func ValidateBootstrapGroupName(name string) error {
|
||||||
if bootstrapGroupRegexp.Match([]byte(name)) {
|
if BootstrapGroupRegexp.Match([]byte(name)) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("bootstrap group %q is invalid (must match %s)", name, api.BootstrapGroupPattern)
|
return fmt.Errorf("bootstrap group %q is invalid (must match %s)", name, api.BootstrapGroupPattern)
|
||||||
@ -46,7 +127,7 @@ func ValidateUsages(usages []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(invalidUsages) > 0 {
|
if len(invalidUsages) > 0 {
|
||||||
return fmt.Errorf("invalide bootstrap token usage string: %s, valid usage options: %s", strings.Join(invalidUsages.List(), ","), strings.Join(api.KnownTokenUsages, ","))
|
return fmt.Errorf("invalid bootstrap token usage string: %s, valid usage options: %s", strings.Join(invalidUsages.List(), ","), strings.Join(api.KnownTokenUsages, ","))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,143 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestGenerateBootstrapToken(t *testing.T) {
|
||||||
|
token, err := GenerateBootstrapToken()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GenerateBootstrapToken returned an unexpected error: %+v", err)
|
||||||
|
}
|
||||||
|
if !IsValidBootstrapToken(token) {
|
||||||
|
t.Errorf("GenerateBootstrapToken didn't generate a valid token: %q", token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRandBytes(t *testing.T) {
|
||||||
|
var randTest = []int{
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
100,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rt := range randTest {
|
||||||
|
actual, err := randBytes(rt)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed randBytes: %v", err)
|
||||||
|
}
|
||||||
|
if len(actual) != rt {
|
||||||
|
t.Errorf("failed randBytes:\n\texpected: %d\n\t actual: %d\n", rt, len(actual))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTokenFromIDAndSecret(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
id string
|
||||||
|
secret string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"foo", "bar", "foo.bar"}, // should use default
|
||||||
|
{"abcdef", "abcdef0123456789", "abcdef.abcdef0123456789"},
|
||||||
|
{"h", "b", "h.b"},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
actual := TokenFromIDAndSecret(rt.id, rt.secret)
|
||||||
|
if actual != rt.expected {
|
||||||
|
t.Errorf(
|
||||||
|
"failed TokenFromIDAndSecret:\n\texpected: %s\n\t actual: %s",
|
||||||
|
rt.expected,
|
||||||
|
actual,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsValidBootstrapToken(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
token string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{token: "", expected: false},
|
||||||
|
{token: ".", expected: false},
|
||||||
|
{token: "1234567890123456789012", expected: false}, // invalid parcel size
|
||||||
|
{token: "12345.1234567890123456", expected: false}, // invalid parcel size
|
||||||
|
{token: ".1234567890123456", expected: false}, // invalid parcel size
|
||||||
|
{token: "123456.", expected: false}, // invalid parcel size
|
||||||
|
{token: "123456:1234567890.123456", expected: false}, // invalid separation
|
||||||
|
{token: "abcdef:1234567890123456", expected: false}, // invalid separation
|
||||||
|
{token: "Abcdef.1234567890123456", expected: false}, // invalid token id
|
||||||
|
{token: "123456.AABBCCDDEEFFGGHH", expected: false}, // invalid token secret
|
||||||
|
{token: "123456.AABBCCD-EEFFGGHH", expected: false}, // invalid character
|
||||||
|
{token: "abc*ef.1234567890123456", expected: false}, // invalid character
|
||||||
|
{token: "abcdef.1234567890123456", expected: true},
|
||||||
|
{token: "123456.aabbccddeeffgghh", expected: true},
|
||||||
|
{token: "ABCDEF.abcdef0123456789", expected: false},
|
||||||
|
{token: "abcdef.abcdef0123456789", expected: true},
|
||||||
|
{token: "123456.1234560123456789", expected: true},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
actual := IsValidBootstrapToken(rt.token)
|
||||||
|
if actual != rt.expected {
|
||||||
|
t.Errorf(
|
||||||
|
"failed IsValidBootstrapToken for the token %q\n\texpected: %t\n\t actual: %t",
|
||||||
|
rt.token,
|
||||||
|
rt.expected,
|
||||||
|
actual,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsValidBootstrapTokenID(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
tokenID string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{tokenID: "", expected: false},
|
||||||
|
{tokenID: "1234567890123456789012", expected: false},
|
||||||
|
{tokenID: "12345", expected: false},
|
||||||
|
{tokenID: "Abcdef", expected: false},
|
||||||
|
{tokenID: "ABCDEF", expected: false},
|
||||||
|
{tokenID: "abcdef.", expected: false},
|
||||||
|
{tokenID: "abcdef", expected: true},
|
||||||
|
{tokenID: "123456", expected: true},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
actual := IsValidBootstrapTokenID(rt.tokenID)
|
||||||
|
if actual != rt.expected {
|
||||||
|
t.Errorf(
|
||||||
|
"failed IsValidBootstrapTokenID for the token %q\n\texpected: %t\n\t actual: %t",
|
||||||
|
rt.tokenID,
|
||||||
|
rt.expected,
|
||||||
|
actual,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBootstrapTokenSecretName(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
tokenID string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"foo", "bootstrap-token-foo"},
|
||||||
|
{"bar", "bootstrap-token-bar"},
|
||||||
|
{"", "bootstrap-token-"},
|
||||||
|
{"abcdef", "bootstrap-token-abcdef"},
|
||||||
|
}
|
||||||
|
for _, rt := range tests {
|
||||||
|
actual := BootstrapTokenSecretName(rt.tokenID)
|
||||||
|
if actual != rt.expected {
|
||||||
|
t.Errorf(
|
||||||
|
"failed BootstrapTokenSecretName:\n\texpected: %s\n\t actual: %s",
|
||||||
|
rt.expected,
|
||||||
|
actual,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidateBootstrapGroupName(t *testing.T) {
|
func TestValidateBootstrapGroupName(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
Loading…
Reference in New Issue
Block a user