mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 20:53:33 +00:00
kubeadm: join ux changes
This commit is contained in:
parent
81d01a84e0
commit
1d37c6be49
@ -37,7 +37,12 @@ func KubeadmFuzzerFuncs(t apitesting.TestingCommon) []interface{} {
|
|||||||
func(obj *kubeadm.NodeConfiguration, c fuzz.Continue) {
|
func(obj *kubeadm.NodeConfiguration, c fuzz.Continue) {
|
||||||
c.FuzzNoCustom(obj)
|
c.FuzzNoCustom(obj)
|
||||||
obj.CACertPath = "foo"
|
obj.CACertPath = "foo"
|
||||||
obj.Discovery.Token = &kubeadm.TokenDiscovery{}
|
obj.CACertPath = "foo"
|
||||||
|
obj.DiscoveryFile = "foo"
|
||||||
|
obj.DiscoveryToken = "foo"
|
||||||
|
obj.DiscoveryTokenAPIServers = []string{"foo"}
|
||||||
|
obj.TLSBootstrapToken = "foo"
|
||||||
|
obj.Token = "foo"
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,8 +92,13 @@ type Etcd struct {
|
|||||||
type NodeConfiguration struct {
|
type NodeConfiguration struct {
|
||||||
metav1.TypeMeta
|
metav1.TypeMeta
|
||||||
|
|
||||||
Discovery Discovery
|
CACertPath string
|
||||||
CACertPath string
|
DiscoveryFile string
|
||||||
|
DiscoveryToken string
|
||||||
|
// Currently we only pay attention to one api server but hope to support >1 in the future
|
||||||
|
DiscoveryTokenAPIServers []string
|
||||||
|
TLSBootstrapToken string
|
||||||
|
Token string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClusterInfo TODO add description
|
// ClusterInfo TODO add description
|
||||||
|
@ -16,7 +16,11 @@ limitations under the License.
|
|||||||
|
|
||||||
package v1alpha1
|
package v1alpha1
|
||||||
|
|
||||||
import "k8s.io/apimachinery/pkg/runtime"
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultServiceDNSDomain = "cluster.local"
|
DefaultServiceDNSDomain = "cluster.local"
|
||||||
@ -68,4 +72,17 @@ func SetDefaults_NodeConfiguration(obj *NodeConfiguration) {
|
|||||||
if obj.CACertPath == "" {
|
if obj.CACertPath == "" {
|
||||||
obj.CACertPath = DefaultCACertPath
|
obj.CACertPath = DefaultCACertPath
|
||||||
}
|
}
|
||||||
|
if len(obj.TLSBootstrapToken) == 0 {
|
||||||
|
obj.TLSBootstrapToken = obj.Token
|
||||||
|
}
|
||||||
|
if len(obj.DiscoveryToken) == 0 && len(obj.DiscoveryFile) == 0 {
|
||||||
|
obj.DiscoveryToken = obj.Token
|
||||||
|
}
|
||||||
|
// Make sure file URLs become paths
|
||||||
|
if len(obj.DiscoveryFile) != 0 {
|
||||||
|
u, err := url.Parse(obj.DiscoveryFile)
|
||||||
|
if err == nil && u.Scheme == "file" {
|
||||||
|
obj.DiscoveryFile = u.Path
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,8 +82,12 @@ type Etcd struct {
|
|||||||
type NodeConfiguration struct {
|
type NodeConfiguration struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
|
||||||
Discovery Discovery `json:"discovery"`
|
CACertPath string `json:"caCertPath"`
|
||||||
CACertPath string `json:"caCertPath"`
|
DiscoveryFile string `json:"discoveryFile"`
|
||||||
|
DiscoveryToken string `json:"discoveryToken"`
|
||||||
|
DiscoveryTokenAPIServers []string `json:"discoveryTokenAPIServers"`
|
||||||
|
TLSBootstrapToken string `json:"tlsBootstrapToken"`
|
||||||
|
Token string `json:"token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClusterInfo TODO add description
|
// ClusterInfo TODO add description
|
||||||
|
@ -8,6 +8,17 @@ load(
|
|||||||
"go_test",
|
"go_test",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = ["validation_test.go"],
|
||||||
|
library = ":go_default_library",
|
||||||
|
tags = ["automanaged"],
|
||||||
|
deps = [
|
||||||
|
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = ["validation.go"],
|
srcs = ["validation.go"],
|
||||||
@ -15,6 +26,7 @@ go_library(
|
|||||||
deps = [
|
deps = [
|
||||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
"//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/token:go_default_library",
|
||||||
"//pkg/kubeapiserver/authorizer/modes:go_default_library",
|
"//pkg/kubeapiserver/authorizer/modes:go_default_library",
|
||||||
"//pkg/registry/core/service/ipallocator:go_default_library",
|
"//pkg/registry/core/service/ipallocator:go_default_library",
|
||||||
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
|
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
|
||||||
@ -33,14 +45,3 @@ filegroup(
|
|||||||
srcs = [":package-srcs"],
|
srcs = [":package-srcs"],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
)
|
)
|
||||||
|
|
||||||
go_test(
|
|
||||||
name = "go_default_test",
|
|
||||||
srcs = ["validation_test.go"],
|
|
||||||
library = ":go_default_library",
|
|
||||||
tags = ["automanaged"],
|
|
||||||
deps = [
|
|
||||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
|
||||||
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
@ -17,13 +17,17 @@ limitations under the License.
|
|||||||
package validation
|
package validation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
|
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
|
||||||
authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
|
authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
|
||||||
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
|
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
|
||||||
)
|
)
|
||||||
@ -45,7 +49,6 @@ var cloudproviders = []string{
|
|||||||
|
|
||||||
func ValidateMasterConfiguration(c *kubeadm.MasterConfiguration) field.ErrorList {
|
func ValidateMasterConfiguration(c *kubeadm.MasterConfiguration) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
allErrs = append(allErrs, ValidateDiscovery(&c.Discovery, field.NewPath("discovery"))...)
|
|
||||||
allErrs = append(allErrs, ValidateServiceSubnet(c.Networking.ServiceSubnet, field.NewPath("service subnet"))...)
|
allErrs = append(allErrs, ValidateServiceSubnet(c.Networking.ServiceSubnet, field.NewPath("service subnet"))...)
|
||||||
allErrs = append(allErrs, ValidateCloudProvider(c.CloudProvider, field.NewPath("cloudprovider"))...)
|
allErrs = append(allErrs, ValidateCloudProvider(c.CloudProvider, field.NewPath("cloudprovider"))...)
|
||||||
allErrs = append(allErrs, ValidateAuthorizationMode(c.AuthorizationMode, field.NewPath("authorization-mode"))...)
|
allErrs = append(allErrs, ValidateAuthorizationMode(c.AuthorizationMode, field.NewPath("authorization-mode"))...)
|
||||||
@ -54,70 +57,107 @@ func ValidateMasterConfiguration(c *kubeadm.MasterConfiguration) field.ErrorList
|
|||||||
|
|
||||||
func ValidateNodeConfiguration(c *kubeadm.NodeConfiguration) field.ErrorList {
|
func ValidateNodeConfiguration(c *kubeadm.NodeConfiguration) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
allErrs = append(allErrs, ValidateDiscovery(&c.Discovery, field.NewPath("discovery"))...)
|
allErrs = append(allErrs, ValidateDiscovery(c, field.NewPath("discovery"))...)
|
||||||
|
|
||||||
if !path.IsAbs(c.CACertPath) || !strings.HasSuffix(c.CACertPath, ".crt") {
|
if !path.IsAbs(c.CACertPath) || !strings.HasSuffix(c.CACertPath, ".crt") {
|
||||||
allErrs = append(allErrs, field.Invalid(field.NewPath("ca-cert-path"), nil, "the ca certificate path must be an absolute path"))
|
allErrs = append(allErrs, field.Invalid(field.NewPath("ca-cert-path"), c.CACertPath, "the ca certificate path must be an absolute path"))
|
||||||
}
|
|
||||||
return allErrs
|
|
||||||
}
|
|
||||||
|
|
||||||
func ValidateDiscovery(c *kubeadm.Discovery, fldPath *field.Path) field.ErrorList {
|
|
||||||
allErrs := field.ErrorList{}
|
|
||||||
var count int
|
|
||||||
if c.Token != nil {
|
|
||||||
allErrs = append(allErrs, ValidateTokenDiscovery(c.Token, fldPath)...)
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
if c.File != nil {
|
|
||||||
allErrs = append(allErrs, ValidateFileDiscovery(c.File, fldPath)...)
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
if c.HTTPS != nil {
|
|
||||||
allErrs = append(allErrs, ValidateHTTPSDiscovery(c.HTTPS, fldPath)...)
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
if count != 1 {
|
|
||||||
allErrs = append(allErrs, field.Invalid(fldPath, nil, "exactly one discovery strategy can be provided"))
|
|
||||||
}
|
|
||||||
return allErrs
|
|
||||||
}
|
|
||||||
|
|
||||||
func ValidateFileDiscovery(c *kubeadm.FileDiscovery, fldPath *field.Path) field.ErrorList {
|
|
||||||
allErrs := field.ErrorList{}
|
|
||||||
return allErrs
|
|
||||||
}
|
|
||||||
|
|
||||||
func ValidateHTTPSDiscovery(c *kubeadm.HTTPSDiscovery, fldPath *field.Path) field.ErrorList {
|
|
||||||
allErrs := field.ErrorList{}
|
|
||||||
return allErrs
|
|
||||||
}
|
|
||||||
|
|
||||||
func ValidateTokenDiscovery(c *kubeadm.TokenDiscovery, fldPath *field.Path) field.ErrorList {
|
|
||||||
allErrs := field.ErrorList{}
|
|
||||||
if len(c.ID) == 0 || len(c.Secret) == 0 {
|
|
||||||
allErrs = append(allErrs, field.Invalid(fldPath, nil, "token must be specific as <ID>:<Secret>"))
|
|
||||||
}
|
|
||||||
if len(c.Addresses) == 0 {
|
|
||||||
allErrs = append(allErrs, field.Invalid(fldPath, nil, "at least one address is required"))
|
|
||||||
}
|
}
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidateAuthorizationMode(authzMode string, fldPath *field.Path) field.ErrorList {
|
func ValidateAuthorizationMode(authzMode string, fldPath *field.Path) field.ErrorList {
|
||||||
if !authzmodes.IsValidAuthorizationMode(authzMode) {
|
if !authzmodes.IsValidAuthorizationMode(authzMode) {
|
||||||
return field.ErrorList{field.Invalid(fldPath, nil, "invalid authorization mode")}
|
return field.ErrorList{field.Invalid(fldPath, authzMode, "invalid authorization mode")}
|
||||||
}
|
}
|
||||||
return field.ErrorList{}
|
return field.ErrorList{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ValidateDiscovery(c *kubeadm.NodeConfiguration, fldPath *field.Path) field.ErrorList {
|
||||||
|
allErrs := field.ErrorList{}
|
||||||
|
if len(c.DiscoveryToken) != 0 {
|
||||||
|
allErrs = append(allErrs, ValidateToken(c.DiscoveryToken, fldPath)...)
|
||||||
|
}
|
||||||
|
if len(c.DiscoveryFile) != 0 {
|
||||||
|
allErrs = append(allErrs, ValidateDiscoveryFile(c.DiscoveryFile, fldPath)...)
|
||||||
|
}
|
||||||
|
allErrs = append(allErrs, ValidateArgSelection(c, fldPath)...)
|
||||||
|
allErrs = append(allErrs, ValidateToken(c.TLSBootstrapToken, fldPath)...)
|
||||||
|
allErrs = append(allErrs, ValidateJoinDiscoveryTokenAPIServer(c, fldPath)...)
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateArgSelection(cfg *kubeadm.NodeConfiguration, fldPath *field.Path) field.ErrorList {
|
||||||
|
allErrs := field.ErrorList{}
|
||||||
|
if len(cfg.DiscoveryToken) != 0 && len(cfg.DiscoveryFile) != 0 {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath, "", "DiscoveryToken and DiscoveryFile cannot both be set"))
|
||||||
|
}
|
||||||
|
if len(cfg.DiscoveryToken) == 0 && len(cfg.DiscoveryFile) == 0 {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath, "", "DiscoveryToken or DiscoveryFile must be set"))
|
||||||
|
}
|
||||||
|
if len(cfg.DiscoveryTokenAPIServers) < 1 && len(cfg.DiscoveryToken) != 0 {
|
||||||
|
allErrs = append(allErrs, field.Required(fldPath, "DiscoveryTokenAPIServers not set"))
|
||||||
|
}
|
||||||
|
// TODO remove once we support multiple api servers
|
||||||
|
if len(cfg.DiscoveryTokenAPIServers) > 1 {
|
||||||
|
fmt.Println("[validation] WARNING: kubeadm doesn't fully support multiple API Servers yet")
|
||||||
|
}
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateJoinDiscoveryTokenAPIServer(c *kubeadm.NodeConfiguration, fldPath *field.Path) field.ErrorList {
|
||||||
|
allErrs := field.ErrorList{}
|
||||||
|
for _, m := range c.DiscoveryTokenAPIServers {
|
||||||
|
_, _, err := net.SplitHostPort(m)
|
||||||
|
if err != nil {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath, m, err.Error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateDiscoveryFile(discoveryFile string, fldPath *field.Path) field.ErrorList {
|
||||||
|
allErrs := field.ErrorList{}
|
||||||
|
u, err := url.Parse(discoveryFile)
|
||||||
|
if err != nil {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath, discoveryFile, "not a valid HTTPS URL or a file on disk"))
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Scheme == "" {
|
||||||
|
// URIs with no scheme should be treated as files
|
||||||
|
if _, err := os.Stat(discoveryFile); os.IsNotExist(err) {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath, discoveryFile, "not a valid HTTPS URL or a file on disk"))
|
||||||
|
}
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Scheme != "https" {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath, discoveryFile, "if an URL is used, the scheme must be https"))
|
||||||
|
}
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateToken(t string, fldPath *field.Path) field.ErrorList {
|
||||||
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
|
id, secret, err := tokenutil.ParseToken(t)
|
||||||
|
if err != nil {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath, nil, err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(id) == 0 || len(secret) == 0 {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath, nil, "token must be of form '[a-z0-9]{6}.[a-z0-9]{16}'"))
|
||||||
|
}
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
func ValidateServiceSubnet(subnet string, fldPath *field.Path) field.ErrorList {
|
func ValidateServiceSubnet(subnet string, fldPath *field.Path) field.ErrorList {
|
||||||
_, svcSubnet, err := net.ParseCIDR(subnet)
|
_, svcSubnet, err := net.ParseCIDR(subnet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return field.ErrorList{field.Invalid(fldPath, nil, "couldn't parse the service subnet")}
|
return field.ErrorList{field.Invalid(fldPath, nil, "couldn't parse the service subnet")}
|
||||||
}
|
}
|
||||||
numAddresses := ipallocator.RangeSize(svcSubnet)
|
numAddresses := ipallocator.RangeSize(svcSubnet)
|
||||||
if numAddresses < kubeadmconstants.MinimumAddressesInServiceSubnet {
|
if numAddresses < constants.MinimumAddressesInServiceSubnet {
|
||||||
return field.ErrorList{field.Invalid(fldPath, nil, "service subnet is too small")}
|
return field.ErrorList{field.Invalid(fldPath, nil, "service subnet is too small")}
|
||||||
}
|
}
|
||||||
return field.ErrorList{}
|
return field.ErrorList{}
|
||||||
|
@ -25,17 +25,16 @@ import (
|
|||||||
|
|
||||||
func TestValidateTokenDiscovery(t *testing.T) {
|
func TestValidateTokenDiscovery(t *testing.T) {
|
||||||
var tests = []struct {
|
var tests = []struct {
|
||||||
c *kubeadm.TokenDiscovery
|
c *kubeadm.NodeConfiguration
|
||||||
f *field.Path
|
f *field.Path
|
||||||
expected bool
|
expected bool
|
||||||
}{
|
}{
|
||||||
{&kubeadm.TokenDiscovery{ID: "772ef5", Secret: "6b6baab1d4a0a171", Addresses: []string{"192.168.122.100:9898"}}, nil, true},
|
{&kubeadm.NodeConfiguration{Token: "772ef5.6b6baab1d4a0a171", DiscoveryTokenAPIServers: []string{"192.168.122.100:9898"}}, nil, true},
|
||||||
{&kubeadm.TokenDiscovery{ID: "", Secret: "6b6baab1d4a0a171", Addresses: []string{"192.168.122.100:9898"}}, nil, false},
|
{&kubeadm.NodeConfiguration{Token: ".6b6baab1d4a0a171", DiscoveryTokenAPIServers: []string{"192.168.122.100:9898"}}, nil, false},
|
||||||
{&kubeadm.TokenDiscovery{ID: "772ef5", Secret: "", Addresses: []string{"192.168.122.100:9898"}}, nil, false},
|
{&kubeadm.NodeConfiguration{Token: "772ef5.", DiscoveryTokenAPIServers: []string{"192.168.122.100:9898"}}, nil, false},
|
||||||
{&kubeadm.TokenDiscovery{ID: "772ef5", Secret: "6b6baab1d4a0a171", Addresses: []string{}}, nil, false},
|
|
||||||
}
|
}
|
||||||
for _, rt := range tests {
|
for _, rt := range tests {
|
||||||
err := ValidateTokenDiscovery(rt.c, 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 ValidateTokenDiscovery:\n\texpected: %t\n\t actual: %t",
|
||||||
@ -125,21 +124,6 @@ func TestValidateMasterConfiguration(t *testing.T) {
|
|||||||
expected bool
|
expected bool
|
||||||
}{
|
}{
|
||||||
{&kubeadm.MasterConfiguration{}, false},
|
{&kubeadm.MasterConfiguration{}, false},
|
||||||
{&kubeadm.MasterConfiguration{
|
|
||||||
Discovery: kubeadm.Discovery{
|
|
||||||
HTTPS: &kubeadm.HTTPSDiscovery{URL: "foo"},
|
|
||||||
File: &kubeadm.FileDiscovery{Path: "foo"},
|
|
||||||
Token: &kubeadm.TokenDiscovery{
|
|
||||||
ID: "abcdef",
|
|
||||||
Secret: "1234567890123456",
|
|
||||||
Addresses: []string{"foobar"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
AuthorizationMode: "RBAC",
|
|
||||||
Networking: kubeadm.Networking{
|
|
||||||
ServiceSubnet: "10.96.0.1/12",
|
|
||||||
},
|
|
||||||
}, false},
|
|
||||||
{&kubeadm.MasterConfiguration{
|
{&kubeadm.MasterConfiguration{
|
||||||
Discovery: kubeadm.Discovery{
|
Discovery: kubeadm.Discovery{
|
||||||
HTTPS: &kubeadm.HTTPSDiscovery{URL: "foo"},
|
HTTPS: &kubeadm.HTTPSDiscovery{URL: "foo"},
|
||||||
@ -191,45 +175,10 @@ func TestValidateNodeConfiguration(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{&kubeadm.NodeConfiguration{}, false},
|
{&kubeadm.NodeConfiguration{}, false},
|
||||||
{&kubeadm.NodeConfiguration{
|
{&kubeadm.NodeConfiguration{
|
||||||
Discovery: kubeadm.Discovery{
|
DiscoveryFile: "foo",
|
||||||
HTTPS: &kubeadm.HTTPSDiscovery{URL: "foo"},
|
DiscoveryToken: "abcdef.1234567890123456@foobar",
|
||||||
File: &kubeadm.FileDiscovery{Path: "foo"},
|
CACertPath: "/some/cert.crt",
|
||||||
Token: &kubeadm.TokenDiscovery{
|
|
||||||
ID: "abcdef",
|
|
||||||
Secret: "1234567890123456",
|
|
||||||
Addresses: []string{"foobar"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
CACertPath: "/some/cert.crt",
|
|
||||||
}, false},
|
}, false},
|
||||||
{&kubeadm.NodeConfiguration{
|
|
||||||
Discovery: kubeadm.Discovery{
|
|
||||||
HTTPS: &kubeadm.HTTPSDiscovery{URL: "foo"},
|
|
||||||
},
|
|
||||||
CACertPath: "/some/path", // no .crt suffix
|
|
||||||
}, false},
|
|
||||||
{&kubeadm.NodeConfiguration{
|
|
||||||
Discovery: kubeadm.Discovery{
|
|
||||||
HTTPS: &kubeadm.HTTPSDiscovery{URL: "foo"},
|
|
||||||
},
|
|
||||||
CACertPath: "/some/cert.crt",
|
|
||||||
}, true},
|
|
||||||
{&kubeadm.NodeConfiguration{
|
|
||||||
Discovery: kubeadm.Discovery{
|
|
||||||
File: &kubeadm.FileDiscovery{Path: "foo"},
|
|
||||||
},
|
|
||||||
CACertPath: "/some/other/cert.crt",
|
|
||||||
}, true},
|
|
||||||
{&kubeadm.NodeConfiguration{
|
|
||||||
Discovery: kubeadm.Discovery{
|
|
||||||
Token: &kubeadm.TokenDiscovery{
|
|
||||||
ID: "abcdef",
|
|
||||||
Secret: "1234567890123456",
|
|
||||||
Addresses: []string{"foobar"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
CACertPath: "/a/third/cert.crt",
|
|
||||||
}, true},
|
|
||||||
}
|
}
|
||||||
for _, rt := range tests {
|
for _, rt := range tests {
|
||||||
actual := ValidateNodeConfiguration(rt.s)
|
actual := ValidateNodeConfiguration(rt.s)
|
||||||
|
@ -52,37 +52,76 @@ var (
|
|||||||
|
|
||||||
// NewCmdJoin returns "kubeadm join" command.
|
// NewCmdJoin returns "kubeadm join" command.
|
||||||
func NewCmdJoin(out io.Writer) *cobra.Command {
|
func NewCmdJoin(out io.Writer) *cobra.Command {
|
||||||
versioned := &kubeadmapiext.NodeConfiguration{}
|
cfg := &kubeadmapiext.NodeConfiguration{}
|
||||||
api.Scheme.Default(versioned)
|
api.Scheme.Default(cfg)
|
||||||
cfg := kubeadmapi.NodeConfiguration{}
|
|
||||||
api.Scheme.Convert(versioned, &cfg, nil)
|
|
||||||
|
|
||||||
var skipPreFlight bool
|
var skipPreFlight bool
|
||||||
var cfgPath string
|
var cfgPath string
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "join <master address>",
|
Use: "join <flags> [DiscoveryTokenAPIServers]",
|
||||||
Short: "Run this on any machine you wish to join an existing cluster",
|
Short: "Run this on any machine you wish to join an existing cluster",
|
||||||
|
Long: dedent.Dedent(`
|
||||||
|
When joining a kubeadm initialized cluster, we need to establish
|
||||||
|
bidirectional trust. This is split into discovery (having the Node
|
||||||
|
trust the Kubernetes Master) and TLS bootstrap (having the Kubernetes
|
||||||
|
Master trust the Node).
|
||||||
|
|
||||||
|
There are 2 main schemes for discovery. The first is to use a shared
|
||||||
|
token along with the IP address of the API server. The second is to
|
||||||
|
provide a file (a subset of the standard kubeconfig file). This file
|
||||||
|
can be a local file or downloaded via an HTTPS URL. The forms are
|
||||||
|
kubeadm join --discovery-token abcdef.1234567890abcdef 1.2.3.4:6443,
|
||||||
|
kubeadm join --discovery-file path/to/file.conf or kubeadm join
|
||||||
|
--discovery-file https://url/file.conf. Only one form can be used. If
|
||||||
|
the discovery information is loaded from a URL, HTTPS must be used and
|
||||||
|
the host installed CA bundle is used to verify the connection.
|
||||||
|
|
||||||
|
The TLS bootstrap mechanism is also driven via a shared token. This is
|
||||||
|
used to temporarily authenticate with the Kubernetes Master to submit a
|
||||||
|
certificate signing request (CSR) for a locally created key pair. By
|
||||||
|
default kubeadm will set up the Kubernetes Master to automatically
|
||||||
|
approve these signing requests. This token is passed in with the
|
||||||
|
--tls-bootstrap-token abcdef.1234567890abcdef flag.
|
||||||
|
|
||||||
|
Often times the same token is use for both parts. In this case, the
|
||||||
|
--token flag can be used instead of specifying the each token
|
||||||
|
individually.
|
||||||
|
`),
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
j, err := NewJoin(cfgPath, args, &cfg, skipPreFlight)
|
cfg.DiscoveryTokenAPIServers = args
|
||||||
|
api.Scheme.Default(cfg)
|
||||||
|
internalcfg := &kubeadmapi.NodeConfiguration{}
|
||||||
|
api.Scheme.Convert(cfg, internalcfg, nil)
|
||||||
|
j, err := NewJoin(cfgPath, args, internalcfg, skipPreFlight)
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
kubeadmutil.CheckErr(j.Validate())
|
kubeadmutil.CheckErr(j.Validate())
|
||||||
kubeadmutil.CheckErr(j.Run(out))
|
kubeadmutil.CheckErr(j.Run(out))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.PersistentFlags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file")
|
cmd.PersistentFlags().StringVar(
|
||||||
|
&cfgPath, "config", cfgPath,
|
||||||
|
"Path to kubeadm config file")
|
||||||
|
|
||||||
|
cmd.PersistentFlags().StringVar(
|
||||||
|
&cfg.DiscoveryFile, "discovery-file", "",
|
||||||
|
"A file or url from which to load cluster information")
|
||||||
|
cmd.PersistentFlags().StringVar(
|
||||||
|
&cfg.DiscoveryToken, "discovery-token", "",
|
||||||
|
"A token used to validate cluster information fetched from the master")
|
||||||
|
cmd.PersistentFlags().StringVar(
|
||||||
|
&cfg.TLSBootstrapToken, "tls-bootstrap-token", "",
|
||||||
|
"A token used for TLS bootstrapping")
|
||||||
|
cmd.PersistentFlags().StringVar(
|
||||||
|
&cfg.Token, "token", "",
|
||||||
|
"Use this token for both discovery-token and tls-bootstrap-token")
|
||||||
|
|
||||||
cmd.PersistentFlags().BoolVar(
|
cmd.PersistentFlags().BoolVar(
|
||||||
&skipPreFlight, "skip-preflight-checks", false,
|
&skipPreFlight, "skip-preflight-checks", false,
|
||||||
"skip preflight checks normally run before modifying the system",
|
"skip preflight checks normally run before modifying the system",
|
||||||
)
|
)
|
||||||
|
|
||||||
cmd.PersistentFlags().Var(
|
|
||||||
discovery.NewDiscoveryValue(&cfg.Discovery), "discovery",
|
|
||||||
"The discovery method kubeadm will use for connecting nodes to the master",
|
|
||||||
)
|
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +170,7 @@ func (j *Join) Validate() error {
|
|||||||
|
|
||||||
// Run executes worker node provisioning and tries to join an existing cluster.
|
// Run executes worker node provisioning and tries to join an existing cluster.
|
||||||
func (j *Join) Run(out io.Writer) error {
|
func (j *Join) Run(out io.Writer) error {
|
||||||
cfg, err := discovery.For(j.cfg.Discovery)
|
cfg, err := discovery.For(j.cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
@ -29,27 +30,34 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// For identifies and executes the desired discovery mechanism.
|
// For identifies and executes the desired discovery mechanism.
|
||||||
func For(d kubeadmapi.Discovery) (*clientcmdapi.Config, error) {
|
func For(d *kubeadmapi.NodeConfiguration) (*clientcmdapi.Config, error) {
|
||||||
switch {
|
switch {
|
||||||
case d.File != nil:
|
case len(d.DiscoveryFile) != 0:
|
||||||
return runFileDiscovery(d.File)
|
if isHTTPSURL(d.DiscoveryFile) {
|
||||||
case d.HTTPS != nil:
|
return runHTTPSDiscovery(d.DiscoveryFile)
|
||||||
return runHTTPSDiscovery(d.HTTPS)
|
}
|
||||||
case d.Token != nil:
|
return runFileDiscovery(d.DiscoveryFile)
|
||||||
return runTokenDiscovery(d.Token)
|
case len(d.DiscoveryToken) != 0:
|
||||||
|
return runTokenDiscovery(d.DiscoveryToken, d.DiscoveryTokenAPIServers)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("couldn't find a valid discovery configuration.")
|
return nil, fmt.Errorf("couldn't find a valid discovery configuration.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isHTTPSURL checks whether the string is parsable as an URL
|
||||||
|
func isHTTPSURL(s string) bool {
|
||||||
|
u, err := url.Parse(s)
|
||||||
|
return err == nil && u.Scheme == "https"
|
||||||
|
}
|
||||||
|
|
||||||
// runFileDiscovery executes file-based discovery.
|
// runFileDiscovery executes file-based discovery.
|
||||||
func runFileDiscovery(fd *kubeadmapi.FileDiscovery) (*clientcmdapi.Config, error) {
|
func runFileDiscovery(fd string) (*clientcmdapi.Config, error) {
|
||||||
return clientcmd.LoadFromFile(fd.Path)
|
return clientcmd.LoadFromFile(fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// runHTTPSDiscovery executes HTTPS-based discovery.
|
// runHTTPSDiscovery executes HTTPS-based discovery.
|
||||||
func runHTTPSDiscovery(hd *kubeadmapi.HTTPSDiscovery) (*clientcmdapi.Config, error) {
|
func runHTTPSDiscovery(hd string) (*clientcmdapi.Config, error) {
|
||||||
response, err := http.Get(hd.URL)
|
response, err := http.Get(hd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -64,17 +72,23 @@ func runHTTPSDiscovery(hd *kubeadmapi.HTTPSDiscovery) (*clientcmdapi.Config, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// runTokenDiscovery executes token-based discovery.
|
// runTokenDiscovery executes token-based discovery.
|
||||||
func runTokenDiscovery(td *kubeadmapi.TokenDiscovery) (*clientcmdapi.Config, error) {
|
func runTokenDiscovery(td string, m []string) (*clientcmdapi.Config, error) {
|
||||||
if valid, err := tokenutil.ValidateToken(td); valid == false {
|
id, secret, err := tokenutil.ParseToken(td)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t := &kubeadmapi.TokenDiscovery{ID: id, Secret: secret, Addresses: m}
|
||||||
|
|
||||||
|
if valid, err := tokenutil.ValidateToken(t); valid == false {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
clusterInfo, err := kubenode.RetrieveTrustedClusterInfo(td)
|
clusterInfo, err := kubenode.RetrieveTrustedClusterInfo(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := kubenode.EstablishMasterConnection(td, clusterInfo)
|
cfg, err := kubenode.EstablishMasterConnection(t, clusterInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -24,40 +24,37 @@ import (
|
|||||||
|
|
||||||
func TestFor(t *testing.T) {
|
func TestFor(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
d kubeadm.Discovery
|
d kubeadm.NodeConfiguration
|
||||||
expect bool
|
expect bool
|
||||||
}{
|
}{
|
||||||
{d: kubeadm.Discovery{}, expect: false},
|
{d: kubeadm.NodeConfiguration{}, expect: false},
|
||||||
{
|
{
|
||||||
d: kubeadm.Discovery{
|
d: kubeadm.NodeConfiguration{
|
||||||
HTTPS: &kubeadm.HTTPSDiscovery{URL: "notnil"},
|
DiscoveryFile: "notnil",
|
||||||
},
|
},
|
||||||
expect: false,
|
expect: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
d: kubeadm.Discovery{
|
d: kubeadm.NodeConfiguration{
|
||||||
HTTPS: &kubeadm.HTTPSDiscovery{URL: "http://localhost"},
|
DiscoveryFile: "https://localhost",
|
||||||
},
|
},
|
||||||
expect: false,
|
expect: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
d: kubeadm.Discovery{
|
d: kubeadm.NodeConfiguration{
|
||||||
File: &kubeadm.FileDiscovery{Path: "notnil"},
|
DiscoveryFile: "notnil",
|
||||||
},
|
},
|
||||||
expect: false,
|
expect: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
d: kubeadm.Discovery{
|
d: kubeadm.NodeConfiguration{
|
||||||
Token: &kubeadm.TokenDiscovery{
|
DiscoveryToken: "foo.bar@foobar",
|
||||||
ID: "foo",
|
},
|
||||||
Secret: "bar",
|
expect: false,
|
||||||
Addresses: []string{"foobar"},
|
|
||||||
},
|
|
||||||
}, expect: false,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, rt := range tests {
|
for _, rt := range tests {
|
||||||
_, actual := For(rt.d)
|
_, actual := For(&rt.d)
|
||||||
if (actual == nil) != rt.expect {
|
if (actual == nil) != rt.expect {
|
||||||
t.Errorf(
|
t.Errorf(
|
||||||
"failed For:\n\texpected: %t\n\t actual: %t",
|
"failed For:\n\texpected: %t\n\t actual: %t",
|
||||||
|
@ -46,32 +46,3 @@ func TestCmdJoinConfig(t *testing.T) {
|
|||||||
kubeadmReset()
|
kubeadmReset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCmdJoinDiscovery(t *testing.T) {
|
|
||||||
if *kubeadmCmdSkip {
|
|
||||||
t.Log("kubeadm cmd tests being skipped")
|
|
||||||
t.Skip()
|
|
||||||
}
|
|
||||||
|
|
||||||
var initTest = []struct {
|
|
||||||
args string
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{"--discovery=foobar", false},
|
|
||||||
{"--dicovery=magic", false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rt := range initTest {
|
|
||||||
_, _, actual := RunCmd(*kubeadmPath, "join", rt.args, "--skip-preflight-checks")
|
|
||||||
if (actual == nil) != rt.expected {
|
|
||||||
t.Errorf(
|
|
||||||
"failed CmdJoinDiscovery running 'kubeadm join %s' with an error: %v\n\texpected: %t\n\t actual: %t",
|
|
||||||
rt.args,
|
|
||||||
actual,
|
|
||||||
rt.expected,
|
|
||||||
(actual == nil),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
kubeadmReset()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -154,7 +154,9 @@ disable-attach-detach-reconcile-sync
|
|||||||
disable-filter
|
disable-filter
|
||||||
disable-kubenet
|
disable-kubenet
|
||||||
disable-log-dump
|
disable-log-dump
|
||||||
|
discovery-file
|
||||||
discovery-port
|
discovery-port
|
||||||
|
discovery-token
|
||||||
dns-bind-address
|
dns-bind-address
|
||||||
dns-port
|
dns-port
|
||||||
dns-provider
|
dns-provider
|
||||||
@ -647,6 +649,7 @@ tcp-services
|
|||||||
terminated-pod-gc-threshold
|
terminated-pod-gc-threshold
|
||||||
test-flags
|
test-flags
|
||||||
test-timeout
|
test-timeout
|
||||||
|
tls-bootstrap-token
|
||||||
tls-ca-file
|
tls-ca-file
|
||||||
tls-cert-file
|
tls-cert-file
|
||||||
tls-private-key-file
|
tls-private-key-file
|
||||||
@ -666,6 +669,8 @@ upgrade-image
|
|||||||
upgrade-target
|
upgrade-target
|
||||||
use-kubernetes-cluster-service
|
use-kubernetes-cluster-service
|
||||||
use-kubernetes-version
|
use-kubernetes-version
|
||||||
|
use-service-account-credentials
|
||||||
|
use-taint-based-evictions
|
||||||
user-whitelist
|
user-whitelist
|
||||||
use-service-account-credentials
|
use-service-account-credentials
|
||||||
use-service-account-credentials
|
use-service-account-credentials
|
||||||
|
Loading…
Reference in New Issue
Block a user