Replace godep with dep

This commit is contained in:
Manuel de Brito Fontes 2017-10-06 17:26:14 -03:00
parent 1e7489927c
commit bf5616c65b
14883 changed files with 3937406 additions and 361781 deletions

54
vendor/k8s.io/kubernetes/cmd/kubeadm/app/BUILD generated vendored Normal file
View file

@ -0,0 +1,54 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["kubeadm.go"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm/install:go_default_library",
"//cmd/kubeadm/app/cmd:go_default_library",
"//vendor/github.com/spf13/pflag:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//cmd/kubeadm/app/apis/kubeadm:all-srcs",
"//cmd/kubeadm/app/cmd:all-srcs",
"//cmd/kubeadm/app/constants:all-srcs",
"//cmd/kubeadm/app/discovery:all-srcs",
"//cmd/kubeadm/app/features:all-srcs",
"//cmd/kubeadm/app/images:all-srcs",
"//cmd/kubeadm/app/node:all-srcs",
"//cmd/kubeadm/app/phases/addons/dns:all-srcs",
"//cmd/kubeadm/app/phases/addons/proxy:all-srcs",
"//cmd/kubeadm/app/phases/apiconfig:all-srcs",
"//cmd/kubeadm/app/phases/bootstraptoken/clusterinfo:all-srcs",
"//cmd/kubeadm/app/phases/bootstraptoken/node:all-srcs",
"//cmd/kubeadm/app/phases/certs:all-srcs",
"//cmd/kubeadm/app/phases/controlplane:all-srcs",
"//cmd/kubeadm/app/phases/etcd:all-srcs",
"//cmd/kubeadm/app/phases/kubeconfig:all-srcs",
"//cmd/kubeadm/app/phases/markmaster:all-srcs",
"//cmd/kubeadm/app/phases/selfhosting:all-srcs",
"//cmd/kubeadm/app/phases/token:all-srcs",
"//cmd/kubeadm/app/phases/upgrade:all-srcs",
"//cmd/kubeadm/app/phases/uploadconfig:all-srcs",
"//cmd/kubeadm/app/preflight:all-srcs",
"//cmd/kubeadm/app/util:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -0,0 +1,42 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"register.go",
"types.go",
"well_known_labels.go",
"zz_generated.deepcopy.go",
],
deps = [
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1: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/schema:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//cmd/kubeadm/app/apis/kubeadm/fuzzer:all-srcs",
"//cmd/kubeadm/app/apis/kubeadm/install:all-srcs",
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:all-srcs",
"//cmd/kubeadm/app/apis/kubeadm/validation:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -0,0 +1,19 @@
/*
Copyright 2016 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.
*/
// +k8s:deepcopy-gen=package,register
// +groupName=kubeadm.k8s.io
package kubeadm // import "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"

View file

@ -0,0 +1,29 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["fuzzer.go"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//vendor/github.com/google/gofuzz:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,58 @@
/*
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 fuzzer
import (
"github.com/google/gofuzz"
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
// Funcs returns the fuzzer functions for the kubeadm apis.
func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
return []interface{}{
func(obj *kubeadm.MasterConfiguration, c fuzz.Continue) {
c.FuzzNoCustom(obj)
obj.KubernetesVersion = "v10"
obj.API.BindPort = 20
obj.API.AdvertiseAddress = "foo"
obj.Networking.ServiceSubnet = "foo"
obj.Networking.DNSDomain = "foo"
obj.AuthorizationModes = []string{"foo"}
obj.CertificatesDir = "foo"
obj.APIServerCertSANs = []string{}
obj.Token = "foo"
obj.Etcd.Image = "foo"
obj.Etcd.DataDir = "foo"
obj.ImageRepository = "foo"
obj.CIImageRepository = ""
obj.UnifiedControlPlaneImage = "foo"
obj.FeatureGates = map[string]bool{}
},
func(obj *kubeadm.NodeConfiguration, c fuzz.Continue) {
c.FuzzNoCustom(obj)
obj.CACertPath = "foo"
obj.CACertPath = "foo"
obj.DiscoveryFile = "foo"
obj.DiscoveryToken = "foo"
obj.DiscoveryTokenAPIServers = []string{"foo"}
obj.TLSBootstrapToken = "foo"
obj.Token = "foo"
},
}
}

View file

@ -0,0 +1,46 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"install.go",
],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
"//pkg/api:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apimachinery/announced:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apimachinery/registered:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
go_test(
name = "go_default_test",
srcs = ["install_test.go"],
library = ":go_default_library",
deps = [
"//cmd/kubeadm/app/apis/kubeadm/fuzzer:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/testing/roundtrip:go_default_library",
],
)

View file

@ -0,0 +1,17 @@
/*
Copyright 2016 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 install // import "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/install"

View file

@ -0,0 +1,46 @@
/*
Copyright 2016 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 install
import (
"k8s.io/apimachinery/pkg/apimachinery/announced"
"k8s.io/apimachinery/pkg/apimachinery/registered"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/pkg/api"
)
func init() {
Install(api.GroupFactoryRegistry, api.Registry, api.Scheme)
}
// Install registers the API group and adds types to a scheme
func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *registered.APIRegistrationManager, scheme *runtime.Scheme) {
if err := announced.NewGroupMetaFactory(
&announced.GroupMetaFactoryArgs{
GroupName: kubeadm.GroupName,
VersionPreferenceOrder: []string{v1alpha1.SchemeGroupVersion.Version},
AddInternalObjectsToScheme: kubeadm.AddToScheme,
},
announced.VersionToSchemeFunc{
v1alpha1.SchemeGroupVersion.Version: v1alpha1.AddToScheme,
},
).Announce(groupFactoryRegistry).RegisterAndEnable(registry, scheme); err != nil {
panic(err)
}
}

View file

@ -0,0 +1,28 @@
/*
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 install
import (
"testing"
"k8s.io/apimachinery/pkg/api/testing/roundtrip"
kubeadmfuzzer "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/fuzzer"
)
func TestRoundTripTypes(t *testing.T) {
roundtrip.RoundTripTestForAPIGroup(t, Install, kubeadmfuzzer.Funcs)
}

View file

@ -0,0 +1,51 @@
/*
Copyright 2016 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 (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// GroupName is the group name use in this package
const GroupName = "kubeadm.k8s.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
// Kind takes an unqualified kind and returns a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&MasterConfiguration{},
&NodeConfiguration{},
)
return nil
}

View file

@ -0,0 +1,132 @@
/*
Copyright 2016 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 (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type MasterConfiguration struct {
metav1.TypeMeta
API API
Etcd Etcd
Networking Networking
KubernetesVersion string
CloudProvider string
NodeName string
AuthorizationModes []string
Token string
TokenTTL metav1.Duration
APIServerExtraArgs map[string]string
ControllerManagerExtraArgs map[string]string
SchedulerExtraArgs map[string]string
// APIServerCertSANs sets extra Subject Alternative Names for the API Server signing cert
APIServerCertSANs []string
// CertificatesDir specifies where to store or look for all required certificates
CertificatesDir string
// ImageRepository what container registry to pull control plane images from
ImageRepository string
// Container registry for core images generated by CI
// +k8s:conversion-gen=false
CIImageRepository string
// UnifiedControlPlaneImage specifies if a specific container image should be used for all control plane components
UnifiedControlPlaneImage string
// FeatureGates enabled by the user
FeatureGates map[string]bool
}
type API struct {
// AdvertiseAddress sets the address for the API server to advertise.
AdvertiseAddress string
// BindPort sets the secure port for the API Server to bind to
BindPort int32
}
type TokenDiscovery struct {
ID string
Secret string
Addresses []string
}
type Networking struct {
ServiceSubnet string
PodSubnet string
DNSDomain string
}
type Etcd struct {
Endpoints []string
CAFile string
CertFile string
KeyFile string
DataDir string
ExtraArgs map[string]string
// Image specifies which container image to use for running etcd. If empty, automatically populated by kubeadm using the image repository and default etcd version
Image string
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type NodeConfiguration struct {
metav1.TypeMeta
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
NodeName string
TLSBootstrapToken string
Token string
// DiscoveryTokenCACertHashes specifies a set of public key pins to verify
// when token-based discovery is used. The root CA found during discovery
// must match one of these values. Specifying an empty set disables root CA
// pinning, which can be unsafe. Each hash is specified as "<type>:<value>",
// where the only currently supported type is "sha256". This is a hex-encoded
// SHA-256 hash of the Subject Public Key Info (SPKI) object in DER-encoded
// ASN.1. These hashes can be calculated using, for example, OpenSSL:
// openssl x509 -pubkey -in ca.crt openssl rsa -pubin -outform der 2>&/dev/null | openssl dgst -sha256 -hex
DiscoveryTokenCACertHashes []string
// DiscoveryTokenUnsafeSkipCAVerification allows token-based discovery
// without CA verification via DiscoveryTokenCACertHashes. This can weaken
// the security of kubeadm since other nodes can impersonate the master.
DiscoveryTokenUnsafeSkipCAVerification bool
}
// GetControlPlaneImageRepository returns name of image repository
// for control plane images (API,Controller Manager,Scheduler and Proxy)
// It will override location with CI registry name in case user requests special
// Kubernetes version from CI build area.
// (See: kubeadmconstants.DefaultCIImageRepository)
func (cfg *MasterConfiguration) GetControlPlaneImageRepository() string {
if cfg.CIImageRepository != "" {
return cfg.CIImageRepository
}
return cfg.ImageRepository
}

View file

@ -0,0 +1,40 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"defaults.go",
"doc.go",
"register.go",
"types.go",
"zz_generated.conversion.go",
"zz_generated.deepcopy.go",
"zz_generated.defaults.go",
],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1: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/schema:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,101 @@
/*
Copyright 2016 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 v1alpha1
import (
"net/url"
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
)
const (
DefaultServiceDNSDomain = "cluster.local"
DefaultServicesSubnet = "10.96.0.0/12"
DefaultKubernetesVersion = "stable-1.8"
DefaultAPIBindPort = 6443
DefaultAuthorizationModes = "Node,RBAC"
DefaultCACertPath = "/etc/kubernetes/pki/ca.crt"
DefaultCertificatesDir = "/etc/kubernetes/pki"
DefaultEtcdDataDir = "/var/lib/etcd"
DefaultImageRepository = "gcr.io/google_containers"
)
func addDefaultingFuncs(scheme *runtime.Scheme) error {
return RegisterDefaults(scheme)
}
func SetDefaults_MasterConfiguration(obj *MasterConfiguration) {
if obj.KubernetesVersion == "" {
obj.KubernetesVersion = DefaultKubernetesVersion
}
if obj.API.BindPort == 0 {
obj.API.BindPort = DefaultAPIBindPort
}
if obj.Networking.ServiceSubnet == "" {
obj.Networking.ServiceSubnet = DefaultServicesSubnet
}
if obj.Networking.DNSDomain == "" {
obj.Networking.DNSDomain = DefaultServiceDNSDomain
}
if len(obj.AuthorizationModes) == 0 {
obj.AuthorizationModes = strings.Split(DefaultAuthorizationModes, ",")
}
if obj.CertificatesDir == "" {
obj.CertificatesDir = DefaultCertificatesDir
}
if obj.TokenTTL.Duration == 0 {
obj.TokenTTL = metav1.Duration{
Duration: constants.DefaultTokenDuration,
}
}
if obj.ImageRepository == "" {
obj.ImageRepository = DefaultImageRepository
}
if obj.Etcd.DataDir == "" {
obj.Etcd.DataDir = DefaultEtcdDataDir
}
}
func SetDefaults_NodeConfiguration(obj *NodeConfiguration) {
if obj.CACertPath == "" {
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
}
}
}

View file

@ -0,0 +1,21 @@
/*
Copyright 2016 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.
*/
// +k8s:defaulter-gen=TypeMeta
// +groupName=kubeadm.k8s.io
// +k8s:deepcopy-gen=package
// +k8s:conversion-gen=k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm
package v1alpha1 // import "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"

View file

@ -0,0 +1,63 @@
/*
Copyright 2016 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 v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// GroupName is the group name use in this package
const GroupName = "kubeadm.k8s.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
var (
// TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api.
// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
)
func init() {
// We only register manually written functions here. The registration of the
// generated functions takes place in the generated files. The separation
// makes the code compile even when the generated files are missing.
localSchemeBuilder.Register(addKnownTypes, addDefaultingFuncs)
}
// Kind takes an unqualified kind and returns a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&MasterConfiguration{},
&NodeConfiguration{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}

View file

@ -0,0 +1,114 @@
/*
Copyright 2016 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 v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type MasterConfiguration struct {
metav1.TypeMeta `json:",inline"`
API API `json:"api"`
Etcd Etcd `json:"etcd"`
Networking Networking `json:"networking"`
KubernetesVersion string `json:"kubernetesVersion"`
CloudProvider string `json:"cloudProvider"`
NodeName string `json:"nodeName"`
AuthorizationModes []string `json:"authorizationModes,omitempty"`
Token string `json:"token"`
TokenTTL metav1.Duration `json:"tokenTTL"`
APIServerExtraArgs map[string]string `json:"apiServerExtraArgs,omitempty"`
ControllerManagerExtraArgs map[string]string `json:"controllerManagerExtraArgs,omitempty"`
SchedulerExtraArgs map[string]string `json:"schedulerExtraArgs,omitempty"`
// APIServerCertSANs sets extra Subject Alternative Names for the API Server signing cert
APIServerCertSANs []string `json:"apiServerCertSANs,omitempty"`
// CertificatesDir specifies where to store or look for all required certificates
CertificatesDir string `json:"certificatesDir"`
// ImageRepository what container registry to pull control plane images from
ImageRepository string `json:"imageRepository"`
// UnifiedControlPlaneImage specifies if a specific container image should be used for all control plane components
UnifiedControlPlaneImage string `json:"unifiedControlPlaneImage"`
// FeatureGates enabled by the user
FeatureGates map[string]bool `json:"featureGates,omitempty"`
}
type API struct {
// AdvertiseAddress sets the address for the API server to advertise.
AdvertiseAddress string `json:"advertiseAddress"`
// BindPort sets the secure port for the API Server to bind to
BindPort int32 `json:"bindPort"`
}
type TokenDiscovery struct {
ID string `json:"id"`
Secret string `json:"secret"`
Addresses []string `json:"addresses"`
}
type Networking struct {
ServiceSubnet string `json:"serviceSubnet"`
PodSubnet string `json:"podSubnet"`
DNSDomain string `json:"dnsDomain"`
}
type Etcd struct {
Endpoints []string `json:"endpoints"`
CAFile string `json:"caFile"`
CertFile string `json:"certFile"`
KeyFile string `json:"keyFile"`
DataDir string `json:"dataDir"`
ExtraArgs map[string]string `json:"extraArgs,omitempty"`
// Image specifies which container image to use for running etcd. If empty, automatically populated by kubeadm using the image repository and default etcd version
Image string `json:"image"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type NodeConfiguration struct {
metav1.TypeMeta `json:",inline"`
CACertPath string `json:"caCertPath"`
DiscoveryFile string `json:"discoveryFile"`
DiscoveryToken string `json:"discoveryToken"`
DiscoveryTokenAPIServers []string `json:"discoveryTokenAPIServers,omitempty"`
NodeName string `json:"nodeName"`
TLSBootstrapToken string `json:"tlsBootstrapToken"`
Token string `json:"token"`
// DiscoveryTokenCACertHashes specifies a set of public key pins to verify
// when token-based discovery is used. The root CA found during discovery
// must match one of these values. Specifying an empty set disables root CA
// pinning, which can be unsafe. Each hash is specified as "<type>:<value>",
// where the only currently supported type is "sha256". This is a hex-encoded
// SHA-256 hash of the Subject Public Key Info (SPKI) object in DER-encoded
// ASN.1. These hashes can be calculated using, for example, OpenSSL:
// openssl x509 -pubkey -in ca.crt openssl rsa -pubin -outform der 2>&/dev/null | openssl dgst -sha256 -hex
DiscoveryTokenCACertHashes []string `json:"discoveryTokenCACertHashes,omitempty"`
// DiscoveryTokenUnsafeSkipCAVerification allows token-based discovery
// without CA verification via DiscoveryTokenCACertHashes. This can weaken
// the security of kubeadm since other nodes can impersonate the master.
DiscoveryTokenUnsafeSkipCAVerification bool `json:"discoveryTokenUnsafeSkipCAVerification"`
}

View file

@ -0,0 +1,254 @@
// +build !ignore_autogenerated
/*
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.
*/
// This file was autogenerated by conversion-gen. Do not edit it manually!
package v1alpha1
import (
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
unsafe "unsafe"
)
func init() {
localSchemeBuilder.Register(RegisterConversions)
}
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(scheme *runtime.Scheme) error {
return scheme.AddGeneratedConversionFuncs(
Convert_v1alpha1_API_To_kubeadm_API,
Convert_kubeadm_API_To_v1alpha1_API,
Convert_v1alpha1_Etcd_To_kubeadm_Etcd,
Convert_kubeadm_Etcd_To_v1alpha1_Etcd,
Convert_v1alpha1_MasterConfiguration_To_kubeadm_MasterConfiguration,
Convert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration,
Convert_v1alpha1_Networking_To_kubeadm_Networking,
Convert_kubeadm_Networking_To_v1alpha1_Networking,
Convert_v1alpha1_NodeConfiguration_To_kubeadm_NodeConfiguration,
Convert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration,
Convert_v1alpha1_TokenDiscovery_To_kubeadm_TokenDiscovery,
Convert_kubeadm_TokenDiscovery_To_v1alpha1_TokenDiscovery,
)
}
func autoConvert_v1alpha1_API_To_kubeadm_API(in *API, out *kubeadm.API, s conversion.Scope) error {
out.AdvertiseAddress = in.AdvertiseAddress
out.BindPort = in.BindPort
return nil
}
// Convert_v1alpha1_API_To_kubeadm_API is an autogenerated conversion function.
func Convert_v1alpha1_API_To_kubeadm_API(in *API, out *kubeadm.API, s conversion.Scope) error {
return autoConvert_v1alpha1_API_To_kubeadm_API(in, out, s)
}
func autoConvert_kubeadm_API_To_v1alpha1_API(in *kubeadm.API, out *API, s conversion.Scope) error {
out.AdvertiseAddress = in.AdvertiseAddress
out.BindPort = in.BindPort
return nil
}
// Convert_kubeadm_API_To_v1alpha1_API is an autogenerated conversion function.
func Convert_kubeadm_API_To_v1alpha1_API(in *kubeadm.API, out *API, s conversion.Scope) error {
return autoConvert_kubeadm_API_To_v1alpha1_API(in, out, s)
}
func autoConvert_v1alpha1_Etcd_To_kubeadm_Etcd(in *Etcd, out *kubeadm.Etcd, s conversion.Scope) error {
out.Endpoints = *(*[]string)(unsafe.Pointer(&in.Endpoints))
out.CAFile = in.CAFile
out.CertFile = in.CertFile
out.KeyFile = in.KeyFile
out.DataDir = in.DataDir
out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs))
out.Image = in.Image
return nil
}
// Convert_v1alpha1_Etcd_To_kubeadm_Etcd is an autogenerated conversion function.
func Convert_v1alpha1_Etcd_To_kubeadm_Etcd(in *Etcd, out *kubeadm.Etcd, s conversion.Scope) error {
return autoConvert_v1alpha1_Etcd_To_kubeadm_Etcd(in, out, s)
}
func autoConvert_kubeadm_Etcd_To_v1alpha1_Etcd(in *kubeadm.Etcd, out *Etcd, s conversion.Scope) error {
out.Endpoints = *(*[]string)(unsafe.Pointer(&in.Endpoints))
out.CAFile = in.CAFile
out.CertFile = in.CertFile
out.KeyFile = in.KeyFile
out.DataDir = in.DataDir
out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs))
out.Image = in.Image
return nil
}
// Convert_kubeadm_Etcd_To_v1alpha1_Etcd is an autogenerated conversion function.
func Convert_kubeadm_Etcd_To_v1alpha1_Etcd(in *kubeadm.Etcd, out *Etcd, s conversion.Scope) error {
return autoConvert_kubeadm_Etcd_To_v1alpha1_Etcd(in, out, s)
}
func autoConvert_v1alpha1_MasterConfiguration_To_kubeadm_MasterConfiguration(in *MasterConfiguration, out *kubeadm.MasterConfiguration, s conversion.Scope) error {
if err := Convert_v1alpha1_API_To_kubeadm_API(&in.API, &out.API, s); err != nil {
return err
}
if err := Convert_v1alpha1_Etcd_To_kubeadm_Etcd(&in.Etcd, &out.Etcd, s); err != nil {
return err
}
if err := Convert_v1alpha1_Networking_To_kubeadm_Networking(&in.Networking, &out.Networking, s); err != nil {
return err
}
out.KubernetesVersion = in.KubernetesVersion
out.CloudProvider = in.CloudProvider
out.NodeName = in.NodeName
out.AuthorizationModes = *(*[]string)(unsafe.Pointer(&in.AuthorizationModes))
out.Token = in.Token
out.TokenTTL = in.TokenTTL
out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs))
out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs))
out.SchedulerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.SchedulerExtraArgs))
out.APIServerCertSANs = *(*[]string)(unsafe.Pointer(&in.APIServerCertSANs))
out.CertificatesDir = in.CertificatesDir
out.ImageRepository = in.ImageRepository
out.UnifiedControlPlaneImage = in.UnifiedControlPlaneImage
out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates))
return nil
}
// Convert_v1alpha1_MasterConfiguration_To_kubeadm_MasterConfiguration is an autogenerated conversion function.
func Convert_v1alpha1_MasterConfiguration_To_kubeadm_MasterConfiguration(in *MasterConfiguration, out *kubeadm.MasterConfiguration, s conversion.Scope) error {
return autoConvert_v1alpha1_MasterConfiguration_To_kubeadm_MasterConfiguration(in, out, s)
}
func autoConvert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in *kubeadm.MasterConfiguration, out *MasterConfiguration, s conversion.Scope) error {
if err := Convert_kubeadm_API_To_v1alpha1_API(&in.API, &out.API, s); err != nil {
return err
}
if err := Convert_kubeadm_Etcd_To_v1alpha1_Etcd(&in.Etcd, &out.Etcd, s); err != nil {
return err
}
if err := Convert_kubeadm_Networking_To_v1alpha1_Networking(&in.Networking, &out.Networking, s); err != nil {
return err
}
out.KubernetesVersion = in.KubernetesVersion
out.CloudProvider = in.CloudProvider
out.NodeName = in.NodeName
out.AuthorizationModes = *(*[]string)(unsafe.Pointer(&in.AuthorizationModes))
out.Token = in.Token
out.TokenTTL = in.TokenTTL
out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs))
out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs))
out.SchedulerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.SchedulerExtraArgs))
out.APIServerCertSANs = *(*[]string)(unsafe.Pointer(&in.APIServerCertSANs))
out.CertificatesDir = in.CertificatesDir
out.ImageRepository = in.ImageRepository
// INFO: in.CIImageRepository opted out of conversion generation
out.UnifiedControlPlaneImage = in.UnifiedControlPlaneImage
out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates))
return nil
}
// Convert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration is an autogenerated conversion function.
func Convert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in *kubeadm.MasterConfiguration, out *MasterConfiguration, s conversion.Scope) error {
return autoConvert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in, out, s)
}
func autoConvert_v1alpha1_Networking_To_kubeadm_Networking(in *Networking, out *kubeadm.Networking, s conversion.Scope) error {
out.ServiceSubnet = in.ServiceSubnet
out.PodSubnet = in.PodSubnet
out.DNSDomain = in.DNSDomain
return nil
}
// Convert_v1alpha1_Networking_To_kubeadm_Networking is an autogenerated conversion function.
func Convert_v1alpha1_Networking_To_kubeadm_Networking(in *Networking, out *kubeadm.Networking, s conversion.Scope) error {
return autoConvert_v1alpha1_Networking_To_kubeadm_Networking(in, out, s)
}
func autoConvert_kubeadm_Networking_To_v1alpha1_Networking(in *kubeadm.Networking, out *Networking, s conversion.Scope) error {
out.ServiceSubnet = in.ServiceSubnet
out.PodSubnet = in.PodSubnet
out.DNSDomain = in.DNSDomain
return nil
}
// Convert_kubeadm_Networking_To_v1alpha1_Networking is an autogenerated conversion function.
func Convert_kubeadm_Networking_To_v1alpha1_Networking(in *kubeadm.Networking, out *Networking, s conversion.Scope) error {
return autoConvert_kubeadm_Networking_To_v1alpha1_Networking(in, out, s)
}
func autoConvert_v1alpha1_NodeConfiguration_To_kubeadm_NodeConfiguration(in *NodeConfiguration, out *kubeadm.NodeConfiguration, s conversion.Scope) error {
out.CACertPath = in.CACertPath
out.DiscoveryFile = in.DiscoveryFile
out.DiscoveryToken = in.DiscoveryToken
out.DiscoveryTokenAPIServers = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenAPIServers))
out.NodeName = in.NodeName
out.TLSBootstrapToken = in.TLSBootstrapToken
out.Token = in.Token
out.DiscoveryTokenCACertHashes = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenCACertHashes))
out.DiscoveryTokenUnsafeSkipCAVerification = in.DiscoveryTokenUnsafeSkipCAVerification
return nil
}
// Convert_v1alpha1_NodeConfiguration_To_kubeadm_NodeConfiguration is an autogenerated conversion function.
func Convert_v1alpha1_NodeConfiguration_To_kubeadm_NodeConfiguration(in *NodeConfiguration, out *kubeadm.NodeConfiguration, s conversion.Scope) error {
return autoConvert_v1alpha1_NodeConfiguration_To_kubeadm_NodeConfiguration(in, out, s)
}
func autoConvert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration(in *kubeadm.NodeConfiguration, out *NodeConfiguration, s conversion.Scope) error {
out.CACertPath = in.CACertPath
out.DiscoveryFile = in.DiscoveryFile
out.DiscoveryToken = in.DiscoveryToken
out.DiscoveryTokenAPIServers = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenAPIServers))
out.NodeName = in.NodeName
out.TLSBootstrapToken = in.TLSBootstrapToken
out.Token = in.Token
out.DiscoveryTokenCACertHashes = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenCACertHashes))
out.DiscoveryTokenUnsafeSkipCAVerification = in.DiscoveryTokenUnsafeSkipCAVerification
return nil
}
// Convert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration is an autogenerated conversion function.
func Convert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration(in *kubeadm.NodeConfiguration, out *NodeConfiguration, s conversion.Scope) error {
return autoConvert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration(in, out, s)
}
func autoConvert_v1alpha1_TokenDiscovery_To_kubeadm_TokenDiscovery(in *TokenDiscovery, out *kubeadm.TokenDiscovery, s conversion.Scope) error {
out.ID = in.ID
out.Secret = in.Secret
out.Addresses = *(*[]string)(unsafe.Pointer(&in.Addresses))
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
out.Addresses = *(*[]string)(unsafe.Pointer(&in.Addresses))
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)
}

View file

@ -0,0 +1,244 @@
// +build !ignore_autogenerated
/*
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.
*/
// This file was autogenerated by deepcopy-gen. Do not edit it manually!
package v1alpha1
import (
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
reflect "reflect"
)
// GetGeneratedDeepCopyFuncs returns the generated funcs, since we aren't registering them.
//
// Deprecated: deepcopy registration will go away when static deepcopy is fully implemented.
func GetGeneratedDeepCopyFuncs() []conversion.GeneratedDeepCopyFunc {
return []conversion.GeneratedDeepCopyFunc{
{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*API).DeepCopyInto(out.(*API))
return nil
}, InType: reflect.TypeOf(&API{})},
{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*Etcd).DeepCopyInto(out.(*Etcd))
return nil
}, InType: reflect.TypeOf(&Etcd{})},
{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*MasterConfiguration).DeepCopyInto(out.(*MasterConfiguration))
return nil
}, InType: reflect.TypeOf(&MasterConfiguration{})},
{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*Networking).DeepCopyInto(out.(*Networking))
return nil
}, InType: reflect.TypeOf(&Networking{})},
{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*NodeConfiguration).DeepCopyInto(out.(*NodeConfiguration))
return nil
}, InType: reflect.TypeOf(&NodeConfiguration{})},
{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*TokenDiscovery).DeepCopyInto(out.(*TokenDiscovery))
return nil
}, InType: reflect.TypeOf(&TokenDiscovery{})},
}
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *API) DeepCopyInto(out *API) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new API.
func (in *API) DeepCopy() *API {
if in == nil {
return nil
}
out := new(API)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Etcd) DeepCopyInto(out *Etcd) {
*out = *in
if in.Endpoints != nil {
in, out := &in.Endpoints, &out.Endpoints
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.ExtraArgs != nil {
in, out := &in.ExtraArgs, &out.ExtraArgs
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Etcd.
func (in *Etcd) DeepCopy() *Etcd {
if in == nil {
return nil
}
out := new(Etcd)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MasterConfiguration) DeepCopyInto(out *MasterConfiguration) {
*out = *in
out.TypeMeta = in.TypeMeta
out.API = in.API
in.Etcd.DeepCopyInto(&out.Etcd)
out.Networking = in.Networking
if in.AuthorizationModes != nil {
in, out := &in.AuthorizationModes, &out.AuthorizationModes
*out = make([]string, len(*in))
copy(*out, *in)
}
out.TokenTTL = in.TokenTTL
if in.APIServerExtraArgs != nil {
in, out := &in.APIServerExtraArgs, &out.APIServerExtraArgs
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.ControllerManagerExtraArgs != nil {
in, out := &in.ControllerManagerExtraArgs, &out.ControllerManagerExtraArgs
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.SchedulerExtraArgs != nil {
in, out := &in.SchedulerExtraArgs, &out.SchedulerExtraArgs
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.APIServerCertSANs != nil {
in, out := &in.APIServerCertSANs, &out.APIServerCertSANs
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.FeatureGates != nil {
in, out := &in.FeatureGates, &out.FeatureGates
*out = make(map[string]bool, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MasterConfiguration.
func (in *MasterConfiguration) DeepCopy() *MasterConfiguration {
if in == nil {
return nil
}
out := new(MasterConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MasterConfiguration) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
} else {
return nil
}
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Networking) DeepCopyInto(out *Networking) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Networking.
func (in *Networking) DeepCopy() *Networking {
if in == nil {
return nil
}
out := new(Networking)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NodeConfiguration) DeepCopyInto(out *NodeConfiguration) {
*out = *in
out.TypeMeta = in.TypeMeta
if in.DiscoveryTokenAPIServers != nil {
in, out := &in.DiscoveryTokenAPIServers, &out.DiscoveryTokenAPIServers
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.DiscoveryTokenCACertHashes != nil {
in, out := &in.DiscoveryTokenCACertHashes, &out.DiscoveryTokenCACertHashes
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeConfiguration.
func (in *NodeConfiguration) DeepCopy() *NodeConfiguration {
if in == nil {
return nil
}
out := new(NodeConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *NodeConfiguration) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
} else {
return nil
}
}
// 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
if in.Addresses != nil {
in, out := &in.Addresses, &out.Addresses
*out = make([]string, len(*in))
copy(*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
}

View file

@ -0,0 +1,42 @@
// +build !ignore_autogenerated
/*
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.
*/
// This file was autogenerated by defaulter-gen. Do not edit it manually!
package v1alpha1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// RegisterDefaults adds defaulters functions to the given scheme.
// Public to allow building arbitrary schemes.
// All generated defaulters are covering - they call all nested defaulters.
func RegisterDefaults(scheme *runtime.Scheme) error {
scheme.AddTypeDefaultingFunc(&MasterConfiguration{}, func(obj interface{}) { SetObjectDefaults_MasterConfiguration(obj.(*MasterConfiguration)) })
scheme.AddTypeDefaultingFunc(&NodeConfiguration{}, func(obj interface{}) { SetObjectDefaults_NodeConfiguration(obj.(*NodeConfiguration)) })
return nil
}
func SetObjectDefaults_MasterConfiguration(in *MasterConfiguration) {
SetDefaults_MasterConfiguration(in)
}
func SetObjectDefaults_NodeConfiguration(in *NodeConfiguration) {
SetDefaults_NodeConfiguration(in)
}

View file

@ -0,0 +1,50 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["validation_test.go"],
library = ":go_default_library",
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//vendor/github.com/spf13/pflag:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["validation.go"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/features:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
"//cmd/kubeadm/app/util/token:go_default_library",
"//pkg/api/validation:go_default_library",
"//pkg/kubeapiserver/authorizer/modes:go_default_library",
"//pkg/registry/core/service/ipallocator:go_default_library",
"//pkg/util/node:go_default_library",
"//vendor/github.com/spf13/pflag:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,323 @@
/*
Copyright 2016 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 validation
import (
"fmt"
"net"
"net/url"
"os"
"path/filepath"
"strings"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/features"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
apivalidation "k8s.io/kubernetes/pkg/api/validation"
authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
"k8s.io/kubernetes/pkg/util/node"
)
// TODO: Break out the cloudprovider functionality out of core and only support the new flow
// described in https://github.com/kubernetes/community/pull/128
var cloudproviders = []string{
"aws",
"azure",
"cloudstack",
"gce",
"openstack",
"ovirt",
"photon",
"rackspace",
"vsphere",
}
// Describes the authorization modes that are enforced by kubeadm
var requiredAuthzModes = []string{
authzmodes.ModeRBAC,
authzmodes.ModeNode,
}
func ValidateMasterConfiguration(c *kubeadm.MasterConfiguration) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateCloudProvider(c.CloudProvider, field.NewPath("cloudprovider"))...)
allErrs = append(allErrs, ValidateAuthorizationModes(c.AuthorizationModes, field.NewPath("authorization-modes"))...)
allErrs = append(allErrs, ValidateNetworking(&c.Networking, field.NewPath("networking"))...)
allErrs = append(allErrs, ValidateAPIServerCertSANs(c.APIServerCertSANs, field.NewPath("cert-altnames"))...)
allErrs = append(allErrs, ValidateAbsolutePath(c.CertificatesDir, field.NewPath("certificates-dir"))...)
allErrs = append(allErrs, ValidateNodeName(c.NodeName, field.NewPath("node-name"))...)
allErrs = append(allErrs, ValidateToken(c.Token, field.NewPath("token"))...)
allErrs = append(allErrs, ValidateFeatureGates(c.FeatureGates, field.NewPath("feature-gates"))...)
allErrs = append(allErrs, ValidateAPIEndpoint(c, field.NewPath("api-endpoint"))...)
return allErrs
}
func ValidateNodeConfiguration(c *kubeadm.NodeConfiguration) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateDiscovery(c, field.NewPath("discovery"))...)
if !filepath.IsAbs(c.CACertPath) || !strings.HasSuffix(c.CACertPath, ".crt") {
allErrs = append(allErrs, field.Invalid(field.NewPath("ca-cert-path"), c.CACertPath, "the ca certificate path must be an absolute path"))
}
return allErrs
}
func ValidateAuthorizationModes(authzModes []string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
found := map[string]bool{}
for _, authzMode := range authzModes {
if !authzmodes.IsValidAuthorizationMode(authzMode) {
allErrs = append(allErrs, field.Invalid(fldPath, authzMode, "invalid authorization mode"))
}
if found[authzMode] {
allErrs = append(allErrs, field.Invalid(fldPath, authzMode, "duplicate authorization mode"))
continue
}
found[authzMode] = true
}
for _, requiredMode := range requiredAuthzModes {
if !found[requiredMode] {
allErrs = append(allErrs, field.Required(fldPath, fmt.Sprintf("authorization mode %s must be enabled", requiredMode)))
}
}
return allErrs
}
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)...)
if len(c.DiscoveryToken) != 0 {
allErrs = append(allErrs, ValidateToken(c.DiscoveryToken, fldPath)...)
}
if len(c.DiscoveryFile) != 0 {
allErrs = append(allErrs, ValidateDiscoveryFile(c.DiscoveryFile, 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"))
}
if len(cfg.DiscoveryFile) != 0 && len(cfg.DiscoveryTokenCACertHashes) != 0 {
allErrs = append(allErrs, field.Invalid(fldPath, "", "DiscoveryTokenCACertHashes cannot be used with DiscoveryFile"))
}
// TODO: convert this warning to an error after v1.8
if len(cfg.DiscoveryFile) == 0 && len(cfg.DiscoveryTokenCACertHashes) == 0 && !cfg.DiscoveryTokenUnsafeSkipCAVerification {
fmt.Println("[validation] WARNING: using token-based discovery without DiscoveryTokenCACertHashes can be unsafe (see https://kubernetes.io/docs/admin/kubeadm/#kubeadm-join).")
fmt.Println("[validation] WARNING: Pass --discovery-token-unsafe-skip-ca-verification to disable this warning. This warning will become an error in Kubernetes 1.9.")
}
// 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, 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
}
func ValidateAPIServerCertSANs(altnames []string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
for _, altname := range altnames {
if len(validation.IsDNS1123Subdomain(altname)) != 0 && net.ParseIP(altname) == nil {
allErrs = append(allErrs, field.Invalid(fldPath, altname, "altname is not a valid dns label or ip address"))
}
}
return allErrs
}
func ValidateIPFromString(ipaddr string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if net.ParseIP(ipaddr) == nil {
allErrs = append(allErrs, field.Invalid(fldPath, ipaddr, "ip address is not valid"))
}
return allErrs
}
func ValidateIPNetFromString(subnet string, minAddrs int64, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
_, svcSubnet, err := net.ParseCIDR(subnet)
if err != nil {
allErrs = append(allErrs, field.Invalid(fldPath, subnet, "couldn't parse subnet"))
return allErrs
}
numAddresses := ipallocator.RangeSize(svcSubnet)
if numAddresses < minAddrs {
allErrs = append(allErrs, field.Invalid(fldPath, subnet, "subnet is too small"))
}
return allErrs
}
func ValidateNetworking(c *kubeadm.Networking, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, apivalidation.ValidateDNS1123Subdomain(c.DNSDomain, field.NewPath("dns-domain"))...)
allErrs = append(allErrs, ValidateIPNetFromString(c.ServiceSubnet, constants.MinimumAddressesInServiceSubnet, field.NewPath("service-subnet"))...)
if len(c.PodSubnet) != 0 {
allErrs = append(allErrs, ValidateIPNetFromString(c.PodSubnet, constants.MinimumAddressesInServiceSubnet, field.NewPath("pod-subnet"))...)
}
return allErrs
}
func ValidateAbsolutePath(path string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if !filepath.IsAbs(path) {
allErrs = append(allErrs, field.Invalid(fldPath, path, "path is not absolute"))
}
return allErrs
}
func ValidateNodeName(nodename string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if node.GetHostname(nodename) != nodename {
allErrs = append(allErrs, field.Invalid(fldPath, nodename, "nodename is not valid, must be lower case"))
}
return allErrs
}
func ValidateCloudProvider(provider string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(provider) == 0 {
return allErrs
}
for _, supported := range cloudproviders {
if provider == supported {
return allErrs
}
}
allErrs = append(allErrs, field.Invalid(fldPath, provider, "cloudprovider not supported"))
return allErrs
}
func ValidateMixedArguments(flag *pflag.FlagSet) error {
// If --config isn't set, we have nothing to validate
if !flag.Changed("config") {
return nil
}
mixedInvalidFlags := []string{}
flag.Visit(func(f *pflag.Flag) {
if f.Name == "config" || strings.HasPrefix(f.Name, "skip-") || f.Name == "dry-run" || f.Name == "kubeconfig" {
// "--skip-*" flags or other whitelisted flags can be set with --config
return
}
mixedInvalidFlags = append(mixedInvalidFlags, f.Name)
})
if len(mixedInvalidFlags) != 0 {
return fmt.Errorf("can not mix '--config' with arguments %v", mixedInvalidFlags)
}
return nil
}
func ValidateFeatureGates(featureGates map[string]bool, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
validFeatures := features.Keys(features.InitFeatureGates)
// check valid feature names are provided
for k := range featureGates {
if !features.Supports(features.InitFeatureGates, k) {
allErrs = append(allErrs, field.Invalid(fldPath, featureGates,
fmt.Sprintf("%s is not a valid feature name. Valid features are: %s", k, validFeatures)))
}
}
return allErrs
}
func ValidateAPIEndpoint(c *kubeadm.MasterConfiguration, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
endpoint, err := kubeadmutil.GetMasterEndpoint(c)
if err != nil {
allErrs = append(allErrs, field.Invalid(fldPath, endpoint, "Invalid API Endpoint"))
}
return allErrs
}

View file

@ -0,0 +1,461 @@
/*
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 validation
import (
"testing"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func TestValidateTokenDiscovery(t *testing.T) {
var tests = []struct {
c *kubeadm.NodeConfiguration
f *field.Path
expected bool
}{
{&kubeadm.NodeConfiguration{Token: "772ef5.6b6baab1d4a0a171", DiscoveryTokenAPIServers: []string{"192.168.122.100:9898"}}, nil, true},
{&kubeadm.NodeConfiguration{Token: ".6b6baab1d4a0a171", DiscoveryTokenAPIServers: []string{"192.168.122.100:9898"}}, nil, false},
{&kubeadm.NodeConfiguration{Token: "772ef5.", DiscoveryTokenAPIServers: []string{"192.168.122.100:9898"}}, nil, false},
{&kubeadm.NodeConfiguration{Token: "772ef5.6b6baab1d4a0a171", DiscoveryTokenAPIServers: []string{"2001:db8::100:9898"}}, nil, true},
{&kubeadm.NodeConfiguration{Token: ".6b6baab1d4a0a171", DiscoveryTokenAPIServers: []string{"2001:db8::100:9898"}}, nil, false},
{&kubeadm.NodeConfiguration{Token: "772ef5.", DiscoveryTokenAPIServers: []string{"2001:db8::100:9898"}}, nil, false},
}
for _, rt := range tests {
err := ValidateToken(rt.c.Token, rt.f).ToAggregate()
if (err == nil) != rt.expected {
t.Errorf(
"failed ValidateTokenDiscovery:\n\texpected: %t\n\t actual: %t",
rt.expected,
(err == nil),
)
}
}
}
func TestValidateAuthorizationModes(t *testing.T) {
var tests = []struct {
s []string
f *field.Path
expected bool
}{
{[]string{""}, nil, false},
{[]string{"rBAC"}, nil, false}, // mode not supported
{[]string{"rBAC", "Webhook"}, nil, false}, // mode not supported
{[]string{"RBAC", "Webhook"}, nil, false}, // mode Node required
{[]string{"Node", "RBAC", "Webhook", "Webhook"}, nil, false}, // no duplicates allowed
{[]string{"not valid"}, nil, false}, // invalid mode
{[]string{"Node", "RBAC"}, nil, true}, // supported
{[]string{"RBAC", "Node"}, nil, true}, // supported
{[]string{"Node", "RBAC", "Webhook", "ABAC"}, nil, true}, // supported
}
for _, rt := range tests {
actual := ValidateAuthorizationModes(rt.s, rt.f)
if (len(actual) == 0) != rt.expected {
t.Errorf(
"failed ValidateAuthorizationModes:\n\texpected: %t\n\t actual: %t",
rt.expected,
(len(actual) == 0),
)
}
}
}
func TestValidateNodeName(t *testing.T) {
var tests = []struct {
s string
f *field.Path
expected bool
}{
{"", nil, false}, // ok if not provided
{"1234", nil, true}, // supported
{"valid-nodename", nil, true}, // supported
{"INVALID-NODENAME", nil, false}, // Upper cases is invalid
}
for _, rt := range tests {
actual := ValidateNodeName(rt.s, rt.f)
if (len(actual) == 0) != rt.expected {
t.Errorf(
"failed ValidateNodeName:\n\texpected: %t\n\t actual: %t",
rt.expected,
(len(actual) == 0),
)
}
}
}
func TestValidateCloudProvider(t *testing.T) {
var tests = []struct {
s string
f *field.Path
expected bool
}{
{"", nil, true}, // if not provided, ok, it's optional
{"1234", nil, false}, // not supported
{"awws", nil, false}, // not supported
{"aws", nil, true}, // supported
{"gce", nil, true}, // supported
}
for _, rt := range tests {
actual := ValidateCloudProvider(rt.s, rt.f)
if (len(actual) == 0) != rt.expected {
t.Errorf(
"failed ValidateCloudProvider:\n\texpected: %t\n\t actual: %t",
rt.expected,
(len(actual) == 0),
)
}
}
}
func TestValidateAPIServerCertSANs(t *testing.T) {
var tests = []struct {
sans []string
expected bool
}{
{[]string{}, true}, // ok if not provided
{[]string{"1,2,,3"}, false}, // not a DNS label or IP
{[]string{"my-hostname", "???&?.garbage"}, false}, // not valid
{[]string{"my-hostname", "my.subdomain", "1.2.3.4"}, true}, // supported
{[]string{"my-hostname2", "my.other.subdomain", "10.0.0.10"}, true}, // supported
{[]string{"my-hostname", "my.subdomain", "2001:db8::4"}, true}, // supported
{[]string{"my-hostname2", "my.other.subdomain", "2001:db8::10"}, true}, // supported
}
for _, rt := range tests {
actual := ValidateAPIServerCertSANs(rt.sans, nil)
if (len(actual) == 0) != rt.expected {
t.Errorf(
"failed ValidateAPIServerCertSANs:\n\texpected: %t\n\t actual: %t",
rt.expected,
(len(actual) == 0),
)
}
}
}
func TestValidateIPFromString(t *testing.T) {
var tests = []struct {
name string
ip string
expected bool
}{
{"invalid missing address", "", false},
{"invalid missing decimal points in IPv4 address", "1234", false},
{"invalid incomplete IPv4 address", "1.2", false},
{"invalid IPv4 CIDR provided instead of IPv4 address", "1.2.3.4/16", false},
{"valid IPv4 address", "1.2.3.4", true},
{"valid IPv6 address", "2001:db8::1", true},
{"invalid IPv6 CIDR provided instead of IPv6 address", "2001:db8::1/64", false},
{"invalid hex character in IPv6 address", "2001:xb8::", false},
{"invalid use of colons in IPv6 address", "2001::db8::", false},
}
for _, rt := range tests {
actual := ValidateIPFromString(rt.ip, nil)
if (len(actual) == 0) != rt.expected {
t.Errorf(
"%s test case failed:\n\texpected: %t\n\t actual: %t",
rt.name,
rt.expected,
(len(actual) == 0),
)
}
}
}
func TestValidateIPNetFromString(t *testing.T) {
var tests = []struct {
name string
subnet string
minaddrs int64
expected bool
}{
{"invalid missing CIDR", "", 0, false},
{"invalid CIDR missing decimal points in IPv4 address and / mask", "1234", 0, false},
{"invalid CIDR use of letters instead of numbers and / mask", "abc", 0, false},
{"invalid IPv4 address provided instead of CIDR representation", "1.2.3.4", 0, false},
{"invalid IPv6 address provided instead of CIDR representation", "2001:db8::1", 0, false},
{"valid, but IPv4 CIDR too small. At least 10 addresses needed", "10.0.0.16/29", 10, false},
{"valid, but IPv6 CIDR too small. At least 10 addresses needed", "2001:db8::/125", 10, false},
{"valid IPv4 CIDR", "10.0.0.16/12", 10, true},
{"valid IPv6 CIDR", "2001:db8::/98", 10, true},
}
for _, rt := range tests {
actual := ValidateIPNetFromString(rt.subnet, rt.minaddrs, nil)
if (len(actual) == 0) != rt.expected {
t.Errorf(
"%s test case failed :\n\texpected: %t\n\t actual: %t",
rt.name,
rt.expected,
(len(actual) == 0),
)
}
}
}
func TestValidateAPIEndpoint(t *testing.T) {
var tests = []struct {
name string
s *kubeadm.MasterConfiguration
expected bool
}{
{
name: "Missing configuration",
s: &kubeadm.MasterConfiguration{},
expected: false,
},
{
name: "Valid IPv4 address and default port",
s: &kubeadm.MasterConfiguration{
API: kubeadm.API{
AdvertiseAddress: "1.2.3.4",
BindPort: 6443,
},
},
expected: true,
},
{
name: "Valid IPv6 address and port",
s: &kubeadm.MasterConfiguration{
API: kubeadm.API{
AdvertiseAddress: "2001:db7::1",
BindPort: 3446,
},
},
expected: true,
},
{
name: "Invalid IPv4 address",
s: &kubeadm.MasterConfiguration{
API: kubeadm.API{
AdvertiseAddress: "1.2.34",
BindPort: 6443,
},
},
expected: false,
},
{
name: "Invalid IPv6 address",
s: &kubeadm.MasterConfiguration{
API: kubeadm.API{
AdvertiseAddress: "2001:db7:1",
BindPort: 3446,
},
},
expected: false,
},
}
for _, rt := range tests {
actual := ValidateAPIEndpoint(rt.s, nil)
if (len(actual) == 0) != rt.expected {
t.Errorf(
"%s test case failed:\n\texpected: %t\n\t actual: %t",
rt.name,
rt.expected,
(len(actual) == 0),
)
}
}
}
func TestValidateMasterConfiguration(t *testing.T) {
nodename := "valid-nodename"
var tests = []struct {
name string
s *kubeadm.MasterConfiguration
expected bool
}{
{"invalid missing master configuration",
&kubeadm.MasterConfiguration{}, false},
{"invalid missing token with IPv4 service subnet",
&kubeadm.MasterConfiguration{
API: kubeadm.API{
AdvertiseAddress: "1.2.3.4",
BindPort: 6443,
},
AuthorizationModes: []string{"Node", "RBAC"},
Networking: kubeadm.Networking{
ServiceSubnet: "10.96.0.1/12",
DNSDomain: "cluster.local",
},
CertificatesDir: "/some/cert/dir",
NodeName: nodename,
}, false},
{"invalid missing token with IPv6 service subnet",
&kubeadm.MasterConfiguration{
API: kubeadm.API{
AdvertiseAddress: "1.2.3.4",
BindPort: 6443,
},
AuthorizationModes: []string{"Node", "RBAC"},
Networking: kubeadm.Networking{
ServiceSubnet: "2001:db8::1/98",
DNSDomain: "cluster.local",
},
CertificatesDir: "/some/cert/dir",
NodeName: nodename,
}, false},
{"invalid missing node name",
&kubeadm.MasterConfiguration{
API: kubeadm.API{
AdvertiseAddress: "1.2.3.4",
BindPort: 6443,
},
AuthorizationModes: []string{"Node", "RBAC"},
Networking: kubeadm.Networking{
ServiceSubnet: "10.96.0.1/12",
DNSDomain: "cluster.local",
},
CertificatesDir: "/some/other/cert/dir",
Token: "abcdef.0123456789abcdef",
}, false},
{"valid master configuration with IPv4 service subnet",
&kubeadm.MasterConfiguration{
API: kubeadm.API{
AdvertiseAddress: "1.2.3.4",
BindPort: 6443,
},
AuthorizationModes: []string{"Node", "RBAC"},
Networking: kubeadm.Networking{
ServiceSubnet: "10.96.0.1/12",
DNSDomain: "cluster.local",
},
CertificatesDir: "/some/other/cert/dir",
Token: "abcdef.0123456789abcdef",
NodeName: nodename,
}, true},
{"valid master configuration using IPv6 service subnet",
&kubeadm.MasterConfiguration{
API: kubeadm.API{
AdvertiseAddress: "1:2:3::4",
BindPort: 3446,
},
AuthorizationModes: []string{"Node", "RBAC"},
Networking: kubeadm.Networking{
ServiceSubnet: "2001:db8::1/98",
DNSDomain: "cluster.local",
},
CertificatesDir: "/some/other/cert/dir",
Token: "abcdef.0123456789abcdef",
NodeName: nodename,
}, true},
}
for _, rt := range tests {
actual := ValidateMasterConfiguration(rt.s)
if (len(actual) == 0) != rt.expected {
t.Errorf(
"%s test case failed:\n\texpected: %t\n\t actual: %t",
rt.name,
rt.expected,
(len(actual) == 0),
)
}
}
}
func TestValidateNodeConfiguration(t *testing.T) {
var tests = []struct {
s *kubeadm.NodeConfiguration
expected bool
}{
{&kubeadm.NodeConfiguration{}, false},
{&kubeadm.NodeConfiguration{
DiscoveryFile: "foo",
DiscoveryToken: "abcdef.1234567890123456@foobar",
CACertPath: "/some/cert.crt",
}, false},
}
for _, rt := range tests {
actual := ValidateNodeConfiguration(rt.s)
if (len(actual) == 0) != rt.expected {
t.Errorf(
"failed ValidateNodeConfiguration:\n\texpected: %t\n\t actual: %t",
rt.expected,
(len(actual) == 0),
)
}
}
}
func TestValidateMixedArguments(t *testing.T) {
var tests = []struct {
args []string
expected bool
}{
// Expected to succeed, --config is mixed with skip-* flags only or no other flags
{[]string{"--foo=bar"}, true},
{[]string{"--config=hello"}, true},
{[]string{"--config=hello", "--skip-preflight-checks=true"}, true},
{[]string{"--config=hello", "--skip-token-print=true"}, true},
{[]string{"--config=hello", "--skip-preflight-checks", "--skip-token-print"}, true},
// Expected to fail, --config is mixed with the --foo flag
{[]string{"--config=hello", "--skip-preflight-checks", "--foo=bar"}, false},
{[]string{"--config=hello", "--foo=bar"}, false},
}
var cfgPath string
for _, rt := range tests {
f := pflag.NewFlagSet("test", pflag.ContinueOnError)
if f.Parsed() {
t.Error("f.Parse() = true before Parse")
}
f.String("foo", "", "flag bound to config object")
f.Bool("skip-preflight-checks", false, "flag not bound to config object")
f.Bool("skip-token-print", false, "flag not bound to config object")
f.StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file")
if err := f.Parse(rt.args); err != nil {
t.Fatal(err)
}
actual := ValidateMixedArguments(f)
if (actual == nil) != rt.expected {
t.Errorf(
"failed ValidateMixedArguments:\n\texpected: %t\n\t actual: %t testdata: %v",
rt.expected,
(actual == nil),
rt.args,
)
}
}
}
func TestValidateFeatureGates(t *testing.T) {
type featureFlag map[string]bool
var tests = []struct {
featureGates featureFlag
expected bool
}{
{featureFlag{"SelfHosting": true}, true},
{featureFlag{"SelfHosting": false}, true},
{featureFlag{"StoreCertsInSecrets": true}, true},
{featureFlag{"StoreCertsInSecrets": false}, true},
{featureFlag{"Foo": true}, false},
}
for _, rt := range tests {
actual := ValidateFeatureGates(rt.featureGates, nil)
if (len(actual) == 0) != rt.expected {
t.Errorf(
"failed featureGates:\n\texpected: %t\n\t actual: %t",
rt.expected,
(len(actual) == 0),
)
}
}
}

View file

@ -0,0 +1,43 @@
/*
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 kubeadm
// Role labels are applied to Nodes to mark their purpose. In particular, we
// usually want to distinguish the master, so that we can isolate privileged
// pods and operations.
//
// Originally we relied on not registering the master, on the fact that the
// master was Unschedulable, and on static manifests for master components.
// But we now do register masters in many environments, are generally moving
// away from static manifests (for better manageability), and working towards
// deprecating the unschedulable field (replacing it with taints & tolerations
// instead).
//
// Even with tainting, a label remains the easiest way of making a positive
// selection, so that pods can schedule only to master nodes for example, and
// thus installations will likely define a label for their master nodes.
//
// So that we can recognize master nodes in consequent places though (such as
// kubectl get nodes), we encourage installations to use the well-known labels.
// We define NodeLabelRole, which is the preferred form, but we will also recognize
// other forms that are known to be in widespread use (NodeLabelKubeadmAlphaRole).
const (
// NodeLabelKubeadmAlphaRole is a label that kubeadm applies to a Node as a hint that it has a particular purpose.
// Use of NodeLabelRole is preferred.
NodeLabelKubeadmAlphaRole = "kubeadm.alpha.kubernetes.io/role"
)

View file

@ -0,0 +1,249 @@
// +build !ignore_autogenerated
/*
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.
*/
// This file was autogenerated by deepcopy-gen. Do not edit it manually!
package kubeadm
import (
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
reflect "reflect"
)
func init() {
SchemeBuilder.Register(RegisterDeepCopies)
}
// RegisterDeepCopies adds deep-copy functions to the given scheme. Public
// to allow building arbitrary schemes.
//
// Deprecated: deepcopy registration will go away when static deepcopy is fully implemented.
func RegisterDeepCopies(scheme *runtime.Scheme) error {
return scheme.AddGeneratedDeepCopyFuncs(
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*API).DeepCopyInto(out.(*API))
return nil
}, InType: reflect.TypeOf(&API{})},
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*Etcd).DeepCopyInto(out.(*Etcd))
return nil
}, InType: reflect.TypeOf(&Etcd{})},
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*MasterConfiguration).DeepCopyInto(out.(*MasterConfiguration))
return nil
}, InType: reflect.TypeOf(&MasterConfiguration{})},
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*Networking).DeepCopyInto(out.(*Networking))
return nil
}, InType: reflect.TypeOf(&Networking{})},
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*NodeConfiguration).DeepCopyInto(out.(*NodeConfiguration))
return nil
}, InType: reflect.TypeOf(&NodeConfiguration{})},
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
in.(*TokenDiscovery).DeepCopyInto(out.(*TokenDiscovery))
return nil
}, InType: reflect.TypeOf(&TokenDiscovery{})},
)
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *API) DeepCopyInto(out *API) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new API.
func (in *API) DeepCopy() *API {
if in == nil {
return nil
}
out := new(API)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Etcd) DeepCopyInto(out *Etcd) {
*out = *in
if in.Endpoints != nil {
in, out := &in.Endpoints, &out.Endpoints
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.ExtraArgs != nil {
in, out := &in.ExtraArgs, &out.ExtraArgs
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Etcd.
func (in *Etcd) DeepCopy() *Etcd {
if in == nil {
return nil
}
out := new(Etcd)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MasterConfiguration) DeepCopyInto(out *MasterConfiguration) {
*out = *in
out.TypeMeta = in.TypeMeta
out.API = in.API
in.Etcd.DeepCopyInto(&out.Etcd)
out.Networking = in.Networking
if in.AuthorizationModes != nil {
in, out := &in.AuthorizationModes, &out.AuthorizationModes
*out = make([]string, len(*in))
copy(*out, *in)
}
out.TokenTTL = in.TokenTTL
if in.APIServerExtraArgs != nil {
in, out := &in.APIServerExtraArgs, &out.APIServerExtraArgs
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.ControllerManagerExtraArgs != nil {
in, out := &in.ControllerManagerExtraArgs, &out.ControllerManagerExtraArgs
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.SchedulerExtraArgs != nil {
in, out := &in.SchedulerExtraArgs, &out.SchedulerExtraArgs
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.APIServerCertSANs != nil {
in, out := &in.APIServerCertSANs, &out.APIServerCertSANs
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.FeatureGates != nil {
in, out := &in.FeatureGates, &out.FeatureGates
*out = make(map[string]bool, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MasterConfiguration.
func (in *MasterConfiguration) DeepCopy() *MasterConfiguration {
if in == nil {
return nil
}
out := new(MasterConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MasterConfiguration) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
} else {
return nil
}
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Networking) DeepCopyInto(out *Networking) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Networking.
func (in *Networking) DeepCopy() *Networking {
if in == nil {
return nil
}
out := new(Networking)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NodeConfiguration) DeepCopyInto(out *NodeConfiguration) {
*out = *in
out.TypeMeta = in.TypeMeta
if in.DiscoveryTokenAPIServers != nil {
in, out := &in.DiscoveryTokenAPIServers, &out.DiscoveryTokenAPIServers
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.DiscoveryTokenCACertHashes != nil {
in, out := &in.DiscoveryTokenCACertHashes, &out.DiscoveryTokenCACertHashes
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeConfiguration.
func (in *NodeConfiguration) DeepCopy() *NodeConfiguration {
if in == nil {
return nil
}
out := new(NodeConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *NodeConfiguration) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
} else {
return nil
}
}
// 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
if in.Addresses != nil {
in, out := &in.Addresses, &out.Addresses
*out = make([]string, len(*in))
copy(*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
}

108
vendor/k8s.io/kubernetes/cmd/kubeadm/app/cmd/BUILD generated vendored Normal file
View file

@ -0,0 +1,108 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"cmd.go",
"completion.go",
"config.go",
"init.go",
"join.go",
"reset.go",
"token.go",
"version.go",
],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
"//cmd/kubeadm/app/cmd/phases:go_default_library",
"//cmd/kubeadm/app/cmd/upgrade:go_default_library",
"//cmd/kubeadm/app/cmd/util:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/discovery:go_default_library",
"//cmd/kubeadm/app/features:go_default_library",
"//cmd/kubeadm/app/images:go_default_library",
"//cmd/kubeadm/app/node:go_default_library",
"//cmd/kubeadm/app/phases/addons/dns:go_default_library",
"//cmd/kubeadm/app/phases/addons/proxy:go_default_library",
"//cmd/kubeadm/app/phases/apiconfig:go_default_library",
"//cmd/kubeadm/app/phases/bootstraptoken/clusterinfo:go_default_library",
"//cmd/kubeadm/app/phases/bootstraptoken/node:go_default_library",
"//cmd/kubeadm/app/phases/certs:go_default_library",
"//cmd/kubeadm/app/phases/certs/pkiutil:go_default_library",
"//cmd/kubeadm/app/phases/controlplane:go_default_library",
"//cmd/kubeadm/app/phases/etcd:go_default_library",
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
"//cmd/kubeadm/app/phases/markmaster:go_default_library",
"//cmd/kubeadm/app/phases/selfhosting:go_default_library",
"//cmd/kubeadm/app/phases/uploadconfig:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
"//cmd/kubeadm/app/util/apiclient:go_default_library",
"//cmd/kubeadm/app/util/config:go_default_library",
"//cmd/kubeadm/app/util/dryrun:go_default_library",
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
"//cmd/kubeadm/app/util/pubkeypin:go_default_library",
"//cmd/kubeadm/app/util/token:go_default_library",
"//pkg/api:go_default_library",
"//pkg/bootstrap/api:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/util/i18n:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/util/initsystem:go_default_library",
"//pkg/util/node:go_default_library",
"//pkg/util/version:go_default_library",
"//pkg/version:go_default_library",
"//vendor/github.com/ghodss/yaml:go_default_library",
"//vendor/github.com/renstrom/dedent:go_default_library",
"//vendor/github.com/spf13/cobra: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/fields:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/version: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/util/cert:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"reset_test.go",
"token_test.go",
],
library = ":go_default_library",
deps = [
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//cmd/kubeadm/app/cmd/phases:all-srcs",
"//cmd/kubeadm/app/cmd/upgrade:all-srcs",
"//cmd/kubeadm/app/cmd/util:all-srcs",
],
tags = ["automanaged"],
)

89
vendor/k8s.io/kubernetes/cmd/kubeadm/app/cmd/cmd.go generated vendored Normal file
View file

@ -0,0 +1,89 @@
/*
Copyright 2016 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 cmd
import (
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/apiserver/pkg/util/flag"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/upgrade"
)
func NewKubeadmCommand(_ io.Reader, out, err io.Writer) *cobra.Command {
cmds := &cobra.Command{
Use: "kubeadm",
Short: "kubeadm: easily bootstrap a secure Kubernetes cluster",
Long: dedent.Dedent(`
kubeadm: easily bootstrap a secure Kubernetes cluster.
KUBEADM IS BETA, DO NOT USE IT FOR PRODUCTION CLUSTERS!
But, please try it out! Give us feedback at:
https://github.com/kubernetes/kubeadm/issues │
and at-mention @kubernetes/sig-cluster-lifecycle-bugs
or @kubernetes/sig-cluster-lifecycle-feature-requests
Example usage:
Create a two-machine cluster with one master (which controls the cluster),
and one node (where your workloads, like Pods and Deployments run).
On the first machine
master# kubeadm init
On the second machine
node# kubeadm join <arguments-returned-from-init>
You can then repeat the second step on as many other machines as you like.
`),
}
cmds.ResetFlags()
cmds.SetGlobalNormalizationFunc(flag.WarnWordSepNormalizeFunc)
cmds.AddCommand(NewCmdCompletion(out, ""))
cmds.AddCommand(NewCmdConfig(out))
cmds.AddCommand(NewCmdInit(out))
cmds.AddCommand(NewCmdJoin(out))
cmds.AddCommand(NewCmdReset(out))
cmds.AddCommand(NewCmdVersion(out))
cmds.AddCommand(NewCmdToken(out, err))
cmds.AddCommand(upgrade.NewCmdUpgrade(out))
// Wrap not yet fully supported commands in an alpha subcommand
experimentalCmd := &cobra.Command{
Use: "alpha",
Short: "Experimental sub-commands not yet fully functional.",
}
experimentalCmd.AddCommand(phases.NewCmdPhase(out))
cmds.AddCommand(experimentalCmd)
return cmds
}

View file

@ -0,0 +1,293 @@
/*
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 cmd
import (
"bytes"
"fmt"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)
const defaultBoilerPlate = `
# 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.
`
var (
completion_long = dedent.Dedent(`
Output shell completion code for the specified shell (bash or zsh).
The shell code must be evalutated to provide interactive
completion of kubeadm commands. This can be done by sourcing it from
the .bash_profile.
Note: this requires the bash-completion framework, which is not installed
by default on Mac. This can be installed by using homebrew:
$ brew install bash-completion
Once installed, bash_completion must be evaluated. This can be done by adding the
following line to the .bash_profile
$ source $(brew --prefix)/etc/bash_completion
Note for zsh users: [1] zsh completions are only supported in versions of zsh >= 5.2`)
completion_example = dedent.Dedent(`
# Install bash completion on a Mac using homebrew
brew install bash-completion
printf "\n# Bash completion support\nsource $(brew --prefix)/etc/bash_completion\n" >> $HOME/.bash_profile
source $HOME/.bash_profile
# Load the kubeadm completion code for bash into the current shell
source <(kubeadm completion bash)
# Write bash completion code to a file and source if from .bash_profile
kubeadm completion bash > ~/.kube/kubeadm_completion.bash.inc
printf "\n# Kubeadm shell completion\nsource '$HOME/.kube/kubeadm_completion.bash.inc'\n" >> $HOME/.bash_profile
source $HOME/.bash_profile
# Load the kubeadm completion code for zsh[1] into the current shell
source <(kubeadm completion zsh)`)
)
var (
completion_shells = map[string]func(out io.Writer, cmd *cobra.Command) error{
"bash": runCompletionBash,
"zsh": runCompletionZsh,
}
)
func NewCmdCompletion(out io.Writer, boilerPlate string) *cobra.Command {
shells := []string{}
for s := range completion_shells {
shells = append(shells, s)
}
cmd := &cobra.Command{
Use: "completion SHELL",
Short: i18n.T("Output shell completion code for the specified shell (bash or zsh)"),
Long: completion_long,
Example: completion_example,
Run: func(cmd *cobra.Command, args []string) {
err := RunCompletion(out, boilerPlate, cmd, args)
kubeadmutil.CheckErr(err)
},
ValidArgs: shells,
}
return cmd
}
func RunCompletion(out io.Writer, boilerPlate string, cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("shell not specified.")
}
if len(args) > 1 {
return fmt.Errorf("too many arguments. expected only the shell type.")
}
run, found := completion_shells[args[0]]
if !found {
return fmt.Errorf("unsupported shell type %q.", args[0])
}
if len(boilerPlate) == 0 {
boilerPlate = defaultBoilerPlate
}
if _, err := out.Write([]byte(boilerPlate)); err != nil {
return err
}
return run(out, cmd.Parent())
}
func runCompletionBash(out io.Writer, kubeadm *cobra.Command) error {
return kubeadm.GenBashCompletion(out)
}
func runCompletionZsh(out io.Writer, kubeadm *cobra.Command) error {
zsh_initialization := `
__kubeadm_bash_source() {
alias shopt=':'
alias _expand=_bash_expand
alias _complete=_bash_comp
emulate -L sh
setopt kshglob noshglob braceexpand
source "$@"
}
__kubeadm_type() {
# -t is not supported by zsh
if [ "$1" == "-t" ]; then
shift
# fake Bash 4 to disable "complete -o nospace". Instead
# "compopt +-o nospace" is used in the code to toggle trailing
# spaces. We don't support that, but leave trailing spaces on
# all the time
if [ "$1" = "__kubeadm_compopt" ]; then
echo builtin
return 0
fi
fi
type "$@"
}
__kubeadm_compgen() {
local completions w
completions=( $(compgen "$@") ) || return $?
# filter by given word as prefix
while [[ "$1" = -* && "$1" != -- ]]; do
shift
shift
done
if [[ "$1" == -- ]]; then
shift
fi
for w in "${completions[@]}"; do
if [[ "${w}" = "$1"* ]]; then
echo "${w}"
fi
done
}
__kubeadm_compopt() {
true # don't do anything. Not supported by bashcompinit in zsh
}
__kubeadm_ltrim_colon_completions()
{
if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
# Remove colon-word prefix from COMPREPLY items
local colon_word=${1%${1##*:}}
local i=${#COMPREPLY[*]}
while [[ $((--i)) -ge 0 ]]; do
COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
done
fi
}
__kubeadm_get_comp_words_by_ref() {
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[${COMP_CWORD}-1]}"
words=("${COMP_WORDS[@]}")
cword=("${COMP_CWORD[@]}")
}
__kubeadm_filedir() {
local RET OLD_IFS w qw
__debug "_filedir $@ cur=$cur"
if [[ "$1" = \~* ]]; then
# somehow does not work. Maybe, zsh does not call this at all
eval echo "$1"
return 0
fi
OLD_IFS="$IFS"
IFS=$'\n'
if [ "$1" = "-d" ]; then
shift
RET=( $(compgen -d) )
else
RET=( $(compgen -f) )
fi
IFS="$OLD_IFS"
IFS="," __debug "RET=${RET[@]} len=${#RET[@]}"
for w in ${RET[@]}; do
if [[ ! "${w}" = "${cur}"* ]]; then
continue
fi
if eval "[[ \"\${w}\" = *.$1 || -d \"\${w}\" ]]"; then
qw="$(__kubeadm_quote "${w}")"
if [ -d "${w}" ]; then
COMPREPLY+=("${qw}/")
else
COMPREPLY+=("${qw}")
fi
fi
done
}
__kubeadm_quote() {
if [[ $1 == \'* || $1 == \"* ]]; then
# Leave out first character
printf %q "${1:1}"
else
printf %q "$1"
fi
}
autoload -U +X bashcompinit && bashcompinit
# use word boundary patterns for BSD or GNU sed
LWORD='[[:<:]]'
RWORD='[[:>:]]'
if sed --help 2>&1 | grep -q GNU; then
LWORD='\<'
RWORD='\>'
fi
__kubeadm_convert_bash_to_zsh() {
sed \
-e 's/declare -F/whence -w/' \
-e 's/local \([a-zA-Z0-9_]*\)=/local \1; \1=/' \
-e 's/flags+=("\(--.*\)=")/flags+=("\1"); two_word_flags+=("\1")/' \
-e 's/must_have_one_flag+=("\(--.*\)=")/must_have_one_flag+=("\1")/' \
-e "s/${LWORD}_filedir${RWORD}/__kubeadm_filedir/g" \
-e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__kubeadm_get_comp_words_by_ref/g" \
-e "s/${LWORD}__ltrim_colon_completions${RWORD}/__kubeadm_ltrim_colon_completions/g" \
-e "s/${LWORD}compgen${RWORD}/__kubeadm_compgen/g" \
-e "s/${LWORD}compopt${RWORD}/__kubeadm_compopt/g" \
-e "s/${LWORD}declare${RWORD}/builtin declare/g" \
-e "s/\\\$(type${RWORD}/\$(__kubeadm_type/g" \
<<'BASH_COMPLETION_EOF'
`
out.Write([]byte(zsh_initialization))
buf := new(bytes.Buffer)
kubeadm.GenBashCompletion(buf)
out.Write(buf.Bytes())
zsh_tail := `
BASH_COMPLETION_EOF
}
__kubeadm_bash_source <(__kubeadm_convert_bash_to_zsh)
`
out.Write([]byte(zsh_tail))
return nil
}

188
vendor/k8s.io/kubernetes/cmd/kubeadm/app/cmd/config.go generated vendored Normal file
View file

@ -0,0 +1,188 @@
/*
Copyright 2016 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 cmd
import (
"fmt"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientset "k8s.io/client-go/kubernetes"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/features"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/uploadconfig"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
"k8s.io/kubernetes/pkg/api"
)
func NewCmdConfig(out io.Writer) *cobra.Command {
var kubeConfigFile string
cmd := &cobra.Command{
Use: "config",
Short: "Manage configuration for a kubeadm cluster persisted in a ConfigMap in the cluster.",
Long: fmt.Sprintf(dedent.Dedent(`
There is a ConfigMap in the %s namespace called %q that kubeadm uses to store internal configuration about the
cluster. kubeadm CLI v1.8.0+ automatically creates this ConfigMap with used config on 'kubeadm init', but if you
initialized your cluster using kubeadm v1.7.x or lower, you must use the 'config upload' command to create this
ConfigMap in order for 'kubeadm upgrade' to be able to configure your upgraded cluster correctly.
`), metav1.NamespaceSystem, constants.MasterConfigurationConfigMap),
// Without this callback, if a user runs just the "upload"
// command without a subcommand, or with an invalid subcommand,
// cobra will print usage information, but still exit cleanly.
// We want to return an error code in these cases so that the
// user knows that their command was invalid.
RunE: cmdutil.SubCmdRunE("config"),
}
cmd.PersistentFlags().StringVar(&kubeConfigFile, "kubeconfig", "/etc/kubernetes/admin.conf", "The KubeConfig file to use for talking to the cluster")
cmd.AddCommand(NewCmdConfigUpload(out, &kubeConfigFile))
cmd.AddCommand(NewCmdConfigView(out, &kubeConfigFile))
return cmd
}
func NewCmdConfigUpload(out io.Writer, kubeConfigFile *string) *cobra.Command {
cmd := &cobra.Command{
Use: "upload",
Short: "Upload configuration about the current state so 'kubeadm upgrade' later can know how to configure the upgraded cluster",
RunE: cmdutil.SubCmdRunE("upload"),
}
cmd.AddCommand(NewCmdConfigUploadFromFile(out, kubeConfigFile))
cmd.AddCommand(NewCmdConfigUploadFromFlags(out, kubeConfigFile))
return cmd
}
func NewCmdConfigView(out io.Writer, kubeConfigFile *string) *cobra.Command {
return &cobra.Command{
Use: "view",
Short: "View the kubeadm configuration stored inside the cluster",
Long: fmt.Sprintf(dedent.Dedent(`
Using this command, you can view the ConfigMap in the cluster where the configuration for kubeadm is located
The configuration is located in the %q namespace in the %q ConfigMap
`), metav1.NamespaceSystem, constants.MasterConfigurationConfigMap),
Run: func(cmd *cobra.Command, args []string) {
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
kubeadmutil.CheckErr(err)
err = RunConfigView(out, client)
kubeadmutil.CheckErr(err)
},
}
}
func NewCmdConfigUploadFromFile(out io.Writer, kubeConfigFile *string) *cobra.Command {
var cfgPath string
cmd := &cobra.Command{
Use: "from-file",
Short: "Upload a configuration file to the in-cluster ConfigMap for kubeadm configuration",
Long: fmt.Sprintf(dedent.Dedent(`
Using from-file, you can upload configuration to the ConfigMap in the cluster using the same config file you gave to kubeadm init.
If you initialized your cluster using a v1.7.x or lower kubeadm client and used the --config option; you need to run this command with the
same config file before upgrading to v1.8 using 'kubeadm upgrade'.
The configuration is located in the %q namespace in the %q ConfigMap
`), metav1.NamespaceSystem, constants.MasterConfigurationConfigMap),
Run: func(cmd *cobra.Command, args []string) {
if len(cfgPath) == 0 {
kubeadmutil.CheckErr(fmt.Errorf("The --config flag is mandatory"))
}
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
kubeadmutil.CheckErr(err)
// The default configuration is empty; everything should come from the file on disk
defaultcfg := &kubeadmapiext.MasterConfiguration{}
// Upload the configuration using the file; don't care about the defaultcfg really
err = uploadConfiguration(client, cfgPath, defaultcfg)
kubeadmutil.CheckErr(err)
},
}
cmd.Flags().StringVar(&cfgPath, "config", "", "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)")
return cmd
}
func NewCmdConfigUploadFromFlags(out io.Writer, kubeConfigFile *string) *cobra.Command {
cfg := &kubeadmapiext.MasterConfiguration{}
api.Scheme.Default(cfg)
var featureGatesString string
cmd := &cobra.Command{
Use: "from-flags",
Short: "Create the in-cluster configuration file for the first time from using flags",
Long: fmt.Sprintf(dedent.Dedent(`
Using from-flags, you can upload configuration to the ConfigMap in the cluster using the same flags you'd give to kubeadm init.
If you initialized your cluster using a v1.7.x or lower kubeadm client and set some flag; you need to run this command with the
same flags before upgrading to v1.8 using 'kubeadm upgrade'.
The configuration is located in the %q namespace in the %q ConfigMap
`), metav1.NamespaceSystem, constants.MasterConfigurationConfigMap),
Run: func(cmd *cobra.Command, args []string) {
var err error
if cfg.FeatureGates, err = features.NewFeatureGate(&features.InitFeatureGates, featureGatesString); err != nil {
kubeadmutil.CheckErr(err)
}
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
kubeadmutil.CheckErr(err)
// Default both statically and dynamically, convert to internal API type, and validate everything
// The cfgPath argument is unset here as we shouldn't load a config file from disk, just go with cfg
err = uploadConfiguration(client, "", cfg)
kubeadmutil.CheckErr(err)
},
}
AddInitConfigFlags(cmd.PersistentFlags(), cfg, &featureGatesString)
return cmd
}
// RunConfigView gets the configuration persisted in the cluster
func RunConfigView(out io.Writer, client clientset.Interface) error {
cfgConfigMap, err := client.CoreV1().ConfigMaps(metav1.NamespaceSystem).Get(constants.MasterConfigurationConfigMap, metav1.GetOptions{})
if err != nil {
return err
}
// No need to append \n as that already exists in the ConfigMap
fmt.Fprintf(out, "%s", cfgConfigMap.Data[constants.MasterConfigurationConfigMapKey])
return nil
}
// uploadConfiguration handles the uploading of the configuration internally
func uploadConfiguration(client clientset.Interface, cfgPath string, defaultcfg *kubeadmapiext.MasterConfiguration) error {
// Default both statically and dynamically, convert to internal API type, and validate everything
// First argument is unset here as we shouldn't load a config file from disk
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, defaultcfg)
if err != nil {
return err
}
// Then just call the uploadconfig phase to do the rest of the work
return uploadconfig.UploadConfiguration(internalcfg, client)
}

537
vendor/k8s.io/kubernetes/cmd/kubeadm/app/cmd/init.go generated vendored Normal file
View file

@ -0,0 +1,537 @@
/*
Copyright 2016 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 cmd
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"text/template"
"time"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/runtime"
clientset "k8s.io/client-go/kubernetes"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/features"
"k8s.io/kubernetes/cmd/kubeadm/app/images"
dnsaddonphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns"
proxyaddonphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/proxy"
apiconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/apiconfig"
clusterinfophase "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/clusterinfo"
nodebootstraptokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node"
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/pkiutil"
controlplanephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane"
etcdphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/etcd"
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
markmasterphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/markmaster"
selfhostingphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/selfhosting"
uploadconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/uploadconfig"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
"k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/version"
)
var (
initDoneTempl = template.Must(template.New("init").Parse(dedent.Dedent(`
Your Kubernetes master has initialized successfully!
To start using your cluster, you need to run (as a regular user):
mkdir -p $HOME/.kube
sudo cp -i {{.KubeConfigPath}} $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
http://kubernetes.io/docs/admin/addons/
You can now join any number of machines by running the following on each node
as root:
kubeadm join --token {{.Token}} {{.MasterHostPort}} --discovery-token-ca-cert-hash {{.CAPubKeyPin}}
`)))
kubeletFailTempl = template.Must(template.New("init").Parse(dedent.Dedent(`
Unfortunately, an error has occurred:
{{ .Error }}
This error is likely caused by that:
- The kubelet is not running
- The kubelet is unhealthy due to a misconfiguration of the node in some way (required cgroups disabled)
- There is no internet connection; so the kubelet can't pull the following control plane images:
- {{ .APIServerImage }}
- {{ .ControllerManagerImage }}
- {{ .SchedulerImage }}
You can troubleshoot this for example with the following commands if you're on a systemd-powered system:
- 'systemctl status kubelet'
- 'journalctl -xeu kubelet'
`)))
)
// NewCmdInit returns "kubeadm init" command.
func NewCmdInit(out io.Writer) *cobra.Command {
cfg := &kubeadmapiext.MasterConfiguration{}
api.Scheme.Default(cfg)
var cfgPath string
var skipPreFlight bool
var skipTokenPrint bool
var dryRun bool
var featureGatesString string
cmd := &cobra.Command{
Use: "init",
Short: "Run this in order to set up the Kubernetes master",
Run: func(cmd *cobra.Command, args []string) {
var err error
if cfg.FeatureGates, err = features.NewFeatureGate(&features.InitFeatureGates, featureGatesString); err != nil {
kubeadmutil.CheckErr(err)
}
api.Scheme.Default(cfg)
internalcfg := &kubeadmapi.MasterConfiguration{}
api.Scheme.Convert(cfg, internalcfg, nil)
i, err := NewInit(cfgPath, internalcfg, skipPreFlight, skipTokenPrint, dryRun)
kubeadmutil.CheckErr(err)
kubeadmutil.CheckErr(i.Validate(cmd))
// TODO: remove this warning in 1.9
if !cmd.Flags().Lookup("token-ttl").Changed {
fmt.Println("[kubeadm] WARNING: starting in 1.8, tokens expire after 24 hours by default (if you require a non-expiring token use --token-ttl 0)")
}
kubeadmutil.CheckErr(i.Run(out))
},
}
AddInitConfigFlags(cmd.PersistentFlags(), cfg, &featureGatesString)
AddInitOtherFlags(cmd.PersistentFlags(), &cfgPath, &skipPreFlight, &skipTokenPrint, &dryRun)
return cmd
}
// AddInitConfigFlags adds init flags bound to the config to the specified flagset
func AddInitConfigFlags(flagSet *flag.FlagSet, cfg *kubeadmapiext.MasterConfiguration, featureGatesString *string) {
flagSet.StringVar(
&cfg.API.AdvertiseAddress, "apiserver-advertise-address", cfg.API.AdvertiseAddress,
"The IP address the API Server will advertise it's listening on. 0.0.0.0 means the default network interface's address.",
)
flagSet.Int32Var(
&cfg.API.BindPort, "apiserver-bind-port", cfg.API.BindPort,
"Port for the API Server to bind to",
)
flagSet.StringVar(
&cfg.Networking.ServiceSubnet, "service-cidr", cfg.Networking.ServiceSubnet,
"Use alternative range of IP address for service VIPs",
)
flagSet.StringVar(
&cfg.Networking.PodSubnet, "pod-network-cidr", cfg.Networking.PodSubnet,
"Specify range of IP addresses for the pod network; if set, the control plane will automatically allocate CIDRs for every node",
)
flagSet.StringVar(
&cfg.Networking.DNSDomain, "service-dns-domain", cfg.Networking.DNSDomain,
`Use alternative domain for services, e.g. "myorg.internal"`,
)
flagSet.StringVar(
&cfg.KubernetesVersion, "kubernetes-version", cfg.KubernetesVersion,
`Choose a specific Kubernetes version for the control plane`,
)
flagSet.StringVar(
&cfg.CertificatesDir, "cert-dir", cfg.CertificatesDir,
`The path where to save and store the certificates`,
)
flagSet.StringSliceVar(
&cfg.APIServerCertSANs, "apiserver-cert-extra-sans", cfg.APIServerCertSANs,
`Optional extra altnames to use for the API Server serving cert. Can be both IP addresses and dns names.`,
)
flagSet.StringVar(
&cfg.NodeName, "node-name", cfg.NodeName,
`Specify the node name`,
)
flagSet.StringVar(
&cfg.Token, "token", cfg.Token,
"The token to use for establishing bidirectional trust between nodes and masters.",
)
flagSet.DurationVar(
&cfg.TokenTTL.Duration, "token-ttl", cfg.TokenTTL.Duration,
"The duration before the bootstrap token is automatically deleted. 0 means 'never expires'.",
)
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"))
}
// AddInitOtherFlags adds init flags that are not bound to a configuration file to the given flagset
func AddInitOtherFlags(flagSet *flag.FlagSet, cfgPath *string, skipPreFlight, skipTokenPrint, dryRun *bool) {
flagSet.StringVar(
cfgPath, "config", *cfgPath,
"Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)",
)
// Note: All flags that are not bound to the cfg object should be whitelisted in cmd/kubeadm/app/apis/kubeadm/validation/validation.go
flagSet.BoolVar(
skipPreFlight, "skip-preflight-checks", *skipPreFlight,
"Skip preflight checks normally run before modifying the system",
)
// Note: All flags that are not bound to the cfg object should be whitelisted in cmd/kubeadm/app/apis/kubeadm/validation/validation.go
flagSet.BoolVar(
skipTokenPrint, "skip-token-print", *skipTokenPrint,
"Skip printing of the default bootstrap token generated by 'kubeadm init'",
)
// Note: All flags that are not bound to the cfg object should be whitelisted in cmd/kubeadm/app/apis/kubeadm/validation/validation.go
flagSet.BoolVar(
dryRun, "dry-run", *dryRun,
"Don't apply any changes; just output what would be done",
)
}
func NewInit(cfgPath string, cfg *kubeadmapi.MasterConfiguration, skipPreFlight, skipTokenPrint, dryRun bool) (*Init, error) {
fmt.Println("[kubeadm] WARNING: kubeadm is in beta, please do not use it for production clusters.")
if cfgPath != "" {
b, err := ioutil.ReadFile(cfgPath)
if err != nil {
return nil, fmt.Errorf("unable to read config from %q [%v]", cfgPath, err)
}
if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), b, cfg); err != nil {
return nil, fmt.Errorf("unable to decode config from %q [%v]", cfgPath, err)
}
}
// Set defaults dynamically that the API group defaulting can't (by fetching information from the internet, looking up network interfaces, etc.)
err := configutil.SetInitDynamicDefaults(cfg)
if err != nil {
return nil, err
}
fmt.Printf("[init] Using Kubernetes version: %s\n", cfg.KubernetesVersion)
fmt.Printf("[init] Using Authorization modes: %v\n", cfg.AuthorizationModes)
// Warn about the limitations with the current cloudprovider solution.
if cfg.CloudProvider != "" {
fmt.Println("[init] WARNING: For cloudprovider integrations to work --cloud-provider must be set for all kubelets in the cluster.")
fmt.Println("\t(/etc/systemd/system/kubelet.service.d/10-kubeadm.conf should be edited for this purpose)")
}
if !skipPreFlight {
fmt.Println("[preflight] Running pre-flight checks")
if err := preflight.RunInitMasterChecks(cfg); err != nil {
return nil, err
}
// Try to start the kubelet service in case it's inactive
preflight.TryStartKubelet()
} else {
fmt.Println("[preflight] Skipping pre-flight checks")
}
return &Init{cfg: cfg, skipTokenPrint: skipTokenPrint, dryRun: dryRun}, nil
}
type Init struct {
cfg *kubeadmapi.MasterConfiguration
skipTokenPrint bool
dryRun bool
}
// Validate validates configuration passed to "kubeadm init"
func (i *Init) Validate(cmd *cobra.Command) error {
if err := validation.ValidateMixedArguments(cmd.Flags()); err != nil {
return err
}
return validation.ValidateMasterConfiguration(i.cfg).ToAggregate()
}
// Run executes master node provisioning, including certificates, needed static pod manifests, etc.
func (i *Init) Run(out io.Writer) error {
k8sVersion, err := version.ParseSemantic(i.cfg.KubernetesVersion)
if err != nil {
return fmt.Errorf("couldn't parse kubernetes version %q: %v", i.cfg.KubernetesVersion, err)
}
// Get directories to write files to; can be faked if we're dry-running
realCertsDir := i.cfg.CertificatesDir
certsDirToWriteTo, kubeConfigDir, manifestDir, err := getDirectoriesToUse(i.dryRun, i.cfg.CertificatesDir)
if err != nil {
return err
}
// certsDirToWriteTo is gonna equal cfg.CertificatesDir in the normal case, but gonna be a temp directory if dryrunning
i.cfg.CertificatesDir = certsDirToWriteTo
adminKubeConfigPath := filepath.Join(kubeConfigDir, kubeadmconstants.AdminKubeConfigFileName)
if res, _ := certsphase.UsingExternalCA(i.cfg); !res {
// PHASE 1: Generate certificates
if err := certsphase.CreatePKIAssets(i.cfg); err != nil {
return err
}
// PHASE 2: Generate kubeconfig files for the admin and the kubelet
if err := kubeconfigphase.CreateInitKubeConfigFiles(kubeConfigDir, i.cfg); err != nil {
return err
}
} else {
fmt.Println("[externalca] No ca.key detected, but all other certificates are available, so using external CA mode. Will not generate certs or kubeconfig.")
}
// Temporarily set cfg.CertificatesDir to the "real value" when writing controlplane manifests
// This is needed for writing the right kind of manifests
i.cfg.CertificatesDir = realCertsDir
// PHASE 3: Bootstrap the control plane
if err := controlplanephase.CreateInitStaticPodManifestFiles(manifestDir, i.cfg); err != nil {
return err
}
// Add etcd static pod spec only if external etcd is not configured
if len(i.cfg.Etcd.Endpoints) == 0 {
if err := etcdphase.CreateLocalEtcdStaticPodManifestFile(manifestDir, i.cfg); err != nil {
return err
}
}
// Revert the earlier CertificatesDir assignment to the directory that can be written to
i.cfg.CertificatesDir = certsDirToWriteTo
// If we're dry-running, print the generated manifests
if err := printFilesIfDryRunning(i.dryRun, manifestDir); err != nil {
return err
}
// Create a kubernetes client and wait for the API server to be healthy (if not dryrunning)
client, err := createClient(i.cfg, i.dryRun)
if err != nil {
return err
}
// waiter holds the apiclient.Waiter implementation of choice, responsible for querying the API server in various ways and waiting for conditions to be fulfilled
waiter := getWaiter(i.dryRun, client)
if err := waitForAPIAndKubelet(waiter); err != nil {
ctx := map[string]string{
"Error": fmt.Sprintf("%v", err),
"APIServerImage": images.GetCoreImage(kubeadmconstants.KubeAPIServer, i.cfg.GetControlPlaneImageRepository(), i.cfg.KubernetesVersion, i.cfg.UnifiedControlPlaneImage),
"ControllerManagerImage": images.GetCoreImage(kubeadmconstants.KubeControllerManager, i.cfg.GetControlPlaneImageRepository(), i.cfg.KubernetesVersion, i.cfg.UnifiedControlPlaneImage),
"SchedulerImage": images.GetCoreImage(kubeadmconstants.KubeScheduler, i.cfg.GetControlPlaneImageRepository(), i.cfg.KubernetesVersion, i.cfg.UnifiedControlPlaneImage),
}
kubeletFailTempl.Execute(out, ctx)
return fmt.Errorf("couldn't initialize a Kubernetes cluster")
}
// Upload currently used configuration to the cluster
// Note: This is done right in the beginning of cluster initialization; as we might want to make other phases
// depend on centralized information from this source in the future
if err := uploadconfigphase.UploadConfiguration(i.cfg, client); err != nil {
return err
}
// PHASE 4: Mark the master with the right label/taint
if err := markmasterphase.MarkMaster(client, i.cfg.NodeName); err != nil {
return err
}
// PHASE 5: Set up the node bootstrap tokens
if !i.skipTokenPrint {
fmt.Printf("[bootstraptoken] Using token: %s\n", i.cfg.Token)
}
// Create the default node bootstrap token
tokenDescription := "The default bootstrap token generated by 'kubeadm init'."
if err := nodebootstraptokenphase.UpdateOrCreateToken(client, i.cfg.Token, false, i.cfg.TokenTTL.Duration, kubeadmconstants.DefaultTokenUsages, []string{kubeadmconstants.V18NodeBootstrapTokenAuthGroup}, tokenDescription); err != nil {
return err
}
// Create RBAC rules that makes the bootstrap tokens able to post CSRs
if err := nodebootstraptokenphase.AllowBootstrapTokensToPostCSRs(client, k8sVersion); err != nil {
return err
}
// Create RBAC rules that makes the bootstrap tokens able to get their CSRs approved automatically
if err := nodebootstraptokenphase.AutoApproveNodeBootstrapTokens(client, k8sVersion); err != nil {
return err
}
// Create the cluster-info ConfigMap with the associated RBAC rules
if err := clusterinfophase.CreateBootstrapConfigMapIfNotExists(client, adminKubeConfigPath); err != nil {
return err
}
if err := clusterinfophase.CreateClusterInfoRBACRules(client); err != nil {
return err
}
// PHASE 6: Install and deploy all addons, and configure things as necessary
if err := apiconfigphase.CreateRBACRules(client, k8sVersion); err != nil {
return err
}
if err := dnsaddonphase.EnsureDNSAddon(i.cfg, client); err != nil {
return err
}
if err := proxyaddonphase.EnsureProxyAddon(i.cfg, client); err != nil {
return err
}
// PHASE 7: Make the control plane self-hosted if feature gate is enabled
if features.Enabled(i.cfg.FeatureGates, features.SelfHosting) {
// Temporary control plane is up, now we create our self hosted control
// plane components and remove the static manifests:
fmt.Println("[self-hosted] Creating self-hosted control plane...")
if err := selfhostingphase.CreateSelfHostedControlPlane(manifestDir, kubeConfigDir, i.cfg, client, waiter); err != nil {
return err
}
}
// Exit earlier if we're dryrunning
if i.dryRun {
fmt.Println("[dryrun] Finished dry-running successfully; above are the resources that would be created.")
return nil
}
// Load the CA certificate from so we can pin its public key
caCert, err := pkiutil.TryLoadCertFromDisk(i.cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName)
// Generate the Master host/port pair used by initDoneTempl
masterHostPort, err := kubeadmutil.GetMasterHostPort(i.cfg)
if err != nil {
return err
}
ctx := map[string]string{
"KubeConfigPath": adminKubeConfigPath,
"Token": i.cfg.Token,
"CAPubKeyPin": pubkeypin.Hash(caCert),
"MasterHostPort": masterHostPort,
}
if i.skipTokenPrint {
ctx["Token"] = "<value withheld>"
}
return initDoneTempl.Execute(out, ctx)
}
// createClient creates a clientset.Interface object
func createClient(cfg *kubeadmapi.MasterConfiguration, dryRun bool) (clientset.Interface, error) {
if dryRun {
// If we're dry-running; we should create a faked client that answers some GETs in order to be able to do the full init flow and just logs the rest of requests
dryRunGetter := apiclient.NewInitDryRunGetter(cfg.NodeName, cfg.Networking.ServiceSubnet)
return apiclient.NewDryRunClient(dryRunGetter, os.Stdout), nil
}
// If we're acting for real, we should create a connection to the API server and wait for it to come up
return kubeconfigutil.ClientSetFromFile(kubeadmconstants.GetAdminKubeConfigPath())
}
// getDirectoriesToUse returns the (in order) certificates, kubeconfig and Static Pod manifest directories, followed by a possible error
// This behaves differently when dry-running vs the normal flow
func getDirectoriesToUse(dryRun bool, defaultPkiDir string) (string, string, string, error) {
if dryRun {
dryRunDir, err := ioutil.TempDir("", "kubeadm-init-dryrun")
if err != nil {
return "", "", "", fmt.Errorf("couldn't create a temporary directory: %v", err)
}
// Use the same temp dir for all
return dryRunDir, dryRunDir, dryRunDir, nil
}
return defaultPkiDir, kubeadmconstants.KubernetesDir, kubeadmconstants.GetStaticPodDirectory(), nil
}
// printFilesIfDryRunning prints the Static Pod manifests to stdout and informs about the temporary directory to go and lookup
func printFilesIfDryRunning(dryRun bool, manifestDir string) error {
if !dryRun {
return nil
}
fmt.Printf("[dryrun] Wrote certificates, kubeconfig files and control plane manifests to %q\n", manifestDir)
fmt.Println("[dryrun] Won't print certificates or kubeconfig files due to the sensitive nature of them")
fmt.Printf("[dryrun] Please go and examine the %q directory for details about what would be written\n", manifestDir)
// Print the contents of the upgraded manifests and pretend like they were in /etc/kubernetes/manifests
files := []dryrunutil.FileToPrint{}
for _, component := range kubeadmconstants.MasterComponents {
realPath := kubeadmconstants.GetStaticPodFilepath(component, manifestDir)
outputPath := kubeadmconstants.GetStaticPodFilepath(component, kubeadmconstants.GetStaticPodDirectory())
files = append(files, dryrunutil.NewFileToPrint(realPath, outputPath))
}
return dryrunutil.PrintDryRunFiles(files, os.Stdout)
}
// getWaiter gets the right waiter implementation for the right occasion
func getWaiter(dryRun bool, client clientset.Interface) apiclient.Waiter {
if dryRun {
return dryrunutil.NewWaiter()
}
return apiclient.NewKubeWaiter(client, 30*time.Minute, os.Stdout)
}
// waitForAPIAndKubelet waits primarily for the API server to come up. If that takes a long time, and the kubelet
// /healthz and /healthz/syncloop endpoints continuously are unhealthy, kubeadm will error out after a period of
// backoffing exponentially
func waitForAPIAndKubelet(waiter apiclient.Waiter) error {
errorChan := make(chan error)
fmt.Printf("[init] Waiting for the kubelet to boot up the control plane as Static Pods from directory %q\n", kubeadmconstants.GetStaticPodDirectory())
fmt.Println("[init] This often takes around a minute; or longer if the control plane images have to be pulled.")
go func(errC chan error, waiter apiclient.Waiter) {
// This goroutine can only make kubeadm init fail. If this check succeeds, it won't do anything special
if err := waiter.WaitForHealthyKubelet(40*time.Second, "http://localhost:10255/healthz"); err != nil {
errC <- err
}
}(errorChan, waiter)
go func(errC chan error, waiter apiclient.Waiter) {
// This goroutine can only make kubeadm init fail. If this check succeeds, it won't do anything special
if err := waiter.WaitForHealthyKubelet(60*time.Second, "http://localhost:10255/healthz/syncloop"); err != nil {
errC <- err
}
}(errorChan, waiter)
go func(errC chan error, waiter apiclient.Waiter) {
// This main goroutine sends whatever WaitForAPI returns (error or not) to the channel
// This in order to continue on success (nil error), or just fail if
errC <- waiter.WaitForAPI()
}(errorChan, waiter)
// This call is blocking until one of the goroutines sends to errorChan
return <-errorChan
}

247
vendor/k8s.io/kubernetes/cmd/kubeadm/app/cmd/join.go generated vendored Normal file
View file

@ -0,0 +1,247 @@
/*
Copyright 2016 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 cmd
import (
"fmt"
"io"
"io/ioutil"
"os/exec"
"path/filepath"
"strings"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
certutil "k8s.io/client-go/util/cert"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/discovery"
kubeadmnode "k8s.io/kubernetes/cmd/kubeadm/app/node"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
"k8s.io/kubernetes/pkg/api"
nodeutil "k8s.io/kubernetes/pkg/util/node"
)
var (
joinDoneMsgf = dedent.Dedent(`
Node join complete:
* Certificate signing request sent to master and response
received.
* Kubelet informed of new secure connection details.
Run 'kubectl get nodes' on the master to see this machine join.
`)
)
// NewCmdJoin returns "kubeadm join" command.
func NewCmdJoin(out io.Writer) *cobra.Command {
cfg := &kubeadmapiext.NodeConfiguration{}
api.Scheme.Default(cfg)
var skipPreFlight bool
var cfgPath string
cmd := &cobra.Command{
Use: "join <flags> [DiscoveryTokenAPIServers]",
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.
If you use a shared token for discovery, you should also pass the
--discovery-token-ca-cert-hash flag to validate the public key of the
root certificate authority (CA) presented by the Kubernetes Master. The
value of this flag is specified as "<hash-type>:<hex-encoded-value>",
where the supported hash type is "sha256". The hash is calculated over
the bytes of the Subject Public Key Info (SPKI) object (as in RFC7469).
This value is available in the output of "kubeadm init" or can be
calcuated using standard tools. The --discovery-token-ca-cert-hash flag
may be repeated multiple times to allow more than one public key.
If you cannot know the CA public key hash ahead of time, you can pass
the --discovery-token-unsafe-skip-ca-verification flag to disable this
verification. This weakens the kubeadm security model since other nodes
can potentially impersonate the Kubernetes Master.
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 used for both parts. In this case, the
--token flag can be used instead of specifying each token individually.
`),
Run: func(cmd *cobra.Command, args []string) {
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(j.Validate(cmd))
kubeadmutil.CheckErr(j.Run(out))
},
}
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.NodeName, "node-name", "",
"Specify the node name")
cmd.PersistentFlags().StringVar(
&cfg.TLSBootstrapToken, "tls-bootstrap-token", "",
"A token used for TLS bootstrapping")
cmd.PersistentFlags().StringSliceVar(
&cfg.DiscoveryTokenCACertHashes, "discovery-token-ca-cert-hash", []string{},
"For token-based discovery, validate that the root CA public key matches this hash (format: \"<type>:<value>\").")
cmd.PersistentFlags().BoolVar(
&cfg.DiscoveryTokenUnsafeSkipCAVerification, "discovery-token-unsafe-skip-ca-verification", false,
"For token-based discovery, allow joining without --discovery-token-ca-cert-hash pinning.")
cmd.PersistentFlags().StringVar(
&cfg.Token, "token", "",
"Use this token for both discovery-token and tls-bootstrap-token")
cmd.PersistentFlags().BoolVar(
&skipPreFlight, "skip-preflight-checks", false,
"Skip preflight checks normally run before modifying the system",
)
return cmd
}
type Join struct {
cfg *kubeadmapi.NodeConfiguration
}
func NewJoin(cfgPath string, args []string, cfg *kubeadmapi.NodeConfiguration, skipPreFlight bool) (*Join, error) {
fmt.Println("[kubeadm] WARNING: kubeadm is in beta, please do not use it for production clusters.")
if cfg.NodeName == "" {
cfg.NodeName = nodeutil.GetHostname("")
}
if cfgPath != "" {
b, err := ioutil.ReadFile(cfgPath)
if err != nil {
return nil, fmt.Errorf("unable to read config from %q [%v]", cfgPath, err)
}
if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), b, cfg); err != nil {
return nil, fmt.Errorf("unable to decode config from %q [%v]", cfgPath, err)
}
}
if !skipPreFlight {
fmt.Println("[preflight] Running pre-flight checks")
// Then continue with the others...
if err := preflight.RunJoinNodeChecks(cfg); err != nil {
return nil, err
}
// Try to start the kubelet service in case it's inactive
preflight.TryStartKubelet()
} else {
fmt.Println("[preflight] Skipping pre-flight checks")
}
return &Join{cfg: cfg}, nil
}
func (j *Join) Validate(cmd *cobra.Command) error {
if err := validation.ValidateMixedArguments(cmd.PersistentFlags()); err != nil {
return err
}
return validation.ValidateNodeConfiguration(j.cfg).ToAggregate()
}
// Run executes worker node provisioning and tries to join an existing cluster.
func (j *Join) Run(out io.Writer) error {
cfg, err := discovery.For(j.cfg)
if err != nil {
return err
}
client, err := kubeconfigutil.KubeConfigToClientSet(cfg)
if err != nil {
return err
}
if err := kubeadmnode.ValidateAPIServer(client); err != nil {
return err
}
kubeconfigFile := filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.KubeletBootstrapKubeConfigFileName)
// Depending on the kubelet version, we might perform the TLS bootstrap or not
kubeletVersionBytes, err := exec.Command("sh", "-c", "kubelet --version").Output()
// In case the command executed successfully and returned v1.7-something, we'll perform TLS Bootstrapping
// Otherwise, just assume v1.8
// TODO: In the beginning of the v1.9 cycle, we can remove the logic as we then don't support v1.7 anymore
if err == nil && strings.HasPrefix(string(kubeletVersionBytes), "Kubernetes v1.7") {
hostname := nodeutil.GetHostname(j.cfg.NodeName)
if err := kubeadmnode.PerformTLSBootstrap(cfg, hostname); err != nil {
return err
}
// As we now performed the TLS Bootstrap, change the filepath to be kubelet.conf instead of bootstrap-kubelet.conf
kubeconfigFile = filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.KubeletKubeConfigFileName)
}
// Write the bootstrap kubelet config file or the TLS-Boostrapped kubelet config file down to disk
if err := kubeconfigutil.WriteToDisk(kubeconfigFile, cfg); err != nil {
return err
}
// Write the ca certificate to disk so kubelet can use it for authentication
cluster := cfg.Contexts[cfg.CurrentContext].Cluster
err = certutil.WriteCert(j.cfg.CACertPath, cfg.Clusters[cluster].CertificateAuthorityData)
if err != nil {
return fmt.Errorf("couldn't save the CA certificate to disk: %v", err)
}
fmt.Fprintf(out, joinDoneMsgf)
return nil
}

View file

@ -0,0 +1,89 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"addons.go",
"bootstraptoken.go",
"certs.go",
"controlplane.go",
"etcd.go",
"kubeconfig.go",
"markmaster.go",
"phase.go",
"preflight.go",
"selfhosting.go",
"uploadconfig.go",
"util.go",
],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
"//cmd/kubeadm/app/cmd/util:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/features:go_default_library",
"//cmd/kubeadm/app/phases/addons/dns:go_default_library",
"//cmd/kubeadm/app/phases/addons/proxy:go_default_library",
"//cmd/kubeadm/app/phases/bootstraptoken/clusterinfo:go_default_library",
"//cmd/kubeadm/app/phases/bootstraptoken/node:go_default_library",
"//cmd/kubeadm/app/phases/certs:go_default_library",
"//cmd/kubeadm/app/phases/controlplane:go_default_library",
"//cmd/kubeadm/app/phases/etcd:go_default_library",
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
"//cmd/kubeadm/app/phases/markmaster:go_default_library",
"//cmd/kubeadm/app/phases/selfhosting:go_default_library",
"//cmd/kubeadm/app/phases/uploadconfig:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
"//cmd/kubeadm/app/util/apiclient:go_default_library",
"//cmd/kubeadm/app/util/config:go_default_library",
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
"//pkg/api:go_default_library",
"//pkg/util/version:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"addons_test.go",
"certs_test.go",
"controlplane_test.go",
"etcd_test.go",
"kubeconfig_test.go",
],
library = ":go_default_library",
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/install:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/phases/certs/pkiutil:go_default_library",
"//cmd/kubeadm/test:go_default_library",
"//cmd/kubeadm/test/cmd:go_default_library",
"//cmd/kubeadm/test/kubeconfig:go_default_library",
"//pkg/util/node:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,150 @@
/*
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 phases
import (
"github.com/spf13/cobra"
clientset "k8s.io/client-go/kubernetes"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
dnsaddon "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns"
proxyaddon "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/proxy"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
"k8s.io/kubernetes/pkg/api"
)
// NewCmdAddon returns the addon Cobra command
func NewCmdAddon() *cobra.Command {
cmd := &cobra.Command{
Use: "addon <addon-name>",
Aliases: []string{"addons"},
Short: "Install an addon to a Kubernetes cluster.",
RunE: cmdutil.SubCmdRunE("addon"),
}
cmd.AddCommand(getAddonsSubCommands()...)
return cmd
}
// EnsureAllAddons install all addons to a Kubernetes cluster.
func EnsureAllAddons(cfg *kubeadmapi.MasterConfiguration, client clientset.Interface) error {
addonActions := []func(cfg *kubeadmapi.MasterConfiguration, client clientset.Interface) error{
dnsaddon.EnsureDNSAddon,
proxyaddon.EnsureProxyAddon,
}
for _, action := range addonActions {
err := action(cfg, client)
if err != nil {
return err
}
}
return nil
}
// getAddonsSubCommands returns sub commands for addons phase
func getAddonsSubCommands() []*cobra.Command {
cfg := &kubeadmapiext.MasterConfiguration{}
// Default values for the cobra help text
api.Scheme.Default(cfg)
var cfgPath, kubeConfigFile string
var subCmds []*cobra.Command
subCmdProperties := []struct {
use string
short string
cmdFunc func(cfg *kubeadmapi.MasterConfiguration, client clientset.Interface) error
}{
{
use: "all",
short: "Install all addons to a Kubernetes cluster",
cmdFunc: EnsureAllAddons,
},
{
use: "kube-dns",
short: "Install the kube-dns addon to a Kubernetes cluster.",
cmdFunc: dnsaddon.EnsureDNSAddon,
},
{
use: "kube-proxy",
short: "Install the kube-proxy addon to a Kubernetes cluster.",
cmdFunc: proxyaddon.EnsureProxyAddon,
},
}
for _, properties := range subCmdProperties {
// Creates the UX Command
cmd := &cobra.Command{
Use: properties.use,
Short: properties.short,
Run: runAddonsCmdFunc(properties.cmdFunc, cfg, &kubeConfigFile, &cfgPath),
}
// Add flags to the command
cmd.Flags().StringVar(&kubeConfigFile, "kubeconfig", "/etc/kubernetes/admin.conf", "The KubeConfig file to use for talking to the cluster")
cmd.Flags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)")
cmd.Flags().StringVar(&cfg.KubernetesVersion, "kubernetes-version", cfg.KubernetesVersion, `Choose a specific Kubernetes version for the control plane.`)
cmd.Flags().StringVar(&cfg.ImageRepository, "image-repository", cfg.ImageRepository, `Choose a container registry to pull control plane images from.`)
if properties.use == "all" || properties.use == "kube-proxy" {
cmd.Flags().StringVar(&cfg.API.AdvertiseAddress, "apiserver-advertise-address", cfg.API.AdvertiseAddress, `The IP address the API Server will advertise it's listening on. 0.0.0.0 means the default network interface's address.`)
cmd.Flags().Int32Var(&cfg.API.BindPort, "apiserver-bind-port", cfg.API.BindPort, `Port for the API Server to bind to.`)
cmd.Flags().StringVar(&cfg.Networking.PodSubnet, "pod-network-cidr", cfg.Networking.PodSubnet, `Specify range of IP addresses for the pod network; if set, the control plane will automatically allocate CIDRs for every node.`)
}
if properties.use == "all" || properties.use == "kube-dns" {
cmd.Flags().StringVar(&cfg.Networking.DNSDomain, "service-dns-domain", cfg.Networking.DNSDomain, `Use alternative domain for services, e.g. "myorg.internal.`)
}
subCmds = append(subCmds, cmd)
}
return subCmds
}
// runAddonsCmdFunc creates a cobra.Command Run function, by composing the call to the given cmdFunc with necessary additional steps (e.g preparation of input parameters)
func runAddonsCmdFunc(cmdFunc func(cfg *kubeadmapi.MasterConfiguration, client clientset.Interface) error, cfg *kubeadmapiext.MasterConfiguration, kubeConfigFile *string, cfgPath *string) func(cmd *cobra.Command, args []string) {
// the following statement build a clousure that wraps a call to a cmdFunc, binding
// the function itself with the specific parameters of each sub command.
// Please note that specific parameter should be passed as value, while other parameters - passed as reference -
// are shared between sub commands and gets access to current value e.g. flags value.
return func(cmd *cobra.Command, args []string) {
if err := validation.ValidateMixedArguments(cmd.Flags()); err != nil {
kubeadmutil.CheckErr(err)
}
internalcfg := &kubeadmapi.MasterConfiguration{}
api.Scheme.Convert(cfg, internalcfg, nil)
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
kubeadmutil.CheckErr(err)
internalcfg, err = configutil.ConfigFileAndDefaultsToInternalConfig(*cfgPath, cfg)
kubeadmutil.CheckErr(err)
// Execute the cmdFunc
err = cmdFunc(internalcfg, client)
kubeadmutil.CheckErr(err)
}
}

View file

@ -0,0 +1,72 @@
/*
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 phases
import (
"testing"
// required for triggering api machinery startup when running unit tests
_ "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/install"
cmdtestutil "k8s.io/kubernetes/cmd/kubeadm/test/cmd"
)
func TestAddonsSubCommandsHasFlags(t *testing.T) {
subCmds := getAddonsSubCommands()
commonFlags := []string{
"kubeconfig",
"config",
"kubernetes-version",
"image-repository",
}
var tests = []struct {
command string
additionalFlags []string
}{
{
command: "all",
additionalFlags: []string{
"apiserver-advertise-address",
"apiserver-bind-port",
"pod-network-cidr",
"service-dns-domain",
},
},
{
command: "kube-proxy",
additionalFlags: []string{
"apiserver-advertise-address",
"apiserver-bind-port",
"pod-network-cidr",
},
},
{
command: "kube-dns",
additionalFlags: []string{
"service-dns-domain",
},
},
}
for _, test := range tests {
expectedFlags := append(commonFlags, test.additionalFlags...)
cmdtestutil.AssertSubCommandHasFlags(t, subCmds, test.command, expectedFlags...)
}
}

View file

@ -0,0 +1,143 @@
/*
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 phases
import (
"fmt"
"github.com/spf13/cobra"
clientset "k8s.io/client-go/kubernetes"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/clusterinfo"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
versionutil "k8s.io/kubernetes/pkg/util/version"
)
// NewCmdBootstrapToken returns the Cobra command for running the mark-master phase
func NewCmdBootstrapToken() *cobra.Command {
var kubeConfigFile string
cmd := &cobra.Command{
Use: "bootstrap-token",
Short: "Manage kubeadm-specific Bootstrap Token functions.",
Aliases: []string{"bootstraptoken"},
RunE: cmdutil.SubCmdRunE("bootstrap-token"),
}
cmd.PersistentFlags().StringVar(&kubeConfigFile, "kubeconfig", "/etc/kubernetes/admin.conf", "The KubeConfig file to use for talking to the cluster")
// Add subcommands
cmd.AddCommand(NewSubCmdClusterInfo(&kubeConfigFile))
cmd.AddCommand(NewSubCmdNodeBootstrapToken(&kubeConfigFile))
return cmd
}
// NewSubCmdClusterInfo returns the Cobra command for running the cluster-info sub-phase
func NewSubCmdClusterInfo(kubeConfigFile *string) *cobra.Command {
cmd := &cobra.Command{
Use: "cluster-info <clusterinfo-file>",
Short: "Uploads and exposes the cluster-info ConfigMap publicly from the given cluster-info file",
Aliases: []string{"clusterinfo"},
Run: func(cmd *cobra.Command, args []string) {
err := cmdutil.ValidateExactArgNumber(args, []string{"clusterinfo-file"})
kubeadmutil.CheckErr(err)
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
kubeadmutil.CheckErr(err)
// Here it's safe to get args[0], since we've validated that the argument exists above in validateExactArgNumber
clusterInfoFile := args[0]
// Create the cluster-info ConfigMap or update if it already exists
err = clusterinfo.CreateBootstrapConfigMapIfNotExists(client, clusterInfoFile)
kubeadmutil.CheckErr(err)
// Create the RBAC rules that expose the cluster-info ConfigMap properly
err = clusterinfo.CreateClusterInfoRBACRules(client)
kubeadmutil.CheckErr(err)
},
}
return cmd
}
// NewSubCmdNodeBootstrapToken returns the Cobra command for running the node sub-phase
func NewSubCmdNodeBootstrapToken(kubeConfigFile *string) *cobra.Command {
cmd := &cobra.Command{
Use: "node",
Short: "Manages Node Bootstrap Tokens",
Aliases: []string{"clusterinfo"},
RunE: cmdutil.SubCmdRunE("node"),
}
cmd.AddCommand(NewSubCmdNodeBootstrapTokenPostCSRs(kubeConfigFile))
cmd.AddCommand(NewSubCmdNodeBootstrapTokenAutoApprove(kubeConfigFile))
return cmd
}
// NewSubCmdNodeBootstrapTokenPostCSRs returns the Cobra command for running the allow-post-csrs sub-phase
func NewSubCmdNodeBootstrapTokenPostCSRs(kubeConfigFile *string) *cobra.Command {
cmd := &cobra.Command{
Use: "allow-post-csrs",
Short: "Configure RBAC to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials",
Run: func(cmd *cobra.Command, args []string) {
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
kubeadmutil.CheckErr(err)
clusterVersion, err := getClusterVersion(client)
kubeadmutil.CheckErr(err)
err = node.AllowBootstrapTokensToPostCSRs(client, clusterVersion)
kubeadmutil.CheckErr(err)
},
}
return cmd
}
// NewSubCmdNodeBootstrapToken returns the Cobra command for running the allow-auto-approve sub-phase
func NewSubCmdNodeBootstrapTokenAutoApprove(kubeConfigFile *string) *cobra.Command {
cmd := &cobra.Command{
Use: "allow-auto-approve",
Short: "Configure RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token",
Run: func(cmd *cobra.Command, args []string) {
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
kubeadmutil.CheckErr(err)
clusterVersion, err := getClusterVersion(client)
kubeadmutil.CheckErr(err)
err = node.AutoApproveNodeBootstrapTokens(client, clusterVersion)
kubeadmutil.CheckErr(err)
},
}
return cmd
}
// getClusterVersion fetches the API server version and parses it
func getClusterVersion(client clientset.Interface) (*versionutil.Version, error) {
clusterVersionInfo, err := client.Discovery().ServerVersion()
if err != nil {
return nil, fmt.Errorf("failed to check server version: %v", err)
}
clusterVersion, err := versionutil.ParseSemantic(clusterVersionInfo.String())
if err != nil {
return nil, fmt.Errorf("failed to parse server version: %v", err)
}
return clusterVersion, nil
}

View file

@ -0,0 +1,150 @@
/*
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 phases
import (
"github.com/spf13/cobra"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
"k8s.io/kubernetes/pkg/api"
)
// NewCmdCerts return main command for certs phase
func NewCmdCerts() *cobra.Command {
cmd := &cobra.Command{
Use: "certs",
Aliases: []string{"certificates"},
Short: "Generate certificates for a Kubernetes cluster.",
RunE: cmdutil.SubCmdRunE("certs"),
}
cmd.AddCommand(getCertsSubCommands("")...)
return cmd
}
// getCertsSubCommands returns sub commands for certs phase
func getCertsSubCommands(defaultKubernetesVersion string) []*cobra.Command {
cfg := &kubeadmapiext.MasterConfiguration{}
// This is used for unit testing only...
// If we wouldn't set this to something, the code would dynamically look up the version from the internet
// By setting this explicitely for tests workarounds that
if defaultKubernetesVersion != "" {
cfg.KubernetesVersion = defaultKubernetesVersion
}
// Default values for the cobra help text
api.Scheme.Default(cfg)
var cfgPath string
var subCmds []*cobra.Command
subCmdProperties := []struct {
use string
short string
cmdFunc func(cfg *kubeadmapi.MasterConfiguration) error
}{
{
use: "all",
short: "Generate all PKI assets necessary to establish the control plane",
cmdFunc: certsphase.CreatePKIAssets,
},
{
use: "ca",
short: "Generate CA certificate and key for a Kubernetes cluster.",
cmdFunc: certsphase.CreateCACertAndKeyfiles,
},
{
use: "apiserver",
short: "Generate API Server serving certificate and key.",
cmdFunc: certsphase.CreateAPIServerCertAndKeyFiles,
},
{
use: "apiserver-kubelet-client",
short: "Generate a client certificate for the API Server to connect to the kubelets securely.",
cmdFunc: certsphase.CreateAPIServerKubeletClientCertAndKeyFiles,
},
{
use: "sa",
short: "Generate a private key for signing service account tokens along with its public key.",
cmdFunc: certsphase.CreateServiceAccountKeyAndPublicKeyFiles,
},
{
use: "front-proxy-ca",
short: "Generate front proxy CA certificate and key for a Kubernetes cluster.",
cmdFunc: certsphase.CreateFrontProxyCACertAndKeyFiles,
},
{
use: "front-proxy-client",
short: "Generate front proxy CA client certificate and key for a Kubernetes cluster.",
cmdFunc: certsphase.CreateFrontProxyClientCertAndKeyFiles,
},
}
for _, properties := range subCmdProperties {
// Creates the UX Command
cmd := &cobra.Command{
Use: properties.use,
Short: properties.short,
Run: runCmdFunc(properties.cmdFunc, &cfgPath, cfg),
}
// Add flags to the command
cmd.Flags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)")
cmd.Flags().StringVar(&cfg.CertificatesDir, "cert-dir", cfg.CertificatesDir, "The path where to save and store the certificates")
if properties.use == "all" || properties.use == "apiserver" {
cmd.Flags().StringVar(&cfg.Networking.DNSDomain, "service-dns-domain", cfg.Networking.DNSDomain, "Use alternative domain for services, e.g. \"myorg.internal\"")
cmd.Flags().StringVar(&cfg.Networking.ServiceSubnet, "service-cidr", cfg.Networking.ServiceSubnet, "Use alternative range of IP address for service VIPs")
cmd.Flags().StringSliceVar(&cfg.APIServerCertSANs, "apiserver-cert-extra-sans", []string{}, "Optional extra altnames to use for the API Server serving cert. Can be both IP addresses and dns names.")
cmd.Flags().StringVar(&cfg.API.AdvertiseAddress, "apiserver-advertise-address", cfg.API.AdvertiseAddress, "The IP address the API Server will advertise it's listening on. 0.0.0.0 means the default network interface's address.")
}
subCmds = append(subCmds, cmd)
}
return subCmds
}
// runCmdFunc creates a cobra.Command Run function, by composing the call to the given cmdFunc with necessary additional steps (e.g preparation of input parameters)
func runCmdFunc(cmdFunc func(cfg *kubeadmapi.MasterConfiguration) error, cfgPath *string, cfg *kubeadmapiext.MasterConfiguration) func(cmd *cobra.Command, args []string) {
// the following statement build a clousure that wraps a call to a cmdFunc, binding
// the function itself with the specific parameters of each sub command.
// Please note that specific parameter should be passed as value, while other parameters - passed as reference -
// are shared between sub commands and gets access to current value e.g. flags value.
return func(cmd *cobra.Command, args []string) {
if err := validation.ValidateMixedArguments(cmd.Flags()); err != nil {
kubeadmutil.CheckErr(err)
}
// 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)
kubeadmutil.CheckErr(err)
// Execute the cmdFunc
err = cmdFunc(internalcfg)
kubeadmutil.CheckErr(err)
}
}

View file

@ -0,0 +1,240 @@
/*
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 phases
import (
"fmt"
"os"
"testing"
// required for triggering api machinery startup when running unit tests
_ "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/install"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/pkiutil"
"k8s.io/kubernetes/pkg/util/node"
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
cmdtestutil "k8s.io/kubernetes/cmd/kubeadm/test/cmd"
)
// phaseTestK8sVersion is a fake kubernetes version to use when testing
const phaseTestK8sVersion = "v1.8.0"
func TestCertsSubCommandsHasFlags(t *testing.T) {
subCmds := getCertsSubCommands(phaseTestK8sVersion)
commonFlags := []string{
"cert-dir",
"config",
}
var tests = []struct {
command string
additionalFlags []string
}{
{
command: "all",
additionalFlags: []string{
"apiserver-advertise-address",
"apiserver-cert-extra-sans",
"service-cidr",
"service-dns-domain",
},
},
{
command: "ca",
},
{
command: "apiserver",
additionalFlags: []string{
"apiserver-advertise-address",
"apiserver-cert-extra-sans",
"service-cidr",
"service-dns-domain",
},
},
{
command: "apiserver-kubelet-client",
},
{
command: "sa",
},
{
command: "front-proxy-ca",
},
{
command: "front-proxy-client",
},
}
for _, test := range tests {
expectedFlags := append(commonFlags, test.additionalFlags...)
cmdtestutil.AssertSubCommandHasFlags(t, subCmds, test.command, expectedFlags...)
}
}
func TestSubCmdCertsCreateFilesWithFlags(t *testing.T) {
subCmds := getCertsSubCommands(phaseTestK8sVersion)
var tests = []struct {
subCmds []string
expectedFiles []string
}{
{
subCmds: []string{"all"},
expectedFiles: []string{
kubeadmconstants.CACertName, kubeadmconstants.CAKeyName,
kubeadmconstants.APIServerCertName, kubeadmconstants.APIServerKeyName,
kubeadmconstants.APIServerKubeletClientCertName, kubeadmconstants.APIServerKubeletClientKeyName,
kubeadmconstants.ServiceAccountPrivateKeyName, kubeadmconstants.ServiceAccountPublicKeyName,
kubeadmconstants.FrontProxyCACertName, kubeadmconstants.FrontProxyCAKeyName,
kubeadmconstants.FrontProxyClientCertName, kubeadmconstants.FrontProxyClientKeyName,
},
},
{
subCmds: []string{"ca", "apiserver", "apiserver-kubelet-client"},
expectedFiles: []string{kubeadmconstants.CACertName, kubeadmconstants.CAKeyName, kubeadmconstants.APIServerCertName, kubeadmconstants.APIServerKeyName, kubeadmconstants.APIServerKubeletClientCertName, kubeadmconstants.APIServerKubeletClientKeyName},
},
{
subCmds: []string{"sa"},
expectedFiles: []string{kubeadmconstants.ServiceAccountPrivateKeyName, kubeadmconstants.ServiceAccountPublicKeyName},
},
{
subCmds: []string{"front-proxy-ca", "front-proxy-client"},
expectedFiles: []string{kubeadmconstants.FrontProxyCACertName, kubeadmconstants.FrontProxyCAKeyName, kubeadmconstants.FrontProxyClientCertName, kubeadmconstants.FrontProxyClientKeyName},
},
}
for _, test := range tests {
// Create temp folder for the test case
tmpdir := testutil.SetupTempDir(t)
defer os.RemoveAll(tmpdir)
// executes given sub commands
for _, subCmdName := range test.subCmds {
certDirFlag := fmt.Sprintf("--cert-dir=%s", tmpdir)
cmdtestutil.RunSubCommand(t, subCmds, subCmdName, certDirFlag)
}
// verify expected files are there
testutil.AssertFileExists(t, tmpdir, test.expectedFiles...)
}
}
func TestSubCmdCertsApiServerForwardsFlags(t *testing.T) {
subCmds := getCertsSubCommands(phaseTestK8sVersion)
// Create temp folder for the test case
tmpdir := testutil.SetupTempDir(t)
defer os.RemoveAll(tmpdir)
// creates ca cert
certDirFlag := fmt.Sprintf("--cert-dir=%s", tmpdir)
cmdtestutil.RunSubCommand(t, subCmds, "ca", certDirFlag)
// creates apiserver cert
apiserverFlags := []string{
fmt.Sprintf("--cert-dir=%s", tmpdir),
"--apiserver-cert-extra-sans=foo,boo",
"--service-cidr=10.0.0.0/24",
"--service-dns-domain=mycluster.local",
"--apiserver-advertise-address=1.2.3.4",
}
cmdtestutil.RunSubCommand(t, subCmds, "apiserver", apiserverFlags...)
// asserts created cert has values from CLI flags
APIserverCert, err := pkiutil.TryLoadCertFromDisk(tmpdir, kubeadmconstants.APIServerCertAndKeyBaseName)
if err != nil {
t.Fatalf("Error loading API server certificate: %v", err)
}
hostname := node.GetHostname("")
for i, name := range []string{hostname, "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.mycluster.local"} {
if APIserverCert.DNSNames[i] != name {
t.Errorf("APIserverCert.DNSNames[%d] is %s instead of %s", i, APIserverCert.DNSNames[i], name)
}
}
for i, ip := range []string{"10.0.0.1", "1.2.3.4"} {
if APIserverCert.IPAddresses[i].String() != ip {
t.Errorf("APIserverCert.IPAddresses[%d] is %s instead of %s", i, APIserverCert.IPAddresses[i], ip)
}
}
}
func TestSubCmdCertsCreateFilesWithConfigFile(t *testing.T) {
subCmds := getCertsSubCommands(phaseTestK8sVersion)
var tests = []struct {
subCmds []string
expectedFiles []string
}{
{
subCmds: []string{"all"},
expectedFiles: []string{
kubeadmconstants.CACertName, kubeadmconstants.CAKeyName,
kubeadmconstants.APIServerCertName, kubeadmconstants.APIServerKeyName,
kubeadmconstants.APIServerKubeletClientCertName, kubeadmconstants.APIServerKubeletClientKeyName,
kubeadmconstants.ServiceAccountPrivateKeyName, kubeadmconstants.ServiceAccountPublicKeyName,
kubeadmconstants.FrontProxyCACertName, kubeadmconstants.FrontProxyCAKeyName,
kubeadmconstants.FrontProxyClientCertName, kubeadmconstants.FrontProxyClientKeyName,
},
},
{
subCmds: []string{"ca", "apiserver", "apiserver-kubelet-client"},
expectedFiles: []string{kubeadmconstants.CACertName, kubeadmconstants.CAKeyName, kubeadmconstants.APIServerCertName, kubeadmconstants.APIServerKeyName, kubeadmconstants.APIServerKubeletClientCertName, kubeadmconstants.APIServerKubeletClientKeyName},
},
{
subCmds: []string{"front-proxy-ca", "front-proxy-client"},
expectedFiles: []string{kubeadmconstants.FrontProxyCACertName, kubeadmconstants.FrontProxyCAKeyName, kubeadmconstants.FrontProxyClientCertName, kubeadmconstants.FrontProxyClientKeyName},
},
{
subCmds: []string{"sa"},
expectedFiles: []string{kubeadmconstants.ServiceAccountPrivateKeyName, kubeadmconstants.ServiceAccountPublicKeyName},
},
}
for _, test := range tests {
// Create temp folder for the test case
tmpdir := testutil.SetupTempDir(t)
defer os.RemoveAll(tmpdir)
certdir := tmpdir
cfg := &kubeadmapi.MasterConfiguration{
API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4", BindPort: 1234},
CertificatesDir: certdir,
NodeName: "valid-node-name",
}
configPath := testutil.SetupMasterConfigurationFile(t, tmpdir, cfg)
// executes given sub commands
for _, subCmdName := range test.subCmds {
configFlag := fmt.Sprintf("--config=%s", configPath)
cmdtestutil.RunSubCommand(t, subCmds, subCmdName, configFlag)
}
// verify expected files are there
testutil.AssertFileExists(t, tmpdir, test.expectedFiles...)
}
}

View file

@ -0,0 +1,116 @@
/*
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 phases
import (
"github.com/spf13/cobra"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
controlplanephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane"
"k8s.io/kubernetes/pkg/api"
)
// NewCmdControlplane return main command for Controlplane phase
func NewCmdControlplane() *cobra.Command {
cmd := &cobra.Command{
Use: "controlplane",
Short: "Generate all static pod manifest files necessary to establish the control plane.",
RunE: cmdutil.SubCmdRunE("controlplane"),
}
manifestPath := kubeadmconstants.GetStaticPodDirectory()
cmd.AddCommand(getControlPlaneSubCommands(manifestPath, "")...)
return cmd
}
// getControlPlaneSubCommands returns sub commands for Controlplane phase
func getControlPlaneSubCommands(outDir, defaultKubernetesVersion string) []*cobra.Command {
cfg := &kubeadmapiext.MasterConfiguration{}
// This is used for unit testing only...
// If we wouldn't set this to something, the code would dynamically look up the version from the internet
// By setting this explicitely for tests workarounds that
if defaultKubernetesVersion != "" {
cfg.KubernetesVersion = defaultKubernetesVersion
}
// Default values for the cobra help text
api.Scheme.Default(cfg)
var cfgPath string
var subCmds []*cobra.Command
subCmdProperties := []struct {
use string
short string
cmdFunc func(outDir string, cfg *kubeadmapi.MasterConfiguration) error
}{
{
use: "all",
short: "Generate all static pod manifest files necessary to establish the control plane.",
cmdFunc: controlplanephase.CreateInitStaticPodManifestFiles,
},
{
use: "apiserver",
short: "Generate apiserver static pod manifest.",
cmdFunc: controlplanephase.CreateAPIServerStaticPodManifestFile,
},
{
use: "controller-manager",
short: "Generate controller-manager static pod manifest.",
cmdFunc: controlplanephase.CreateControllerManagerStaticPodManifestFile,
},
{
use: "scheduler",
short: "Generate scheduler static pod manifest.",
cmdFunc: controlplanephase.CreateSchedulerStaticPodManifestFile,
},
}
for _, properties := range subCmdProperties {
// Creates the UX Command
cmd := &cobra.Command{
Use: properties.use,
Short: properties.short,
Run: runCmdPhase(properties.cmdFunc, &outDir, &cfgPath, cfg),
}
// Add flags to the command
cmd.Flags().StringVar(&cfg.CertificatesDir, "cert-dir", cfg.CertificatesDir, `The path where certificates are stored.`)
cmd.Flags().StringVar(&cfg.KubernetesVersion, "kubernetes-version", cfg.KubernetesVersion, `Choose a specific Kubernetes version for the control plane.`)
if properties.use == "all" || properties.use == "apiserver" {
cmd.Flags().StringVar(&cfg.API.AdvertiseAddress, "apiserver-advertise-address", cfg.API.AdvertiseAddress, "The IP address or DNS name the API Server is accessible on.")
cmd.Flags().Int32Var(&cfg.API.BindPort, "apiserver-bind-port", cfg.API.BindPort, "The port the API Server is accessible on.")
cmd.Flags().StringVar(&cfg.Networking.ServiceSubnet, "service-cidr", cfg.Networking.ServiceSubnet, "The range of IP address used for service VIPs.")
}
if properties.use == "all" || properties.use == "controller-manager" {
cmd.Flags().StringVar(&cfg.Networking.PodSubnet, "pod-network-cidr", cfg.Networking.PodSubnet, "The range of IP addresses used for the pod network.")
}
cmd.Flags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)")
subCmds = append(subCmds, cmd)
}
return subCmds
}

View file

@ -0,0 +1,150 @@
/*
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 phases
import (
"fmt"
"os"
"testing"
// required for triggering api machinery startup when running unit tests
_ "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/install"
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
cmdtestutil "k8s.io/kubernetes/cmd/kubeadm/test/cmd"
)
func TestControlPlaneSubCommandsHasFlags(t *testing.T) {
subCmds := getControlPlaneSubCommands("", phaseTestK8sVersion)
commonFlags := []string{
"cert-dir",
"config",
}
var tests = []struct {
command string
additionalFlags []string
}{
{
command: "all",
additionalFlags: []string{
"kubernetes-version",
"apiserver-advertise-address",
"apiserver-bind-port",
"service-cidr",
"pod-network-cidr",
},
},
{
command: "apiserver",
additionalFlags: []string{
"kubernetes-version",
"apiserver-advertise-address",
"apiserver-bind-port",
"service-cidr",
},
},
{
command: "controller-manager",
additionalFlags: []string{
"kubernetes-version",
"pod-network-cidr",
},
},
{
command: "scheduler",
additionalFlags: []string{
"kubernetes-version",
},
},
}
for _, test := range tests {
expectedFlags := append(commonFlags, test.additionalFlags...)
cmdtestutil.AssertSubCommandHasFlags(t, subCmds, test.command, expectedFlags...)
}
}
func TestControlPlaneCreateFilesWithFlags(t *testing.T) {
var tests = []struct {
command string
additionalFlags []string
expectedFiles []string
}{
{
command: "all",
additionalFlags: []string{
"--kubernetes-version=v1.7.0",
"--apiserver-advertise-address=1.2.3.4",
"--apiserver-bind-port=6443",
"--service-cidr=1.2.3.4/16",
"--pod-network-cidr=1.2.3.4/16",
},
expectedFiles: []string{
"kube-apiserver.yaml",
"kube-controller-manager.yaml",
"kube-scheduler.yaml",
},
},
{
command: "apiserver",
additionalFlags: []string{
"--kubernetes-version=v1.7.0",
"--apiserver-advertise-address=1.2.3.4",
"--apiserver-bind-port=6443",
"--service-cidr=1.2.3.4/16",
},
expectedFiles: []string{"kube-apiserver.yaml"},
},
{
command: "controller-manager",
additionalFlags: []string{
"--kubernetes-version=v1.7.0",
"--pod-network-cidr=1.2.3.4/16",
},
expectedFiles: []string{"kube-controller-manager.yaml"},
},
{
command: "scheduler",
additionalFlags: []string{
"--kubernetes-version=v1.7.0",
},
expectedFiles: []string{"kube-scheduler.yaml"},
},
}
for _, test := range tests {
// Create temp folder for the test case
tmpdir := testutil.SetupTempDir(t)
defer os.RemoveAll(tmpdir)
// Get subcommands working in the temporary directory
subCmds := getControlPlaneSubCommands(tmpdir, phaseTestK8sVersion)
// Execute the subcommand
certDirFlag := fmt.Sprintf("--cert-dir=%s", tmpdir)
allFlags := append(test.additionalFlags, certDirFlag)
cmdtestutil.RunSubCommand(t, subCmds, test.command, allFlags...)
// Checks that requested files are there
testutil.AssertFileExists(t, tmpdir, test.expectedFiles...)
}
}

View file

@ -0,0 +1,85 @@
/*
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 phases
import (
"github.com/spf13/cobra"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
etcdphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/etcd"
"k8s.io/kubernetes/pkg/api"
)
// NewCmdEtcd return main command for Etcd phase
func NewCmdEtcd() *cobra.Command {
cmd := &cobra.Command{
Use: "etcd",
Short: "Generate static pod manifest file for etcd.",
RunE: cmdutil.SubCmdRunE("etcd"),
}
manifestPath := kubeadmconstants.GetStaticPodDirectory()
cmd.AddCommand(getEtcdSubCommands(manifestPath, "")...)
return cmd
}
// getEtcdSubCommands returns sub commands for etcd phase
func getEtcdSubCommands(outDir, defaultKubernetesVersion string) []*cobra.Command {
cfg := &kubeadmapiext.MasterConfiguration{}
// This is used for unit testing only...
// If we wouldn't set this to something, the code would dynamically look up the version from the internet
// By setting this explicitely for tests workarounds that
if defaultKubernetesVersion != "" {
cfg.KubernetesVersion = defaultKubernetesVersion
}
// Default values for the cobra help text
api.Scheme.Default(cfg)
var cfgPath string
var subCmds []*cobra.Command
properties := struct {
use string
short string
cmdFunc func(outDir string, cfg *kubeadmapi.MasterConfiguration) error
}{
use: "local",
short: "Generate static pod manifest file for a local, single-node etcd instance.",
cmdFunc: etcdphase.CreateLocalEtcdStaticPodManifestFile,
}
// Creates the UX Command
cmd := &cobra.Command{
Use: properties.use,
Short: properties.short,
Run: runCmdPhase(properties.cmdFunc, &outDir, &cfgPath, cfg),
}
// Add flags to the command
cmd.Flags().StringVar(&cfg.CertificatesDir, "cert-dir", cfg.CertificatesDir, `The path where certificates are stored`)
cmd.Flags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)")
subCmds = append(subCmds, cmd)
return subCmds
}

View file

@ -0,0 +1,86 @@
/*
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 phases
import (
"fmt"
"os"
"testing"
// required for triggering api machinery startup when running unit tests
_ "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/install"
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
cmdtestutil "k8s.io/kubernetes/cmd/kubeadm/test/cmd"
)
func TestEtcdSubCommandsHasFlags(t *testing.T) {
subCmds := getEtcdSubCommands("", phaseTestK8sVersion)
commonFlags := []string{
"cert-dir",
"config",
}
var tests = []struct {
command string
additionalFlags []string
}{
{
command: "local",
},
}
for _, test := range tests {
expectedFlags := append(commonFlags, test.additionalFlags...)
cmdtestutil.AssertSubCommandHasFlags(t, subCmds, test.command, expectedFlags...)
}
}
func TestEtcdCreateFilesWithFlags(t *testing.T) {
var tests = []struct {
command string
additionalFlags []string
expectedFiles []string
}{
{
command: "local",
expectedFiles: []string{"etcd.yaml"},
additionalFlags: []string{},
},
}
for _, test := range tests {
// Create temp folder for the test case
tmpdir := testutil.SetupTempDir(t)
defer os.RemoveAll(tmpdir)
// Get subcommands working in the temporary directory
subCmds := getEtcdSubCommands(tmpdir, phaseTestK8sVersion)
// Execute the subcommand
certDirFlag := fmt.Sprintf("--cert-dir=%s", tmpdir)
allFlags := append(test.additionalFlags, certDirFlag)
cmdtestutil.RunSubCommand(t, subCmds, test.command, allFlags...)
// Checks that requested files are there
testutil.AssertFileExists(t, tmpdir, test.expectedFiles...)
}
}

View file

@ -0,0 +1,139 @@
/*
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 phases
import (
"fmt"
"io"
"github.com/spf13/cobra"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
"k8s.io/kubernetes/pkg/api"
)
// NewCmdKubeConfig return main command for kubeconfig phase
func NewCmdKubeConfig(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "kubeconfig",
Short: "Generate all kubeconfig files necessary to establish the control plane and the admin kubeconfig file.",
RunE: cmdutil.SubCmdRunE("kubeconfig"),
}
cmd.AddCommand(getKubeConfigSubCommands(out, kubeadmconstants.KubernetesDir, "")...)
return cmd
}
// getKubeConfigSubCommands returns sub commands for kubeconfig phase
func getKubeConfigSubCommands(out io.Writer, outDir, defaultKubernetesVersion string) []*cobra.Command {
cfg := &kubeadmapiext.MasterConfiguration{}
// This is used for unit testing only...
// If we wouldn't set this to something, the code would dynamically look up the version from the internet
// By setting this explicitely for tests workarounds that
if defaultKubernetesVersion != "" {
cfg.KubernetesVersion = defaultKubernetesVersion
}
// Default values for the cobra help text
api.Scheme.Default(cfg)
var cfgPath, token, clientName string
var subCmds []*cobra.Command
subCmdProperties := []struct {
use string
short string
cmdFunc func(outDir string, cfg *kubeadmapi.MasterConfiguration) error
}{
{
use: "all",
short: "Generate all kubeconfig files necessary to establish the control plane and the admin kubeconfig file.",
cmdFunc: kubeconfigphase.CreateInitKubeConfigFiles,
},
{
use: "admin",
short: "Generate a kubeconfig file for the admin to use and for kubeadm itself.",
cmdFunc: kubeconfigphase.CreateAdminKubeConfigFile,
},
{
use: "kubelet",
short: "Generate a kubeconfig file for the Kubelet to use. Please note that this should *only* be used for bootstrapping purposes. After your control plane is up, you should request all kubelet credentials from the CSR API.",
cmdFunc: kubeconfigphase.CreateKubeletKubeConfigFile,
},
{
use: "controller-manager",
short: "Generate a kubeconfig file for the Controller Manager to use.",
cmdFunc: kubeconfigphase.CreateControllerManagerKubeConfigFile,
},
{
use: "scheduler",
short: "Generate a kubeconfig file for the Scheduler to use.",
cmdFunc: kubeconfigphase.CreateSchedulerKubeConfigFile,
},
{
use: "user",
short: "Outputs a kubeconfig file for an additional user.",
cmdFunc: func(outDir string, cfg *kubeadmapi.MasterConfiguration) error {
if clientName == "" {
return fmt.Errorf("missing required argument client-name")
}
// if the kubeconfig file for an additional user has to use a token, use it
if token != "" {
return kubeconfigphase.WriteKubeConfigWithToken(out, cfg, clientName, token)
}
// Otherwise, write a kubeconfig file with a generate client cert
return kubeconfigphase.WriteKubeConfigWithClientCert(out, cfg, clientName)
},
},
}
for _, properties := range subCmdProperties {
// Creates the UX Command
cmd := &cobra.Command{
Use: properties.use,
Short: properties.short,
Run: runCmdPhase(properties.cmdFunc, &outDir, &cfgPath, cfg),
}
// Add flags to the command
if properties.use != "user" {
cmd.Flags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)")
}
cmd.Flags().StringVar(&cfg.CertificatesDir, "cert-dir", cfg.CertificatesDir, "The path where certificates are stored.")
cmd.Flags().StringVar(&cfg.API.AdvertiseAddress, "apiserver-advertise-address", cfg.API.AdvertiseAddress, "The IP address or DNS name the API Server is accessible on.")
cmd.Flags().Int32Var(&cfg.API.BindPort, "apiserver-bind-port", cfg.API.BindPort, "The port the API Server is accessible on.")
if properties.use == "all" || properties.use == "kubelet" {
cmd.Flags().StringVar(&cfg.NodeName, "node-name", cfg.NodeName, `The node name that the kubelet client cert should use.`)
}
if properties.use == "user" {
cmd.Flags().StringVar(&token, "token", token, "The token that should be used as the authentication mechanism for this kubeconfig.")
cmd.Flags().StringVar(&clientName, "client-name", clientName, "The name of the KubeConfig user that will be created. Will also be used as the CN if client certs are created.")
}
subCmds = append(subCmds, cmd)
}
return subCmds
}

View file

@ -0,0 +1,383 @@
/*
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 phases
import (
"bytes"
"fmt"
"os"
"path/filepath"
"testing"
// required for triggering api machinery startup when running unit tests
_ "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/install"
"k8s.io/client-go/tools/clientcmd"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/pkiutil"
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
cmdtestutil "k8s.io/kubernetes/cmd/kubeadm/test/cmd"
kubeconfigtestutil "k8s.io/kubernetes/cmd/kubeadm/test/kubeconfig"
)
func TestKubeConfigCSubCommandsHasFlags(t *testing.T) {
subCmds := getKubeConfigSubCommands(nil, "", phaseTestK8sVersion)
commonFlags := []string{
"cert-dir",
"apiserver-advertise-address",
"apiserver-bind-port",
}
var tests = []struct {
command string
additionalFlags []string
}{
{
command: "all",
additionalFlags: []string{
"config",
"node-name",
},
},
{
command: "admin",
additionalFlags: []string{
"config",
},
},
{
command: "kubelet",
additionalFlags: []string{
"config",
"node-name",
},
},
{
command: "controller-manager",
additionalFlags: []string{
"config",
},
},
{
command: "scheduler",
additionalFlags: []string{
"config",
},
},
{
command: "user",
additionalFlags: []string{
"token",
"client-name",
},
},
}
for _, test := range tests {
expectedFlags := append(commonFlags, test.additionalFlags...)
cmdtestutil.AssertSubCommandHasFlags(t, subCmds, test.command, expectedFlags...)
}
}
func TestKubeConfigSubCommandsThatCreateFilesWithFlags(t *testing.T) {
commonFlags := []string{
"--apiserver-advertise-address=1.2.3.4",
"--apiserver-bind-port=1234",
}
var tests = []struct {
command string
additionalFlags []string
expectedFiles []string
}{
{
command: "all",
additionalFlags: []string{"--node-name=valid-nome-name"},
expectedFiles: []string{
kubeadmconstants.AdminKubeConfigFileName,
kubeadmconstants.KubeletKubeConfigFileName,
kubeadmconstants.ControllerManagerKubeConfigFileName,
kubeadmconstants.SchedulerKubeConfigFileName,
},
},
{
command: "admin",
expectedFiles: []string{kubeadmconstants.AdminKubeConfigFileName},
},
{
command: "kubelet",
additionalFlags: []string{"--node-name=valid-nome-name"},
expectedFiles: []string{kubeadmconstants.KubeletKubeConfigFileName},
},
{
command: "controller-manager",
expectedFiles: []string{kubeadmconstants.ControllerManagerKubeConfigFileName},
},
{
command: "scheduler",
expectedFiles: []string{kubeadmconstants.SchedulerKubeConfigFileName},
},
}
var kubeConfigAssertions = map[string]struct {
clientName string
organizations []string
}{
kubeadmconstants.AdminKubeConfigFileName: {
clientName: "kubernetes-admin",
organizations: []string{kubeadmconstants.MastersGroup},
},
kubeadmconstants.KubeletKubeConfigFileName: {
clientName: "system:node:valid-nome-name",
organizations: []string{kubeadmconstants.NodesGroup},
},
kubeadmconstants.ControllerManagerKubeConfigFileName: {
clientName: kubeadmconstants.ControllerManagerUser,
},
kubeadmconstants.SchedulerKubeConfigFileName: {
clientName: kubeadmconstants.SchedulerUser,
},
}
for _, test := range tests {
// Create temp folder for the test case
tmpdir := testutil.SetupTempDir(t)
defer os.RemoveAll(tmpdir)
// Adds a pki folder with a ca certs to the temp folder
pkidir := testutil.SetupPkiDirWithCertificateAuthorithy(t, tmpdir)
// Retrives ca cert for assertions
caCert, _, err := pkiutil.TryLoadCertAndKeyFromDisk(pkidir, kubeadmconstants.CACertAndKeyBaseName)
if err != nil {
t.Fatalf("couldn't retrive ca cert: %v", err)
}
// Get subcommands working in the temporary directory
subCmds := getKubeConfigSubCommands(nil, tmpdir, phaseTestK8sVersion)
// Execute the subcommand
certDirFlag := fmt.Sprintf("--cert-dir=%s", pkidir)
allFlags := append(commonFlags, certDirFlag)
allFlags = append(allFlags, test.additionalFlags...)
cmdtestutil.RunSubCommand(t, subCmds, test.command, allFlags...)
// Checks that requested files are there
testutil.AssertFileExists(t, tmpdir, test.expectedFiles...)
// Checks contents of generated files
for _, file := range test.expectedFiles {
// reads generated files
config, err := clientcmd.LoadFromFile(filepath.Join(tmpdir, file))
if err != nil {
t.Errorf("Couldn't load generated kubeconfig file: %v", err)
}
// checks that CLI flags are properly propagated and kubeconfig properties are correct
kubeconfigtestutil.AssertKubeConfigCurrentCluster(t, config, "https://1.2.3.4:1234", caCert)
expectedClientName := kubeConfigAssertions[file].clientName
expectedOrganizations := kubeConfigAssertions[file].organizations
kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithClientCert(t, config, caCert, expectedClientName, expectedOrganizations...)
}
}
}
func TestKubeConfigSubCommandsThatCreateFilesWithConfigFile(t *testing.T) {
var tests = []struct {
command string
expectedFiles []string
}{
{
command: "all",
expectedFiles: []string{
kubeadmconstants.AdminKubeConfigFileName,
kubeadmconstants.KubeletKubeConfigFileName,
kubeadmconstants.ControllerManagerKubeConfigFileName,
kubeadmconstants.SchedulerKubeConfigFileName,
},
},
{
command: "admin",
expectedFiles: []string{kubeadmconstants.AdminKubeConfigFileName},
},
{
command: "kubelet",
expectedFiles: []string{kubeadmconstants.KubeletKubeConfigFileName},
},
{
command: "controller-manager",
expectedFiles: []string{kubeadmconstants.ControllerManagerKubeConfigFileName},
},
{
command: "scheduler",
expectedFiles: []string{kubeadmconstants.SchedulerKubeConfigFileName},
},
}
var kubeConfigAssertions = map[string]struct {
clientName string
organizations []string
}{
kubeadmconstants.AdminKubeConfigFileName: {
clientName: "kubernetes-admin",
organizations: []string{kubeadmconstants.MastersGroup},
},
kubeadmconstants.KubeletKubeConfigFileName: {
clientName: "system:node:valid-node-name",
organizations: []string{kubeadmconstants.NodesGroup},
},
kubeadmconstants.ControllerManagerKubeConfigFileName: {
clientName: kubeadmconstants.ControllerManagerUser,
},
kubeadmconstants.SchedulerKubeConfigFileName: {
clientName: kubeadmconstants.SchedulerUser,
},
}
for _, test := range tests {
// Create temp folder for the test case
tmpdir := testutil.SetupTempDir(t)
defer os.RemoveAll(tmpdir)
// Adds a pki folder with a ca certs to the temp folder
pkidir := testutil.SetupPkiDirWithCertificateAuthorithy(t, tmpdir)
// Retrives ca cert for assertions
caCert, _, err := pkiutil.TryLoadCertAndKeyFromDisk(pkidir, kubeadmconstants.CACertAndKeyBaseName)
if err != nil {
t.Fatalf("couldn't retrive ca cert: %v", err)
}
// Adds a master configuration file
cfg := &kubeadmapi.MasterConfiguration{
API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4", BindPort: 1234},
CertificatesDir: pkidir,
NodeName: "valid-node-name",
}
cfgPath := testutil.SetupMasterConfigurationFile(t, tmpdir, cfg)
// Get subcommands working in the temporary directory
subCmds := getKubeConfigSubCommands(nil, tmpdir, phaseTestK8sVersion)
// Execute the subcommand
configFlag := fmt.Sprintf("--config=%s", cfgPath)
cmdtestutil.RunSubCommand(t, subCmds, test.command, configFlag)
// Checks that requested files are there
testutil.AssertFileExists(t, tmpdir, test.expectedFiles...)
// Checks contents of generated files
for _, file := range test.expectedFiles {
// reads generated files
config, err := clientcmd.LoadFromFile(filepath.Join(tmpdir, file))
if err != nil {
t.Errorf("Couldn't load generated kubeconfig file: %v", err)
}
// checks that config file properties are properly propagated and kubeconfig properties are correct
kubeconfigtestutil.AssertKubeConfigCurrentCluster(t, config, "https://1.2.3.4:1234", caCert)
expectedClientName := kubeConfigAssertions[file].clientName
expectedOrganizations := kubeConfigAssertions[file].organizations
kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithClientCert(t, config, caCert, expectedClientName, expectedOrganizations...)
}
}
}
func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) {
// Temporary folders for the test case
tmpdir := testutil.SetupTempDir(t)
defer os.RemoveAll(tmpdir)
// Adds a pki folder with a ca cert to the temp folder
pkidir := testutil.SetupPkiDirWithCertificateAuthorithy(t, tmpdir)
// Retrives ca cert for assertions
caCert, _, err := pkiutil.TryLoadCertAndKeyFromDisk(pkidir, kubeadmconstants.CACertAndKeyBaseName)
if err != nil {
t.Fatalf("couldn't retrive ca cert: %v", err)
}
commonFlags := []string{
"--apiserver-advertise-address=1.2.3.4",
"--apiserver-bind-port=1234",
"--client-name=myUser",
fmt.Sprintf("--cert-dir=%s", pkidir),
}
var tests = []struct {
command string
withClientCert bool
withToken bool
additionalFlags []string
}{
{ // Test user subCommand withClientCert
command: "user",
withClientCert: true,
},
{ // Test user subCommand withToken
withToken: true,
command: "user",
additionalFlags: []string{"--token=123456"},
},
}
for _, test := range tests {
buf := new(bytes.Buffer)
// Get subcommands working in the temporary directory
subCmds := getKubeConfigSubCommands(buf, tmpdir, phaseTestK8sVersion)
// Execute the subcommand
allFlags := append(commonFlags, test.additionalFlags...)
cmdtestutil.RunSubCommand(t, subCmds, test.command, allFlags...)
// reads kubeconfig written to stdout
config, err := clientcmd.Load(buf.Bytes())
if err != nil {
t.Errorf("Couldn't read kubeconfig file from buffer: %v", err)
continue
}
// checks that CLI flags are properly propagated
kubeconfigtestutil.AssertKubeConfigCurrentCluster(t, config, "https://1.2.3.4:1234", caCert)
if test.withClientCert {
// checks that kubeconfig files have expected client cert
kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithClientCert(t, config, caCert, "myUser")
}
if test.withToken {
// checks that kubeconfig files have expected token
kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithToken(t, config, "myUser", "123456")
}
}
}

View file

@ -0,0 +1,49 @@
/*
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 phases
import (
"github.com/spf13/cobra"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
markmasterphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/markmaster"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
)
// NewCmdMarkMaster returns the Cobra command for running the mark-master phase
func NewCmdMarkMaster() *cobra.Command {
var kubeConfigFile string
cmd := &cobra.Command{
Use: "mark-master <node-name>",
Short: "Mark a node as master.",
Aliases: []string{"markmaster"},
RunE: func(_ *cobra.Command, args []string) error {
err := cmdutil.ValidateExactArgNumber(args, []string{"node-name"})
kubeadmutil.CheckErr(err)
client, err := kubeconfigutil.ClientSetFromFile(kubeConfigFile)
kubeadmutil.CheckErr(err)
nodeName := args[0]
return markmasterphase.MarkMaster(client, nodeName)
},
}
cmd.Flags().StringVar(&kubeConfigFile, "kubeconfig", "/etc/kubernetes/admin.conf", "The KubeConfig file to use for talking to the cluster")
return cmd
}

View file

@ -0,0 +1,46 @@
/*
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 phases
import (
"io"
"github.com/spf13/cobra"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
)
// NewCmdPhase returns the cobra command for the "kubeadm phase" command (currently alpha-gated)
func NewCmdPhase(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "phase",
Short: "Invoke subsets of kubeadm functions separately for a manual install.",
RunE: cmdutil.SubCmdRunE("phase"),
}
cmd.AddCommand(NewCmdAddon())
cmd.AddCommand(NewCmdBootstrapToken())
cmd.AddCommand(NewCmdCerts())
cmd.AddCommand(NewCmdControlplane())
cmd.AddCommand(NewCmdEtcd())
cmd.AddCommand(NewCmdKubeConfig(out))
cmd.AddCommand(NewCmdMarkMaster())
cmd.AddCommand(NewCmdPreFlight())
cmd.AddCommand(NewCmdSelfhosting())
cmd.AddCommand(NewCmdUploadConfig())
return cmd
}

View file

@ -0,0 +1,63 @@
/*
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 phases
import (
"github.com/spf13/cobra"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
)
func NewCmdPreFlight() *cobra.Command {
cmd := &cobra.Command{
Use: "preflight",
Short: "Run pre-flight checks",
RunE: cmdutil.SubCmdRunE("preflight"),
}
cmd.AddCommand(NewCmdPreFlightMaster())
cmd.AddCommand(NewCmdPreFlightNode())
return cmd
}
func NewCmdPreFlightMaster() *cobra.Command {
cmd := &cobra.Command{
Use: "master",
Short: "Run master pre-flight checks",
RunE: func(cmd *cobra.Command, args []string) error {
cfg := &kubeadmapi.MasterConfiguration{}
return preflight.RunInitMasterChecks(cfg)
},
}
return cmd
}
func NewCmdPreFlightNode() *cobra.Command {
cmd := &cobra.Command{
Use: "node",
Short: "Run node pre-flight checks",
RunE: func(cmd *cobra.Command, args []string) error {
cfg := &kubeadmapi.NodeConfiguration{}
return preflight.RunJoinNodeChecks(cfg)
},
}
return cmd
}

View file

@ -0,0 +1,103 @@
/*
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 phases
import (
"os"
"strings"
"time"
"github.com/spf13/cobra"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/features"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/selfhosting"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
"k8s.io/kubernetes/pkg/api"
)
// NewCmdSelfhosting returns the self-hosting Cobra command
func NewCmdSelfhosting() *cobra.Command {
cmd := &cobra.Command{
Use: "selfhosting",
Aliases: []string{"selfhosted"},
Short: "Make a kubeadm cluster self-hosted.",
RunE: cmdutil.SubCmdRunE("selfhosting"),
}
cmd.AddCommand(getSelfhostingSubCommand())
return cmd
}
// getSelfhostingSubCommand returns sub commands for Selfhosting phase
func getSelfhostingSubCommand() *cobra.Command {
cfg := &kubeadmapiext.MasterConfiguration{}
// Default values for the cobra help text
api.Scheme.Default(cfg)
var cfgPath, kubeConfigFile, featureGatesString string
// Creates the UX Command
cmd := &cobra.Command{
Use: "convert-from-staticpods",
Aliases: []string{"from-staticpods"},
Short: "Converts a Static Pod-hosted control plane into a self-hosted one.",
Run: func(cmd *cobra.Command, args []string) {
var err error
if cfg.FeatureGates, err = features.NewFeatureGate(&features.InitFeatureGates, featureGatesString); err != nil {
kubeadmutil.CheckErr(err)
}
if err := validation.ValidateMixedArguments(cmd.Flags()); err != nil {
kubeadmutil.CheckErr(err)
}
// 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)
kubeadmutil.CheckErr(err)
// Gets the kubernetes client
client, err := kubeconfigutil.ClientSetFromFile(kubeConfigFile)
kubeadmutil.CheckErr(err)
// Converts the Static Pod-hosted control plane into a self-hosted one
waiter := apiclient.NewKubeWaiter(client, 2*time.Minute, os.Stdout)
err = selfhosting.CreateSelfHostedControlPlane(constants.GetStaticPodDirectory(), constants.KubernetesDir, internalcfg, client, waiter)
kubeadmutil.CheckErr(err)
},
}
// Add flags to the command
// flags bound to the configuration object
cmd.Flags().StringVar(&cfg.CertificatesDir, "cert-dir", cfg.CertificatesDir, `The path where certificates are stored`)
cmd.Flags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)")
cmd.Flags().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"))
// flags that are not bound to the configuration object
// Note: All flags that are not bound to the cfg object should be whitelisted in cmd/kubeadm/app/apis/kubeadm/validation/validation.go
cmd.Flags().StringVar(&kubeConfigFile, "kubeconfig", "/etc/kubernetes/admin.conf", "The KubeConfig file to use for talking to the cluster")
return cmd
}

View file

@ -0,0 +1,58 @@
/*
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 phases
import (
"fmt"
"github.com/spf13/cobra"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/uploadconfig"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
)
// NewCmdUploadConfig returns the Cobra command for running the uploadconfig phase
func NewCmdUploadConfig() *cobra.Command {
var cfgPath, kubeConfigFile string
cmd := &cobra.Command{
Use: "upload-config",
Short: "Upload the currently used configuration for kubeadm to a ConfigMap in the cluster for future use in reconfiguration and upgrades of the cluster.",
Aliases: []string{"uploadconfig"},
Run: func(_ *cobra.Command, args []string) {
if len(cfgPath) == 0 {
kubeadmutil.CheckErr(fmt.Errorf("The --config flag is mandatory"))
}
client, err := kubeconfigutil.ClientSetFromFile(kubeConfigFile)
kubeadmutil.CheckErr(err)
defaultcfg := &kubeadmapiext.MasterConfiguration{}
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, defaultcfg)
kubeadmutil.CheckErr(err)
err = uploadconfig.UploadConfiguration(internalcfg, client)
kubeadmutil.CheckErr(err)
},
}
cmd.Flags().StringVar(&kubeConfigFile, "kubeconfig", "/etc/kubernetes/admin.conf", "The KubeConfig file to use for talking to the cluster")
cmd.Flags().StringVar(&cfgPath, "config", "", "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)")
return cmd
}

View file

@ -0,0 +1,50 @@
/*
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 phases
import (
"github.com/spf13/cobra"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
)
// runCmdPhase creates a cobra.Command Run function, by composing the call to the given cmdFunc with necessary additional steps (e.g preparation of input parameters)
func runCmdPhase(cmdFunc func(outDir string, cfg *kubeadmapi.MasterConfiguration) error, outDir, cfgPath *string, cfg *kubeadmapiext.MasterConfiguration) func(cmd *cobra.Command, args []string) {
// the following statement build a clousure that wraps a call to a cmdFunc, binding
// the function itself with the specific parameters of each sub command.
// Please note that specific parameter should be passed as value, while other parameters - passed as reference -
// are shared between sub commands and gets access to current value e.g. flags value.
return func(cmd *cobra.Command, args []string) {
if err := validation.ValidateMixedArguments(cmd.Flags()); err != nil {
kubeadmutil.CheckErr(err)
}
// 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)
kubeadmutil.CheckErr(err)
// Execute the cmdFunc
err = cmdFunc(*outDir, internalcfg)
kubeadmutil.CheckErr(err)
}
}

189
vendor/k8s.io/kubernetes/cmd/kubeadm/app/cmd/reset.go generated vendored Normal file
View file

@ -0,0 +1,189 @@
/*
Copyright 2016 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 cmd
import (
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"github.com/spf13/cobra"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/pkg/util/initsystem"
)
// NewCmdReset returns the "kubeadm reset" command
func NewCmdReset(out io.Writer) *cobra.Command {
var skipPreFlight bool
var certsDir string
cmd := &cobra.Command{
Use: "reset",
Short: "Run this to revert any changes made to this host by 'kubeadm init' or 'kubeadm join'.",
Run: func(cmd *cobra.Command, args []string) {
r, err := NewReset(skipPreFlight, certsDir)
kubeadmutil.CheckErr(err)
kubeadmutil.CheckErr(r.Run(out))
},
}
cmd.PersistentFlags().BoolVar(
&skipPreFlight, "skip-preflight-checks", false,
"Skip preflight checks normally run before modifying the system",
)
cmd.PersistentFlags().StringVar(
&certsDir, "cert-dir", kubeadmapiext.DefaultCertificatesDir,
"The path to the directory where the certificates are stored. If specified, clean this directory.",
)
return cmd
}
type Reset struct {
certsDir string
}
func NewReset(skipPreFlight bool, certsDir string) (*Reset, error) {
if !skipPreFlight {
fmt.Println("[preflight] Running pre-flight checks")
if err := preflight.RunRootCheckOnly(); err != nil {
return nil, err
}
} else {
fmt.Println("[preflight] Skipping pre-flight checks")
}
return &Reset{
certsDir: certsDir,
}, nil
}
// Run reverts any changes made to this host by "kubeadm init" or "kubeadm join".
func (r *Reset) Run(out io.Writer) error {
// Try to stop the kubelet service
initSystem, err := initsystem.GetInitSystem()
if err != nil {
fmt.Println("[reset] WARNING: The kubelet service couldn't be stopped by kubeadm because no supported init system was detected.")
fmt.Println("[reset] WARNING: Please ensure kubelet is stopped manually.")
} else {
fmt.Println("[reset] Stopping the kubelet service")
if err := initSystem.ServiceStop("kubelet"); err != nil {
fmt.Printf("[reset] WARNING: The kubelet service couldn't be stopped by kubeadm: [%v]\n", err)
fmt.Println("[reset] WARNING: Please ensure kubelet is stopped manually.")
}
}
// Try to unmount mounted directories under /var/lib/kubelet in order to be able to remove the /var/lib/kubelet directory later
fmt.Printf("[reset] Unmounting mounted directories in %q\n", "/var/lib/kubelet")
umountDirsCmd := "awk '$2 ~ path {print $2}' path=/var/lib/kubelet /proc/mounts | xargs -r umount"
umountOutputBytes, err := exec.Command("sh", "-c", umountDirsCmd).Output()
if err != nil {
fmt.Printf("[reset] Failed to unmount mounted directories in /var/lib/kubelet: %s\n", string(umountOutputBytes))
}
dockerCheck := preflight.ServiceCheck{Service: "docker", CheckIfActive: true}
if _, errors := dockerCheck.Check(); len(errors) == 0 {
fmt.Println("[reset] Removing kubernetes-managed containers")
if err := exec.Command("sh", "-c", "docker ps -a --filter name=k8s_ -q | xargs -r docker rm --force --volumes").Run(); err != nil {
fmt.Println("[reset] Failed to stop the running containers")
}
} else {
fmt.Println("[reset] docker doesn't seem to be running, skipping the removal of running kubernetes containers")
}
dirsToClean := []string{"/var/lib/kubelet", "/etc/cni/net.d", "/var/lib/dockershim", "/var/run/kubernetes"}
// Only clear etcd data when the etcd manifest is found. In case it is not found, we must assume that the user
// provided external etcd endpoints. In that case, it is his own responsibility to reset etcd
etcdManifestPath := filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.ManifestsSubDirName, "etcd.yaml")
if _, err := os.Stat(etcdManifestPath); err == nil {
dirsToClean = append(dirsToClean, "/var/lib/etcd")
} else {
fmt.Printf("[reset] No etcd manifest found in %q, assuming external etcd.\n", etcdManifestPath)
}
// Then clean contents from the stateful kubelet, etcd and cni directories
fmt.Printf("[reset] Deleting contents of stateful directories: %v\n", dirsToClean)
for _, dir := range dirsToClean {
cleanDir(dir)
}
// Remove contents from the config and pki directories
resetConfigDir(kubeadmconstants.KubernetesDir, r.certsDir)
return nil
}
// cleanDir removes everything in a directory, but not the directory itself
func cleanDir(filePath string) error {
// If the directory doesn't even exist there's nothing to do, and we do
// not consider this an error
if _, err := os.Stat(filePath); os.IsNotExist(err) {
return nil
}
d, err := os.Open(filePath)
if err != nil {
return err
}
defer d.Close()
names, err := d.Readdirnames(-1)
if err != nil {
return err
}
for _, name := range names {
if err = os.RemoveAll(filepath.Join(filePath, name)); err != nil {
return err
}
}
return nil
}
// resetConfigDir is used to cleanup the files kubeadm writes in /etc/kubernetes/.
func resetConfigDir(configPathDir, pkiPathDir string) {
dirsToClean := []string{
filepath.Join(configPathDir, kubeadmconstants.ManifestsSubDirName),
pkiPathDir,
}
fmt.Printf("[reset] Deleting contents of config directories: %v\n", dirsToClean)
for _, dir := range dirsToClean {
if err := cleanDir(dir); err != nil {
fmt.Printf("[reset] Failed to remove directory: %q [%v]\n", dir, err)
}
}
filesToClean := []string{
filepath.Join(configPathDir, kubeadmconstants.AdminKubeConfigFileName),
filepath.Join(configPathDir, kubeadmconstants.KubeletKubeConfigFileName),
filepath.Join(configPathDir, kubeadmconstants.ControllerManagerKubeConfigFileName),
filepath.Join(configPathDir, kubeadmconstants.SchedulerKubeConfigFileName),
}
fmt.Printf("[reset] Deleting files: %v\n", filesToClean)
for _, path := range filesToClean {
if err := os.RemoveAll(path); err != nil {
fmt.Printf("[reset] Failed to remove file: %q [%v]\n", path, err)
}
}
}

View file

@ -0,0 +1,183 @@
/*
Copyright 2016 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 cmd
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
)
func assertExists(t *testing.T, path string) {
if _, err := os.Stat(path); os.IsNotExist(err) {
t.Errorf("file/dir does not exist error: %s", err)
t.Errorf("file/dir does not exist: %s", path)
}
}
func assertNotExists(t *testing.T, path string) {
if _, err := os.Stat(path); err == nil {
t.Errorf("file/dir exists: %s", path)
}
}
// assertDirEmpty verifies a directory either does not exist, or is empty.
func assertDirEmpty(t *testing.T, path string) {
dac := preflight.DirAvailableCheck{Path: path}
_, errors := dac.Check()
if len(errors) != 0 {
t.Errorf("directory not empty: [%v]", errors)
}
}
func TestConfigDirCleaner(t *testing.T) {
tests := map[string]struct {
setupDirs []string
setupFiles []string
verifyExists []string
verifyNotExists []string
}{
"simple reset": {
setupDirs: []string{
"manifests",
"pki",
},
setupFiles: []string{
"manifests/etcd.yaml",
"manifests/kube-apiserver.yaml",
"pki/ca.pem",
kubeadmconstants.AdminKubeConfigFileName,
kubeadmconstants.KubeletKubeConfigFileName,
},
verifyExists: []string{
"manifests",
"pki",
},
},
"partial reset": {
setupDirs: []string{
"pki",
},
setupFiles: []string{
"pki/ca.pem",
kubeadmconstants.KubeletKubeConfigFileName,
},
verifyExists: []string{
"pki",
},
verifyNotExists: []string{
"manifests",
},
},
"preserve cloud-config": {
setupDirs: []string{
"manifests",
"pki",
},
setupFiles: []string{
"manifests/etcd.yaml",
"manifests/kube-apiserver.yaml",
"pki/ca.pem",
kubeadmconstants.AdminKubeConfigFileName,
kubeadmconstants.KubeletKubeConfigFileName,
"cloud-config",
},
verifyExists: []string{
"manifests",
"pki",
"cloud-config",
},
},
"preserve hidden files and directories": {
setupDirs: []string{
"manifests",
"pki",
".mydir",
},
setupFiles: []string{
"manifests/etcd.yaml",
"manifests/kube-apiserver.yaml",
"pki/ca.pem",
kubeadmconstants.AdminKubeConfigFileName,
kubeadmconstants.KubeletKubeConfigFileName,
".cloud-config",
".mydir/.myfile",
},
verifyExists: []string{
"manifests",
"pki",
".cloud-config",
".mydir",
".mydir/.myfile",
},
},
"no-op reset": {
verifyNotExists: []string{
"pki",
"manifests",
},
},
}
for name, test := range tests {
t.Logf("Running test: %s", name)
// Create a temporary directory for our fake config dir:
tmpDir, err := ioutil.TempDir("", "kubeadm-reset-test")
if err != nil {
t.Errorf("Unable to create temp directory: %s", err)
}
defer os.RemoveAll(tmpDir)
for _, createDir := range test.setupDirs {
err := os.Mkdir(filepath.Join(tmpDir, createDir), 0700)
if err != nil {
t.Errorf("Unable to setup test config directory: %s", err)
}
}
for _, createFile := range test.setupFiles {
fullPath := filepath.Join(tmpDir, createFile)
f, err := os.Create(fullPath)
if err != nil {
t.Errorf("Unable to create test file: %s", err)
}
defer f.Close()
}
resetConfigDir(tmpDir, filepath.Join(tmpDir, "pki"))
// Verify the files we cleanup implicitly in every test:
assertExists(t, tmpDir)
assertNotExists(t, filepath.Join(tmpDir, kubeadmconstants.AdminKubeConfigFileName))
assertNotExists(t, filepath.Join(tmpDir, kubeadmconstants.KubeletKubeConfigFileName))
assertDirEmpty(t, filepath.Join(tmpDir, "manifests"))
assertDirEmpty(t, filepath.Join(tmpDir, "pki"))
// Verify the files as requested by the test:
for _, path := range test.verifyExists {
assertExists(t, filepath.Join(tmpDir, path))
}
for _, path := range test.verifyNotExists {
assertNotExists(t, filepath.Join(tmpDir, path))
}
}
}

374
vendor/k8s.io/kubernetes/cmd/kubeadm/app/cmd/token.go generated vendored Normal file
View file

@ -0,0 +1,374 @@
/*
Copyright 2016 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 cmd
import (
"fmt"
"io"
"os"
"sort"
"strings"
"text/tabwriter"
"time"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/util/sets"
clientset "k8s.io/client-go/kubernetes"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
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"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
"k8s.io/kubernetes/pkg/api"
bootstrapapi "k8s.io/kubernetes/pkg/bootstrap/api"
"k8s.io/kubernetes/pkg/printers"
)
func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
var kubeConfigFile string
var dryRun bool
tokenCmd := &cobra.Command{
Use: "token",
Short: "Manage bootstrap tokens.",
Long: dedent.Dedent(`
This command will manage Bootstrap Token for you.
Please note this usage of this command is optional, and mostly for advanced users.
In short, Bootstrap Tokens are used for establishing bidirectional trust between a client and a server.
A Bootstrap Token can be used when a client (for example a node that's about to join the cluster) needs
to trust the server it is talking to. Then a Bootstrap Token with the "signing" usage can be used.
Bootstrap Tokens can also function as a way to allow short-lived authentication to the API Server
(the token serves as a way for the API Server to trust the client), for example for doing the TLS Bootstrap.
What is a Bootstrap Token more exactly?
- It is a Secret in the kube-system namespace of type "bootstrap.kubernetes.io/token".
- A Bootstrap Token must be of the form "[a-z0-9]{6}.[a-z0-9]{16}"; the former part is the public Token ID,
and the latter is the Token Secret, which must be kept private at all circumstances.
- The name of the Secret must be named "bootstrap-token-(token-id)".
You can read more about Bootstrap Tokens in this proposal:
https://git.k8s.io/community/contributors/design-proposals/bootstrap-discovery.md
`),
// Without this callback, if a user runs just the "token"
// command without a subcommand, or with an invalid subcommand,
// cobra will print usage information, but still exit cleanly.
// We want to return an error code in these cases so that the
// user knows that their command was invalid.
RunE: cmdutil.SubCmdRunE("token"),
}
tokenCmd.PersistentFlags().StringVar(&kubeConfigFile,
"kubeconfig", "/etc/kubernetes/admin.conf", "The KubeConfig file to use for talking to the cluster")
tokenCmd.PersistentFlags().BoolVar(&dryRun,
"dry-run", dryRun, "Whether to enable dry-run mode or not")
var usages []string
var extraGroups []string
var tokenDuration time.Duration
var description string
createCmd := &cobra.Command{
Use: "create [token]",
Short: "Create bootstrap tokens on the server.",
Long: dedent.Dedent(`
This command will create a Bootstrap Token for you.
You can specify the usages for this token, the time to live and an optional human friendly description.
The [token] is the actual token to write.
This should be a securely generated random token of the form "[a-z0-9]{6}.[a-z0-9]{16}".
If no [token] is given, kubeadm will generate a random token instead.
`),
Run: func(tokenCmd *cobra.Command, args []string) {
token := ""
if len(args) != 0 {
token = args[0]
}
client, err := getClientset(kubeConfigFile, dryRun)
kubeadmutil.CheckErr(err)
// TODO: remove this warning in 1.9
if !tokenCmd.Flags().Lookup("ttl").Changed {
// sending this output to stderr s
fmt.Fprintln(errW, "[kubeadm] WARNING: starting in 1.8, tokens expire after 24 hours by default (if you require a non-expiring token use --ttl 0)")
}
err = RunCreateToken(out, client, token, tokenDuration, usages, extraGroups, description)
kubeadmutil.CheckErr(err)
},
}
createCmd.Flags().DurationVar(&tokenDuration,
"ttl", kubeadmconstants.DefaultTokenDuration, "The duration before the token is automatically deleted (e.g. 1s, 2m, 3h). 0 means 'never expires'.")
createCmd.Flags().StringSliceVar(&usages,
"usages", kubeadmconstants.DefaultTokenUsages, "The ways in which this token can be used. Valid options: [signing,authentication].")
createCmd.Flags().StringSliceVar(&extraGroups,
"groups", []string{},
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.")
tokenCmd.AddCommand(createCmd)
tokenCmd.AddCommand(NewCmdTokenGenerate(out))
listCmd := &cobra.Command{
Use: "list",
Short: "List bootstrap tokens on the server.",
Long: dedent.Dedent(`
This command will list all Bootstrap Tokens for you.
`),
Run: func(tokenCmd *cobra.Command, args []string) {
client, err := getClientset(kubeConfigFile, dryRun)
kubeadmutil.CheckErr(err)
err = RunListTokens(out, errW, client)
kubeadmutil.CheckErr(err)
},
}
tokenCmd.AddCommand(listCmd)
deleteCmd := &cobra.Command{
Use: "delete [token-value]",
Short: "Delete bootstrap tokens on the server.",
Long: dedent.Dedent(`
This command will delete a given Bootstrap Token for you.
The [token-value] is the full Token of the form "[a-z0-9]{6}.[a-z0-9]{16}" or the
Token ID of the form "[a-z0-9]{6}" to delete.
`),
Run: func(tokenCmd *cobra.Command, args []string) {
if len(args) < 1 {
kubeadmutil.CheckErr(fmt.Errorf("missing subcommand; 'token delete' is missing token of form [%q]", tokenutil.TokenIDRegexpString))
}
client, err := getClientset(kubeConfigFile, dryRun)
kubeadmutil.CheckErr(err)
err = RunDeleteToken(out, client, args[0])
kubeadmutil.CheckErr(err)
},
}
tokenCmd.AddCommand(deleteCmd)
return tokenCmd
}
func NewCmdTokenGenerate(out io.Writer) *cobra.Command {
return &cobra.Command{
Use: "generate",
Short: "Generate and print a bootstrap token, but do not create it on the server.",
Long: dedent.Dedent(`
This command will print out a randomly-generated bootstrap token that can be used with
the "init" and "join" commands.
You don't have to use this command in order to generate a token, you can do so
yourself as long as it's in the format "[a-z0-9]{6}.[a-z0-9]{16}". This
command is provided for convenience to generate tokens in that format.
You can also use "kubeadm init" without specifying a token, and it will
generate and print one for you.
`),
Run: func(cmd *cobra.Command, args []string) {
err := RunGenerateToken(out)
kubeadmutil.CheckErr(err)
},
}
}
// RunCreateToken generates a new bootstrap token and stores it as a secret on the server.
func RunCreateToken(out io.Writer, client clientset.Interface, token string, tokenDuration time.Duration, usages []string, extraGroups []string, description string) error {
if len(token) == 0 {
var err error
token, err = tokenutil.GenerateToken()
if err != nil {
return err
}
} else {
_, _, err := tokenutil.ParseToken(token)
if err != nil {
return err
}
}
// adding groups only makes sense for authentication
usagesSet := sets.NewString(usages...)
if len(extraGroups) > 0 && !usagesSet.Has("authentication") {
return fmt.Errorf("--groups cannot be specified unless --usages includes \"authentication\"")
}
// validate any extra group names
for _, group := range extraGroups {
if err := bootstrapapi.ValidateBootstrapGroupName(group); err != nil {
return err
}
}
// TODO: Validate usages here so we don't allow something unsupported
err := tokenphase.CreateNewToken(client, token, tokenDuration, usages, extraGroups, description)
if err != nil {
return err
}
fmt.Fprintln(out, token)
return nil
}
// RunGenerateToken just generates a random token for the user
func RunGenerateToken(out io.Writer) error {
token, err := tokenutil.GenerateToken()
if err != nil {
return err
}
fmt.Fprintln(out, token)
return nil
}
// RunListTokens lists details on all existing bootstrap tokens on the server.
func RunListTokens(out io.Writer, errW io.Writer, client clientset.Interface) error {
// First, build our selector for bootstrap tokens only
tokenSelector := fields.SelectorFromSet(
map[string]string{
api.SecretTypeField: string(bootstrapapi.SecretTypeBootstrapToken),
},
)
listOptions := metav1.ListOptions{
FieldSelector: tokenSelector.String(),
}
secrets, err := client.CoreV1().Secrets(metav1.NamespaceSystem).List(listOptions)
if err != nil {
return fmt.Errorf("failed to list bootstrap tokens [%v]", err)
}
w := tabwriter.NewWriter(out, 10, 4, 3, ' ', 0)
fmt.Fprintln(w, "TOKEN\tTTL\tEXPIRES\tUSAGES\tDESCRIPTION\tEXTRA GROUPS")
for _, secret := range secrets.Items {
tokenId := getSecretString(&secret, bootstrapapi.BootstrapTokenIDKey)
if len(tokenId) == 0 {
fmt.Fprintf(errW, "bootstrap token has no token-id data: %s\n", secret.Name)
continue
}
// enforce the right naming convention
if secret.Name != fmt.Sprintf("%s%s", bootstrapapi.BootstrapTokenSecretPrefix, tokenId) {
fmt.Fprintf(errW, "bootstrap token name is not of the form '%s(token-id)': %s\n", bootstrapapi.BootstrapTokenSecretPrefix, secret.Name)
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 = printers.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.Contains(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()
return nil
}
// RunDeleteToken removes a bootstrap token from the server.
func RunDeleteToken(out io.Writer, client clientset.Interface, tokenIdOrToken string) error {
// Assume the given first argument is a token id and try to parse it
tokenId := tokenIdOrToken
if err := tokenutil.ParseTokenID(tokenIdOrToken); err != nil {
if tokenId, _, err = tokenutil.ParseToken(tokenIdOrToken); err != nil {
return fmt.Errorf("given token or token id %q didn't match pattern [%q] or [%q]", tokenIdOrToken, tokenutil.TokenIDRegexpString, tokenutil.TokenRegexpString)
}
}
tokenSecretName := fmt.Sprintf("%s%s", bootstrapapi.BootstrapTokenSecretPrefix, tokenId)
if err := client.CoreV1().Secrets(metav1.NamespaceSystem).Delete(tokenSecretName, nil); err != nil {
return fmt.Errorf("failed to delete bootstrap token [%v]", err)
}
fmt.Fprintf(out, "bootstrap token with id %q deleted\n", tokenId)
return nil
}
func getSecretString(secret *v1.Secret, key string) string {
if secret.Data == nil {
return ""
}
if val, ok := secret.Data[key]; ok {
return string(val)
}
return ""
}
func getClientset(file string, dryRun bool) (clientset.Interface, error) {
if dryRun {
dryRunGetter, err := apiclient.NewClientBackedDryRunGetterFromKubeconfig(file)
if err != nil {
return nil, err
}
return apiclient.NewDryRunClient(dryRunGetter, os.Stdout), nil
}
client, err := kubeconfigutil.ClientSetFromFile(file)
return client, err
}

View file

@ -0,0 +1,46 @@
/*
Copyright 2016 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 cmd
import (
"bytes"
"regexp"
"testing"
)
const (
TokenExpectedRegex = "^\\S{6}\\.\\S{16}\n$"
)
func TestRunGenerateToken(t *testing.T) {
var buf bytes.Buffer
err := RunGenerateToken(&buf)
if err != nil {
t.Errorf("RunGenerateToken returned an error: %v", err)
}
output := buf.String()
matched, err := regexp.MatchString(TokenExpectedRegex, output)
if err != nil {
t.Fatalf("encountered an error while trying to match RunGenerateToken's output: %v", err)
}
if !matched {
t.Errorf("RunGenerateToken's output did not match expected regex; wanted: [%s], got: [%s]", TokenExpectedRegex, output)
}
}

View file

@ -0,0 +1,60 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"apply.go",
"common.go",
"plan.go",
"upgrade.go",
],
visibility = ["//visibility:public"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
"//cmd/kubeadm/app/cmd/util:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/phases/controlplane:go_default_library",
"//cmd/kubeadm/app/phases/upgrade:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
"//cmd/kubeadm/app/util/apiclient:go_default_library",
"//cmd/kubeadm/app/util/dryrun:go_default_library",
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
"//pkg/api:go_default_library",
"//pkg/util/version:go_default_library",
"//vendor/github.com/ghodss/yaml:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/k8s.io/client-go/discovery/fake:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"apply_test.go",
"common_test.go",
"plan_test.go",
],
library = ":go_default_library",
deps = [
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
"//cmd/kubeadm/app/phases/upgrade:go_default_library",
"//pkg/util/version: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"],
)

View file

@ -0,0 +1,269 @@
/*
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 upgrade
import (
"fmt"
"os"
"strings"
"time"
"github.com/spf13/cobra"
clientset "k8s.io/client-go/kubernetes"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/version"
)
const (
upgradeManifestTimeout = 1 * time.Minute
)
// applyFlags holds the information about the flags that can be passed to apply
type applyFlags struct {
nonInteractiveMode bool
force bool
dryRun bool
newK8sVersionStr string
newK8sVersion *version.Version
imagePullTimeout time.Duration
parent *cmdUpgradeFlags
}
// SessionIsInteractive returns true if the session is of an interactive type (the default, can be opted out of with -y, -f or --dry-run)
func (f *applyFlags) SessionIsInteractive() bool {
return !f.nonInteractiveMode
}
// NewCmdApply returns the cobra command for `kubeadm upgrade apply`
func NewCmdApply(parentFlags *cmdUpgradeFlags) *cobra.Command {
flags := &applyFlags{
parent: parentFlags,
imagePullTimeout: 15 * time.Minute,
}
cmd := &cobra.Command{
Use: "apply [version]",
Short: "Upgrade your Kubernetes cluster to the specified version",
Run: func(cmd *cobra.Command, args []string) {
// Ensure the user is root
err := runPreflightChecks(flags.parent.skipPreFlight)
kubeadmutil.CheckErr(err)
err = cmdutil.ValidateExactArgNumber(args, []string{"version"})
kubeadmutil.CheckErr(err)
// It's safe to use args[0] here as the slice has been validated above
flags.newK8sVersionStr = args[0]
// Default the flags dynamically, based on each others' value
err = SetImplicitFlags(flags)
kubeadmutil.CheckErr(err)
err = RunApply(flags)
kubeadmutil.CheckErr(err)
},
}
// Specify the valid flags specific for apply
cmd.Flags().BoolVarP(&flags.nonInteractiveMode, "yes", "y", flags.nonInteractiveMode, "Perform the upgrade and do not prompt for confirmation (non-interactive mode).")
cmd.Flags().BoolVarP(&flags.force, "force", "f", flags.force, "Force upgrading although some requirements might not be met. This also implies non-interactive mode.")
cmd.Flags().BoolVar(&flags.dryRun, "dry-run", flags.dryRun, "Do not change any state, just output what actions would be applied.")
cmd.Flags().DurationVar(&flags.imagePullTimeout, "image-pull-timeout", flags.imagePullTimeout, "The maximum amount of time to wait for the control plane pods to be downloaded.")
return cmd
}
// RunApply takes care of the actual upgrade functionality
// It does the following things:
// - Checks if the cluster is healthy
// - Gets the configuration from the kubeadm-config ConfigMap in the cluster
// - Enforces all version skew policies
// - Asks the user if they really want to upgrade
// - Makes sure the control plane images are available locally on the master(s)
// - Upgrades the control plane components
// - Applies the other resources that'd be created with kubeadm init as well, like
// - Creating the RBAC rules for the Bootstrap Tokens and the cluster-info ConfigMap
// - Applying new kube-dns and kube-proxy manifests
// - Uploads the newly used configuration to the cluster ConfigMap
func RunApply(flags *applyFlags) error {
// Start with the basics, verify that the cluster is healthy and get the configuration from the cluster (using the ConfigMap)
upgradeVars, err := enforceRequirements(flags.parent.kubeConfigPath, flags.parent.cfgPath, flags.parent.printConfig, flags.dryRun)
if err != nil {
return err
}
// Set the upgraded version on the external config object now
upgradeVars.cfg.KubernetesVersion = flags.newK8sVersionStr
// Grab the external, versioned configuration and convert it to the internal type for usage here later
internalcfg := &kubeadmapi.MasterConfiguration{}
api.Scheme.Convert(upgradeVars.cfg, internalcfg, nil)
// Enforce the version skew policies
if err := EnforceVersionPolicies(flags, upgradeVars.versionGetter); err != nil {
return fmt.Errorf("[upgrade/version] FATAL: %v", err)
}
// If the current session is interactive, ask the user whether they really want to upgrade
if flags.SessionIsInteractive() {
if err := InteractivelyConfirmUpgrade("Are you sure you want to proceed with the upgrade?"); err != nil {
return err
}
}
// Use a prepuller implementation based on creating DaemonSets
// and block until all DaemonSets are ready; then we know for sure that all control plane images are cached locally
prepuller := upgrade.NewDaemonSetPrepuller(upgradeVars.client, upgradeVars.waiter, internalcfg)
upgrade.PrepullImagesInParallel(prepuller, flags.imagePullTimeout)
// Now; perform the upgrade procedure
if err := PerformControlPlaneUpgrade(flags, upgradeVars.client, upgradeVars.waiter, internalcfg); err != nil {
return fmt.Errorf("[upgrade/apply] FATAL: %v", err)
}
// Upgrade RBAC rules and addons. Optionally, if needed, perform some extra task for a specific version
if err := upgrade.PerformPostUpgradeTasks(upgradeVars.client, internalcfg, flags.newK8sVersion); err != nil {
return fmt.Errorf("[upgrade/postupgrade] FATAL post-upgrade error: %v", err)
}
if flags.dryRun {
fmt.Println("[dryrun] Finished dryrunning successfully!")
return nil
}
fmt.Println("")
fmt.Printf("[upgrade/successful] SUCCESS! Your cluster was upgraded to %q. Enjoy!\n", flags.newK8sVersionStr)
fmt.Println("")
fmt.Println("[upgrade/kubelet] Now that your control plane is upgraded, please proceed with upgrading your kubelets in turn.")
return nil
}
// SetImplicitFlags handles dynamically defaulting flags based on each other's value
func SetImplicitFlags(flags *applyFlags) error {
// If we are in dry-run or force mode; we should automatically execute this command non-interactively
if flags.dryRun || flags.force {
flags.nonInteractiveMode = true
}
k8sVer, err := version.ParseSemantic(flags.newK8sVersionStr)
if err != nil {
return fmt.Errorf("couldn't parse version %q as a semantic version", flags.newK8sVersionStr)
}
flags.newK8sVersion = k8sVer
// Automatically add the "v" prefix to the string representation in case it doesn't exist
if !strings.HasPrefix(flags.newK8sVersionStr, "v") {
flags.newK8sVersionStr = fmt.Sprintf("v%s", flags.newK8sVersionStr)
}
return nil
}
// EnforceVersionPolicies makes sure that the version the user specified is valid to upgrade to
// There are both fatal and skippable (with --force) errors
func EnforceVersionPolicies(flags *applyFlags, versionGetter upgrade.VersionGetter) error {
fmt.Printf("[upgrade/version] You have chosen to upgrade to version %q\n", flags.newK8sVersionStr)
versionSkewErrs := upgrade.EnforceVersionPolicies(versionGetter, flags.newK8sVersionStr, flags.newK8sVersion, flags.parent.allowExperimentalUpgrades, flags.parent.allowRCUpgrades)
if versionSkewErrs != nil {
if len(versionSkewErrs.Mandatory) > 0 {
return fmt.Errorf("The --version argument is invalid due to these fatal errors: %v", versionSkewErrs.Mandatory)
}
if len(versionSkewErrs.Skippable) > 0 {
// Return the error if the user hasn't specified the --force flag
if !flags.force {
return fmt.Errorf("The --version argument is invalid due to these errors: %v. Can be bypassed if you pass the --force flag", versionSkewErrs.Skippable)
}
// Soft errors found, but --force was specified
fmt.Printf("[upgrade/version] Found %d potential version compatibility errors but skipping since the --force flag is set: %v\n", len(versionSkewErrs.Skippable), versionSkewErrs.Skippable)
}
}
return nil
}
// PerformControlPlaneUpgrade actually performs the upgrade procedure for the cluster of your type (self-hosted or static-pod-hosted)
func PerformControlPlaneUpgrade(flags *applyFlags, client clientset.Interface, waiter apiclient.Waiter, internalcfg *kubeadmapi.MasterConfiguration) error {
// Check if the cluster is self-hosted and act accordingly
if upgrade.IsControlPlaneSelfHosted(client) {
fmt.Printf("[upgrade/apply] Upgrading your Self-Hosted control plane to version %q...\n", flags.newK8sVersionStr)
// Upgrade the self-hosted cluster
return upgrade.SelfHostedControlPlane(client, waiter, internalcfg, flags.newK8sVersion)
}
// OK, the cluster is hosted using static pods. Upgrade a static-pod hosted cluster
fmt.Printf("[upgrade/apply] Upgrading your Static Pod-hosted control plane to version %q...\n", flags.newK8sVersionStr)
if flags.dryRun {
return DryRunStaticPodUpgrade(internalcfg)
}
return PerformStaticPodUpgrade(client, waiter, internalcfg)
}
// PerformStaticPodUpgrade performs the upgrade of the control plane components for a static pod hosted cluster
func PerformStaticPodUpgrade(client clientset.Interface, waiter apiclient.Waiter, internalcfg *kubeadmapi.MasterConfiguration) error {
pathManager, err := upgrade.NewKubeStaticPodPathManagerUsingTempDirs(constants.GetStaticPodDirectory())
if err != nil {
return err
}
if err := upgrade.StaticPodControlPlane(waiter, pathManager, internalcfg); err != nil {
return err
}
return nil
}
// DryRunStaticPodUpgrade fakes an upgrade of the control plane
func DryRunStaticPodUpgrade(internalcfg *kubeadmapi.MasterConfiguration) error {
dryRunManifestDir, err := constants.CreateTempDirForKubeadm("kubeadm-upgrade-dryrun")
if err != nil {
return err
}
defer os.RemoveAll(dryRunManifestDir)
if err := controlplane.CreateInitStaticPodManifestFiles(dryRunManifestDir, internalcfg); err != nil {
return err
}
// Print the contents of the upgraded manifests and pretend like they were in /etc/kubernetes/manifests
files := []dryrunutil.FileToPrint{}
for _, component := range constants.MasterComponents {
realPath := constants.GetStaticPodFilepath(component, dryRunManifestDir)
outputPath := constants.GetStaticPodFilepath(component, constants.GetStaticPodDirectory())
files = append(files, dryrunutil.NewFileToPrint(realPath, outputPath))
}
if err := dryrunutil.PrintDryRunFiles(files, os.Stdout); err != nil {
return err
}
return nil
}

View file

@ -0,0 +1,194 @@
/*
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 upgrade
import (
"reflect"
"testing"
"k8s.io/kubernetes/pkg/util/version"
)
func TestSetImplicitFlags(t *testing.T) {
var tests = []struct {
flags *applyFlags
expectedFlags applyFlags
errExpected bool
}{
{ // if not dryRun or force is set; the nonInteractiveMode field should not be touched
flags: &applyFlags{
newK8sVersionStr: "v1.8.0",
dryRun: false,
force: false,
nonInteractiveMode: false,
},
expectedFlags: applyFlags{
newK8sVersionStr: "v1.8.0",
newK8sVersion: version.MustParseSemantic("v1.8.0"),
dryRun: false,
force: false,
nonInteractiveMode: false,
},
},
{ // if not dryRun or force is set; the nonInteractiveMode field should not be touched
flags: &applyFlags{
newK8sVersionStr: "v1.8.0",
dryRun: false,
force: false,
nonInteractiveMode: true,
},
expectedFlags: applyFlags{
newK8sVersionStr: "v1.8.0",
newK8sVersion: version.MustParseSemantic("v1.8.0"),
dryRun: false,
force: false,
nonInteractiveMode: true,
},
},
{ // if dryRun or force is set; the nonInteractiveMode field should be set to true
flags: &applyFlags{
newK8sVersionStr: "v1.8.0",
dryRun: true,
force: false,
nonInteractiveMode: false,
},
expectedFlags: applyFlags{
newK8sVersionStr: "v1.8.0",
newK8sVersion: version.MustParseSemantic("v1.8.0"),
dryRun: true,
force: false,
nonInteractiveMode: true,
},
},
{ // if dryRun or force is set; the nonInteractiveMode field should be set to true
flags: &applyFlags{
newK8sVersionStr: "v1.8.0",
dryRun: false,
force: true,
nonInteractiveMode: false,
},
expectedFlags: applyFlags{
newK8sVersionStr: "v1.8.0",
newK8sVersion: version.MustParseSemantic("v1.8.0"),
dryRun: false,
force: true,
nonInteractiveMode: true,
},
},
{ // if dryRun or force is set; the nonInteractiveMode field should be set to true
flags: &applyFlags{
newK8sVersionStr: "v1.8.0",
dryRun: true,
force: true,
nonInteractiveMode: false,
},
expectedFlags: applyFlags{
newK8sVersionStr: "v1.8.0",
newK8sVersion: version.MustParseSemantic("v1.8.0"),
dryRun: true,
force: true,
nonInteractiveMode: true,
},
},
{ // if dryRun or force is set; the nonInteractiveMode field should be set to true
flags: &applyFlags{
newK8sVersionStr: "v1.8.0",
dryRun: true,
force: true,
nonInteractiveMode: true,
},
expectedFlags: applyFlags{
newK8sVersionStr: "v1.8.0",
newK8sVersion: version.MustParseSemantic("v1.8.0"),
dryRun: true,
force: true,
nonInteractiveMode: true,
},
},
{ // if the new version is empty; it should error out
flags: &applyFlags{
newK8sVersionStr: "",
},
expectedFlags: applyFlags{
newK8sVersionStr: "",
},
errExpected: true,
},
{ // if the new version is invalid; it should error out
flags: &applyFlags{
newK8sVersionStr: "foo",
},
expectedFlags: applyFlags{
newK8sVersionStr: "foo",
},
errExpected: true,
},
{ // if the new version is valid but without the "v" prefix; it parse and prepend v
flags: &applyFlags{
newK8sVersionStr: "1.8.0",
},
expectedFlags: applyFlags{
newK8sVersionStr: "v1.8.0",
newK8sVersion: version.MustParseSemantic("v1.8.0"),
},
errExpected: false,
},
{ // valid version should succeed
flags: &applyFlags{
newK8sVersionStr: "v1.8.1",
},
expectedFlags: applyFlags{
newK8sVersionStr: "v1.8.1",
newK8sVersion: version.MustParseSemantic("v1.8.1"),
},
errExpected: false,
},
{ // valid version should succeed
flags: &applyFlags{
newK8sVersionStr: "1.8.0-alpha.3",
},
expectedFlags: applyFlags{
newK8sVersionStr: "v1.8.0-alpha.3",
newK8sVersion: version.MustParseSemantic("v1.8.0-alpha.3"),
},
errExpected: false,
},
}
for _, rt := range tests {
actualErr := SetImplicitFlags(rt.flags)
// If an error was returned; make newK8sVersion nil so it's easy to match using reflect.DeepEqual later (instead of a random pointer)
if actualErr != nil {
rt.flags.newK8sVersion = nil
}
if !reflect.DeepEqual(*rt.flags, rt.expectedFlags) {
t.Errorf(
"failed SetImplicitFlags:\n\texpected flags: %v\n\t actual: %v",
rt.expectedFlags,
*rt.flags,
)
}
if (actualErr != nil) != rt.errExpected {
t.Errorf(
"failed SetImplicitFlags:\n\texpected error: %t\n\t actual: %t",
rt.errExpected,
(actualErr != nil),
)
}
}
}

View file

@ -0,0 +1,166 @@
/*
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 upgrade
import (
"bufio"
"bytes"
"fmt"
"io"
"os"
"strings"
"github.com/ghodss/yaml"
fakediscovery "k8s.io/client-go/discovery/fake"
clientset "k8s.io/client-go/kubernetes"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
)
// upgradeVariables holds variables needed for performing an upgrade or planning to do so
// TODO - Restructure or rename upgradeVariables
type upgradeVariables struct {
client clientset.Interface
cfg *kubeadmapiext.MasterConfiguration
versionGetter upgrade.VersionGetter
waiter apiclient.Waiter
}
// enforceRequirements verifies that it's okay to upgrade and then returns the variables needed for the rest of the procedure
func enforceRequirements(kubeConfigPath, cfgPath string, printConfig, dryRun bool) (*upgradeVariables, error) {
client, err := getClient(kubeConfigPath, dryRun)
if err != nil {
return nil, fmt.Errorf("couldn't create a Kubernetes client from file %q: %v", kubeConfigPath, err)
}
// Run healthchecks against the cluster
if err := upgrade.CheckClusterHealth(client); err != nil {
return nil, fmt.Errorf("[upgrade/health] FATAL: %v", err)
}
// Fetch the configuration from a file or ConfigMap and validate it
cfg, err := upgrade.FetchConfiguration(client, os.Stdout, cfgPath)
if err != nil {
return nil, fmt.Errorf("[upgrade/config] FATAL: %v", err)
}
// If the user told us to print this information out; do it!
if printConfig {
printConfiguration(cfg, os.Stdout)
}
return &upgradeVariables{
client: client,
cfg: cfg,
// Use a real version getter interface that queries the API server, the kubeadm client and the Kubernetes CI system for latest versions
versionGetter: upgrade.NewKubeVersionGetter(client, os.Stdout),
// Use the waiter conditionally based on the dryrunning variable
waiter: getWaiter(dryRun, client),
}, nil
}
// printConfiguration prints the external version of the API to yaml
func printConfiguration(cfg *kubeadmapiext.MasterConfiguration, w io.Writer) {
// Short-circuit if cfg is nil, so we can safely get the value of the pointer below
if cfg == nil {
return
}
cfgYaml, err := yaml.Marshal(*cfg)
if err == nil {
fmt.Fprintln(w, "[upgrade/config] Configuration used:")
scanner := bufio.NewScanner(bytes.NewReader(cfgYaml))
for scanner.Scan() {
fmt.Fprintf(w, "\t%s\n", scanner.Text())
}
}
}
// runPreflightChecks runs the root preflight check
func runPreflightChecks(skipPreFlight bool) error {
if skipPreFlight {
fmt.Println("[preflight] Skipping pre-flight checks")
return nil
}
fmt.Println("[preflight] Running pre-flight checks")
return preflight.RunRootCheckOnly()
}
// getClient gets a real or fake client depending on whether the user is dry-running or not
func getClient(file string, dryRun bool) (clientset.Interface, error) {
if dryRun {
dryRunGetter, err := apiclient.NewClientBackedDryRunGetterFromKubeconfig(file)
if err != nil {
return nil, err
}
// In order for fakeclient.Discovery().ServerVersion() to return the backing API Server's
// real version; we have to do some clever API machinery tricks. First, we get the real
// API Server's version
realServerVersion, err := dryRunGetter.Client().Discovery().ServerVersion()
if err != nil {
return nil, fmt.Errorf("failed to get server version: %v", err)
}
// Get the fake clientset
fakeclient := apiclient.NewDryRunClient(dryRunGetter, os.Stdout)
// As we know the return of Discovery() of the fake clientset is of type *fakediscovery.FakeDiscovery
// we can convert it to that struct.
fakeclientDiscovery, ok := fakeclient.Discovery().(*fakediscovery.FakeDiscovery)
if !ok {
return nil, fmt.Errorf("couldn't set fake discovery's server version")
}
// Lastly, set the right server version to be used
fakeclientDiscovery.FakedServerVersion = realServerVersion
// return the fake clientset used for dry-running
return fakeclient, nil
}
return kubeconfigutil.ClientSetFromFile(file)
}
// getWaiter gets the right waiter implementation
func getWaiter(dryRun bool, client clientset.Interface) apiclient.Waiter {
if dryRun {
return dryrunutil.NewWaiter()
}
return apiclient.NewKubeWaiter(client, upgradeManifestTimeout, os.Stdout)
}
// InteractivelyConfirmUpgrade asks the user whether they _really_ want to upgrade.
func InteractivelyConfirmUpgrade(question string) error {
fmt.Printf("[upgrade/confirm] %s [y/N]: ", question)
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
if err := scanner.Err(); err != nil {
return fmt.Errorf("couldn't read from standard input: %v", err)
}
answer := scanner.Text()
if strings.ToLower(answer) == "y" || strings.ToLower(answer) == "yes" {
return nil
}
return fmt.Errorf("won't proceed; the user didn't answer (Y|y) in order to continue")
}

View file

@ -0,0 +1,110 @@
/*
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 upgrade
import (
"bytes"
"testing"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
)
func TestPrintConfiguration(t *testing.T) {
var tests = []struct {
cfg *kubeadmapiext.MasterConfiguration
buf *bytes.Buffer
expectedBytes []byte
}{
{
cfg: nil,
expectedBytes: []byte(""),
},
{
cfg: &kubeadmapiext.MasterConfiguration{
KubernetesVersion: "v1.7.1",
},
expectedBytes: []byte(`[upgrade/config] Configuration used:
api:
advertiseAddress: ""
bindPort: 0
certificatesDir: ""
cloudProvider: ""
etcd:
caFile: ""
certFile: ""
dataDir: ""
endpoints: null
image: ""
keyFile: ""
imageRepository: ""
kubernetesVersion: v1.7.1
networking:
dnsDomain: ""
podSubnet: ""
serviceSubnet: ""
nodeName: ""
token: ""
tokenTTL: 0s
unifiedControlPlaneImage: ""
`),
},
{
cfg: &kubeadmapiext.MasterConfiguration{
KubernetesVersion: "v1.7.1",
Networking: kubeadmapiext.Networking{
ServiceSubnet: "10.96.0.1/12",
},
},
expectedBytes: []byte(`[upgrade/config] Configuration used:
api:
advertiseAddress: ""
bindPort: 0
certificatesDir: ""
cloudProvider: ""
etcd:
caFile: ""
certFile: ""
dataDir: ""
endpoints: null
image: ""
keyFile: ""
imageRepository: ""
kubernetesVersion: v1.7.1
networking:
dnsDomain: ""
podSubnet: ""
serviceSubnet: 10.96.0.1/12
nodeName: ""
token: ""
tokenTTL: 0s
unifiedControlPlaneImage: ""
`),
},
}
for _, rt := range tests {
rt.buf = bytes.NewBufferString("")
printConfiguration(rt.cfg, rt.buf)
actualBytes := rt.buf.Bytes()
if !bytes.Equal(actualBytes, rt.expectedBytes) {
t.Errorf(
"failed PrintConfiguration:\n\texpected: %q\n\t actual: %q",
string(rt.expectedBytes),
string(actualBytes),
)
}
}
}

View file

@ -0,0 +1,142 @@
/*
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 upgrade
import (
"fmt"
"io"
"os"
"sort"
"text/tabwriter"
"github.com/spf13/cobra"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
)
// NewCmdPlan returns the cobra command for `kubeadm upgrade plan`
func NewCmdPlan(parentFlags *cmdUpgradeFlags) *cobra.Command {
cmd := &cobra.Command{
Use: "plan",
Short: "Check which versions are available to upgrade to and validate whether your current cluster is upgradeable",
Run: func(_ *cobra.Command, _ []string) {
// Ensure the user is root
err := runPreflightChecks(parentFlags.skipPreFlight)
kubeadmutil.CheckErr(err)
err = RunPlan(parentFlags)
kubeadmutil.CheckErr(err)
},
}
return cmd
}
// RunPlan takes care of outputting available versions to upgrade to for the user
func RunPlan(parentFlags *cmdUpgradeFlags) error {
// Start with the basics, verify that the cluster is healthy, build a client and a versionGetter. Never set dry-run for plan.
upgradeVars, err := enforceRequirements(parentFlags.kubeConfigPath, parentFlags.cfgPath, parentFlags.printConfig, false)
if err != nil {
return err
}
// Compute which upgrade possibilities there are
availUpgrades, err := upgrade.GetAvailableUpgrades(upgradeVars.versionGetter, parentFlags.allowExperimentalUpgrades, parentFlags.allowRCUpgrades)
if err != nil {
return fmt.Errorf("[upgrade/versions] FATAL: %v", err)
}
// Tell the user which upgrades are available
printAvailableUpgrades(availUpgrades, os.Stdout)
return nil
}
// printAvailableUpgrades prints a UX-friendly overview of what versions are available to upgrade to
// TODO look into columnize or some other formatter when time permits instead of using the tabwriter
func printAvailableUpgrades(upgrades []upgrade.Upgrade, w io.Writer) {
// Return quickly if no upgrades can be made
if len(upgrades) == 0 {
fmt.Fprintln(w, "Awesome, you're up-to-date! Enjoy!")
return
}
// The tab writer writes to the "real" writer w
tabw := tabwriter.NewWriter(w, 10, 4, 3, ' ', 0)
// Loop through the upgrade possibilities and output text to the command line
for _, upgrade := range upgrades {
if upgrade.CanUpgradeKubelets() {
fmt.Fprintln(w, "Components that must be upgraded manually after you've upgraded the control plane with 'kubeadm upgrade apply':")
fmt.Fprintln(tabw, "COMPONENT\tCURRENT\tAVAILABLE")
firstPrinted := false
// The map is of the form <old-version>:<node-count>. Here all the keys are put into a slice and sorted
// in order to always get the right order. Then the map value is extracted separately
for _, oldVersion := range sortedSliceFromStringIntMap(upgrade.Before.KubeletVersions) {
nodeCount := upgrade.Before.KubeletVersions[oldVersion]
if !firstPrinted {
// Output the Kubelet header only on the first version pair
fmt.Fprintf(tabw, "Kubelet\t%d x %s\t%s\n", nodeCount, oldVersion, upgrade.After.KubeVersion)
firstPrinted = true
continue
}
fmt.Fprintf(tabw, "\t\t%d x %s\t%s\n", nodeCount, oldVersion, upgrade.After.KubeVersion)
}
// We should flush the writer here at this stage; as the columns will now be of the right size, adjusted to the above content
tabw.Flush()
fmt.Fprintln(w, "")
}
fmt.Fprintf(w, "Upgrade to the latest %s:\n", upgrade.Description)
fmt.Fprintln(w, "")
fmt.Fprintln(tabw, "COMPONENT\tCURRENT\tAVAILABLE")
fmt.Fprintf(tabw, "API Server\t%s\t%s\n", upgrade.Before.KubeVersion, upgrade.After.KubeVersion)
fmt.Fprintf(tabw, "Controller Manager\t%s\t%s\n", upgrade.Before.KubeVersion, upgrade.After.KubeVersion)
fmt.Fprintf(tabw, "Scheduler\t%s\t%s\n", upgrade.Before.KubeVersion, upgrade.After.KubeVersion)
fmt.Fprintf(tabw, "Kube Proxy\t%s\t%s\n", upgrade.Before.KubeVersion, upgrade.After.KubeVersion)
fmt.Fprintf(tabw, "Kube DNS\t%s\t%s\n", upgrade.Before.DNSVersion, upgrade.After.DNSVersion)
// The tabwriter should be flushed at this stage as we have now put in all the required content for this time. This is required for the tabs' size to be correct.
tabw.Flush()
fmt.Fprintln(w, "")
fmt.Fprintln(w, "You can now apply the upgrade by executing the following command:")
fmt.Fprintln(w, "")
fmt.Fprintf(w, "\tkubeadm upgrade apply %s\n", upgrade.After.KubeVersion)
fmt.Fprintln(w, "")
if upgrade.Before.KubeadmVersion != upgrade.After.KubeadmVersion {
fmt.Fprintf(w, "Note: Before you do can perform this upgrade, you have to update kubeadm to %s\n", upgrade.After.KubeadmVersion)
fmt.Fprintln(w, "")
}
fmt.Fprintln(w, "_____________________________________________________________________")
fmt.Fprintln(w, "")
}
}
// sortedSliceFromStringIntMap returns a slice of the keys in the map sorted alphabetically
func sortedSliceFromStringIntMap(strMap map[string]uint16) []string {
strSlice := []string{}
for k := range strMap {
strSlice = append(strSlice, k)
}
sort.Strings(strSlice)
return strSlice
}

View file

@ -0,0 +1,329 @@
/*
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 upgrade
import (
"bytes"
"reflect"
"testing"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
)
func TestSortedSliceFromStringIntMap(t *testing.T) {
var tests = []struct {
strMap map[string]uint16
expectedSlice []string
}{ // The returned slice should be alphabetically sorted based on the string keys in the map
{
strMap: map[string]uint16{"foo": 1, "bar": 2},
expectedSlice: []string{"bar", "foo"},
},
{ // The int value should not affect this func
strMap: map[string]uint16{"foo": 2, "bar": 1},
expectedSlice: []string{"bar", "foo"},
},
{
strMap: map[string]uint16{"b": 2, "a": 1, "cb": 0, "ca": 1000},
expectedSlice: []string{"a", "b", "ca", "cb"},
},
{ // This should work for version numbers as well; and the lowest version should come first
strMap: map[string]uint16{"v1.7.0": 1, "v1.6.1": 1, "v1.6.2": 1, "v1.8.0": 1, "v1.8.0-alpha.1": 1},
expectedSlice: []string{"v1.6.1", "v1.6.2", "v1.7.0", "v1.8.0", "v1.8.0-alpha.1"},
},
}
for _, rt := range tests {
actualSlice := sortedSliceFromStringIntMap(rt.strMap)
if !reflect.DeepEqual(actualSlice, rt.expectedSlice) {
t.Errorf(
"failed SortedSliceFromStringIntMap:\n\texpected: %v\n\t actual: %v",
rt.expectedSlice,
actualSlice,
)
}
}
}
// TODO Think about modifying this test to be less verbose checking b/c it can be brittle.
func TestPrintAvailableUpgrades(t *testing.T) {
var tests = []struct {
upgrades []upgrade.Upgrade
buf *bytes.Buffer
expectedBytes []byte
}{
{
upgrades: []upgrade.Upgrade{},
expectedBytes: []byte(`Awesome, you're up-to-date! Enjoy!
`),
},
{
upgrades: []upgrade.Upgrade{
{
Description: "version in the v1.7 series",
Before: upgrade.ClusterState{
KubeVersion: "v1.7.1",
KubeletVersions: map[string]uint16{
"v1.7.1": 1,
},
KubeadmVersion: "v1.7.2",
DNSVersion: "1.14.5",
},
After: upgrade.ClusterState{
KubeVersion: "v1.7.3",
KubeadmVersion: "v1.7.3",
DNSVersion: "1.14.5",
},
},
},
expectedBytes: []byte(`Components that must be upgraded manually after you've upgraded the control plane with 'kubeadm upgrade apply':
COMPONENT CURRENT AVAILABLE
Kubelet 1 x v1.7.1 v1.7.3
Upgrade to the latest version in the v1.7 series:
COMPONENT CURRENT AVAILABLE
API Server v1.7.1 v1.7.3
Controller Manager v1.7.1 v1.7.3
Scheduler v1.7.1 v1.7.3
Kube Proxy v1.7.1 v1.7.3
Kube DNS 1.14.5 1.14.5
You can now apply the upgrade by executing the following command:
kubeadm upgrade apply v1.7.3
Note: Before you do can perform this upgrade, you have to update kubeadm to v1.7.3
_____________________________________________________________________
`),
},
{
upgrades: []upgrade.Upgrade{
{
Description: "stable version",
Before: upgrade.ClusterState{
KubeVersion: "v1.7.3",
KubeletVersions: map[string]uint16{
"v1.7.3": 1,
},
KubeadmVersion: "v1.8.0",
DNSVersion: "1.14.5",
},
After: upgrade.ClusterState{
KubeVersion: "v1.8.0",
KubeadmVersion: "v1.8.0",
DNSVersion: "1.14.5",
},
},
},
expectedBytes: []byte(`Components that must be upgraded manually after you've upgraded the control plane with 'kubeadm upgrade apply':
COMPONENT CURRENT AVAILABLE
Kubelet 1 x v1.7.3 v1.8.0
Upgrade to the latest stable version:
COMPONENT CURRENT AVAILABLE
API Server v1.7.3 v1.8.0
Controller Manager v1.7.3 v1.8.0
Scheduler v1.7.3 v1.8.0
Kube Proxy v1.7.3 v1.8.0
Kube DNS 1.14.5 1.14.5
You can now apply the upgrade by executing the following command:
kubeadm upgrade apply v1.8.0
_____________________________________________________________________
`),
},
{
upgrades: []upgrade.Upgrade{
{
Description: "version in the v1.7 series",
Before: upgrade.ClusterState{
KubeVersion: "v1.7.3",
KubeletVersions: map[string]uint16{
"v1.7.3": 1,
},
KubeadmVersion: "v1.8.1",
DNSVersion: "1.14.5",
},
After: upgrade.ClusterState{
KubeVersion: "v1.7.5",
KubeadmVersion: "v1.8.1",
DNSVersion: "1.14.5",
},
},
{
Description: "stable version",
Before: upgrade.ClusterState{
KubeVersion: "v1.7.3",
KubeletVersions: map[string]uint16{
"v1.7.3": 1,
},
KubeadmVersion: "v1.8.1",
DNSVersion: "1.14.5",
},
After: upgrade.ClusterState{
KubeVersion: "v1.8.2",
KubeadmVersion: "v1.8.2",
DNSVersion: "1.14.5",
},
},
},
expectedBytes: []byte(`Components that must be upgraded manually after you've upgraded the control plane with 'kubeadm upgrade apply':
COMPONENT CURRENT AVAILABLE
Kubelet 1 x v1.7.3 v1.7.5
Upgrade to the latest version in the v1.7 series:
COMPONENT CURRENT AVAILABLE
API Server v1.7.3 v1.7.5
Controller Manager v1.7.3 v1.7.5
Scheduler v1.7.3 v1.7.5
Kube Proxy v1.7.3 v1.7.5
Kube DNS 1.14.5 1.14.5
You can now apply the upgrade by executing the following command:
kubeadm upgrade apply v1.7.5
_____________________________________________________________________
Components that must be upgraded manually after you've upgraded the control plane with 'kubeadm upgrade apply':
COMPONENT CURRENT AVAILABLE
Kubelet 1 x v1.7.3 v1.8.2
Upgrade to the latest stable version:
COMPONENT CURRENT AVAILABLE
API Server v1.7.3 v1.8.2
Controller Manager v1.7.3 v1.8.2
Scheduler v1.7.3 v1.8.2
Kube Proxy v1.7.3 v1.8.2
Kube DNS 1.14.5 1.14.5
You can now apply the upgrade by executing the following command:
kubeadm upgrade apply v1.8.2
Note: Before you do can perform this upgrade, you have to update kubeadm to v1.8.2
_____________________________________________________________________
`),
},
{
upgrades: []upgrade.Upgrade{
{
Description: "experimental version",
Before: upgrade.ClusterState{
KubeVersion: "v1.7.5",
KubeletVersions: map[string]uint16{
"v1.7.5": 1,
},
KubeadmVersion: "v1.7.5",
DNSVersion: "1.14.5",
},
After: upgrade.ClusterState{
KubeVersion: "v1.8.0-beta.1",
KubeadmVersion: "v1.8.0-beta.1",
DNSVersion: "1.14.5",
},
},
},
expectedBytes: []byte(`Components that must be upgraded manually after you've upgraded the control plane with 'kubeadm upgrade apply':
COMPONENT CURRENT AVAILABLE
Kubelet 1 x v1.7.5 v1.8.0-beta.1
Upgrade to the latest experimental version:
COMPONENT CURRENT AVAILABLE
API Server v1.7.5 v1.8.0-beta.1
Controller Manager v1.7.5 v1.8.0-beta.1
Scheduler v1.7.5 v1.8.0-beta.1
Kube Proxy v1.7.5 v1.8.0-beta.1
Kube DNS 1.14.5 1.14.5
You can now apply the upgrade by executing the following command:
kubeadm upgrade apply v1.8.0-beta.1
Note: Before you do can perform this upgrade, you have to update kubeadm to v1.8.0-beta.1
_____________________________________________________________________
`),
},
{
upgrades: []upgrade.Upgrade{
{
Description: "release candidate version",
Before: upgrade.ClusterState{
KubeVersion: "v1.7.5",
KubeletVersions: map[string]uint16{
"v1.7.5": 1,
},
KubeadmVersion: "v1.7.5",
DNSVersion: "1.14.5",
},
After: upgrade.ClusterState{
KubeVersion: "v1.8.0-rc.1",
KubeadmVersion: "v1.8.0-rc.1",
DNSVersion: "1.14.5",
},
},
},
expectedBytes: []byte(`Components that must be upgraded manually after you've upgraded the control plane with 'kubeadm upgrade apply':
COMPONENT CURRENT AVAILABLE
Kubelet 1 x v1.7.5 v1.8.0-rc.1
Upgrade to the latest release candidate version:
COMPONENT CURRENT AVAILABLE
API Server v1.7.5 v1.8.0-rc.1
Controller Manager v1.7.5 v1.8.0-rc.1
Scheduler v1.7.5 v1.8.0-rc.1
Kube Proxy v1.7.5 v1.8.0-rc.1
Kube DNS 1.14.5 1.14.5
You can now apply the upgrade by executing the following command:
kubeadm upgrade apply v1.8.0-rc.1
Note: Before you do can perform this upgrade, you have to update kubeadm to v1.8.0-rc.1
_____________________________________________________________________
`),
},
}
for _, rt := range tests {
rt.buf = bytes.NewBufferString("")
printAvailableUpgrades(rt.upgrades, rt.buf)
actualBytes := rt.buf.Bytes()
if !bytes.Equal(actualBytes, rt.expectedBytes) {
t.Errorf(
"failed PrintAvailableUpgrades:\n\texpected: %q\n\t actual: %q",
string(rt.expectedBytes),
string(actualBytes),
)
}
}
}

View file

@ -0,0 +1,64 @@
/*
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 upgrade
import (
"io"
"github.com/spf13/cobra"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
)
// cmdUpgradeFlags holds the values for the common flags in `kubeadm upgrade`
type cmdUpgradeFlags struct {
kubeConfigPath string
cfgPath string
allowExperimentalUpgrades bool
allowRCUpgrades bool
printConfig bool
skipPreFlight bool
}
// NewCmdUpgrade returns the cobra command for `kubeadm upgrade`
func NewCmdUpgrade(out io.Writer) *cobra.Command {
flags := &cmdUpgradeFlags{
kubeConfigPath: "/etc/kubernetes/admin.conf",
cfgPath: "",
allowExperimentalUpgrades: false,
allowRCUpgrades: false,
printConfig: false,
skipPreFlight: false,
}
cmd := &cobra.Command{
Use: "upgrade",
Short: "Upgrade your cluster smoothly to a newer version with this command.",
RunE: cmdutil.SubCmdRunE("upgrade"),
}
cmd.PersistentFlags().StringVar(&flags.kubeConfigPath, "kubeconfig", flags.kubeConfigPath, "The KubeConfig file to use for talking to the cluster.")
cmd.PersistentFlags().StringVar(&flags.cfgPath, "config", flags.cfgPath, "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental).")
cmd.PersistentFlags().BoolVar(&flags.allowExperimentalUpgrades, "allow-experimental-upgrades", flags.allowExperimentalUpgrades, "Show unstable versions of Kubernetes as an upgrade alternative and allow upgrading to an alpha/beta/release candidate versions of Kubernetes.")
cmd.PersistentFlags().BoolVar(&flags.allowRCUpgrades, "allow-release-candidate-upgrades", flags.allowRCUpgrades, "Show release candidate versions of Kubernetes as an upgrade alternative and allow upgrading to a release candidate versions of Kubernetes.")
cmd.PersistentFlags().BoolVar(&flags.printConfig, "print-config", flags.printConfig, "Whether the configuration file that will be used in the upgrade should be printed or not.")
cmd.PersistentFlags().BoolVar(&flags.skipPreFlight, "skip-preflight-checks", flags.skipPreFlight, "Skip preflight checks normally run before modifying the system")
cmd.AddCommand(NewCmdApply(flags))
cmd.AddCommand(NewCmdPlan(flags))
return cmd
}

View file

@ -0,0 +1,28 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["cmdutil.go"],
visibility = ["//visibility:public"],
deps = ["//vendor/github.com/spf13/cobra:go_default_library"],
)
go_test(
name = "go_default_test",
srcs = ["cmdutil_test.go"],
library = ":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"],
)

View file

@ -0,0 +1,57 @@
/*
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 phases
import (
"fmt"
"github.com/spf13/cobra"
)
// SubCmdRunE returns a function that handles a case where a subcommand must be specified
// Without this callback, if a user runs just the command without a subcommand,
// or with an invalid subcommand, cobra will print usage information, but still exit cleanly.
// We want to return an error code in these cases so that the
// user knows that their command was invalid.
func SubCmdRunE(name string) func(*cobra.Command, []string) error {
return func(_ *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("missing subcommand; %q is not meant to be run on its own", name)
}
return fmt.Errorf("invalid subcommand: %q", args[0])
}
}
// ValidateExactArgNumber validates that the required top-level arguments are specified
func ValidateExactArgNumber(args []string, supportedArgs []string) error {
validArgs := 0
// Disregard possible "" arguments; they are invalid
for _, arg := range args {
if len(arg) > 0 {
validArgs++
}
}
if validArgs < len(supportedArgs) {
return fmt.Errorf("missing one or more required arguments. Required arguments: %v", supportedArgs)
}
if validArgs > len(supportedArgs) {
return fmt.Errorf("too many arguments, only %d argument(s) supported: %v", validArgs, supportedArgs)
}
return nil
}

View file

@ -0,0 +1,64 @@
/*
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 phases
import (
"testing"
)
func TestValidateExactArgNumber(t *testing.T) {
var tests = []struct {
args, supportedArgs []string
expectedErr bool
}{
{ // one arg given and one arg expected
args: []string{"my-node-1234"},
supportedArgs: []string{"node-name"},
expectedErr: false,
},
{ // two args given and two args expected
args: []string{"my-node-1234", "foo"},
supportedArgs: []string{"node-name", "second-toplevel-arg"},
expectedErr: false,
},
{ // too few supplied args
args: []string{},
supportedArgs: []string{"node-name"},
expectedErr: true,
},
{ // too few non-empty args
args: []string{""},
supportedArgs: []string{"node-name"},
expectedErr: true,
},
{ // too many args
args: []string{"my-node-1234", "foo"},
supportedArgs: []string{"node-name"},
expectedErr: true,
},
}
for _, rt := range tests {
actual := ValidateExactArgNumber(rt.args, rt.supportedArgs)
if (actual != nil) != rt.expectedErr {
t.Errorf(
"failed ValidateExactArgNumber:\n\texpected error: %t\n\t actual error: %t",
rt.expectedErr,
(actual != nil),
)
}
}
}

View file

@ -0,0 +1,80 @@
/*
Copyright 2016 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 cmd
import (
"encoding/json"
"fmt"
"io"
"github.com/ghodss/yaml"
"github.com/spf13/cobra"
apimachineryversion "k8s.io/apimachinery/pkg/version"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/version"
)
// Version provides the version information of kubeadm.
type Version struct {
ClientVersion *apimachineryversion.Info `json:"clientVersion"`
}
func NewCmdVersion(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "version",
Short: i18n.T("Print the version of kubeadm"),
Run: func(cmd *cobra.Command, args []string) {
err := RunVersion(out, cmd)
kubeadmutil.CheckErr(err)
},
}
cmd.Flags().StringP("output", "o", "", "output format, options available are yaml, json and short")
return cmd
}
func RunVersion(out io.Writer, cmd *cobra.Command) error {
clientVersion := version.Get()
v := Version{
ClientVersion: &clientVersion,
}
switch of := cmdutil.GetFlagString(cmd, "output"); of {
case "":
fmt.Fprintf(out, "kubeadm version: %#v\n", v.ClientVersion)
case "short":
fmt.Fprintf(out, "%s\n", v.ClientVersion.GitVersion)
case "yaml":
y, err := yaml.Marshal(&v)
if err != nil {
return err
}
fmt.Fprintln(out, string(y))
case "json":
y, err := json.MarshalIndent(&v, "", " ")
if err != nil {
return err
}
fmt.Fprintln(out, string(y))
default:
return fmt.Errorf("invalid output format: %s", of)
}
return nil
}

View file

@ -0,0 +1,36 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["constants.go"],
deps = [
"//pkg/util/version:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
go_test(
name = "go_default_test",
srcs = ["constants_test.go"],
library = ":go_default_library",
deps = ["//pkg/util/version:go_default_library"],
)

View file

@ -0,0 +1,210 @@
/*
Copyright 2016 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 constants
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"time"
"k8s.io/api/core/v1"
"k8s.io/kubernetes/pkg/util/version"
)
// KubernetesDir is the directory kubernetes owns for storing various configuration files
// This semi-constant MUST NOT be modified during runtime. It's a variable solely for use in unit testing.
var KubernetesDir = "/etc/kubernetes"
const (
ManifestsSubDirName = "manifests"
TempDirForKubeadm = "/etc/kubernetes/tmp"
CACertAndKeyBaseName = "ca"
CACertName = "ca.crt"
CAKeyName = "ca.key"
APIServerCertAndKeyBaseName = "apiserver"
APIServerCertName = "apiserver.crt"
APIServerKeyName = "apiserver.key"
APIServerCertCommonName = "kube-apiserver" //used as subject.commonname attribute (CN)
APIServerKubeletClientCertAndKeyBaseName = "apiserver-kubelet-client"
APIServerKubeletClientCertName = "apiserver-kubelet-client.crt"
APIServerKubeletClientKeyName = "apiserver-kubelet-client.key"
APIServerKubeletClientCertCommonName = "kube-apiserver-kubelet-client" //used as subject.commonname attribute (CN)
ServiceAccountKeyBaseName = "sa"
ServiceAccountPublicKeyName = "sa.pub"
ServiceAccountPrivateKeyName = "sa.key"
FrontProxyCACertAndKeyBaseName = "front-proxy-ca"
FrontProxyCACertName = "front-proxy-ca.crt"
FrontProxyCAKeyName = "front-proxy-ca.key"
FrontProxyClientCertAndKeyBaseName = "front-proxy-client"
FrontProxyClientCertName = "front-proxy-client.crt"
FrontProxyClientKeyName = "front-proxy-client.key"
FrontProxyClientCertCommonName = "front-proxy-client" //used as subject.commonname attribute (CN)
AdminKubeConfigFileName = "admin.conf"
KubeletBootstrapKubeConfigFileName = "bootstrap-kubelet.conf"
KubeletKubeConfigFileName = "kubelet.conf"
ControllerManagerKubeConfigFileName = "controller-manager.conf"
SchedulerKubeConfigFileName = "scheduler.conf"
// Some well-known users and groups in the core Kubernetes authorization system
ControllerManagerUser = "system:kube-controller-manager"
SchedulerUser = "system:kube-scheduler"
MastersGroup = "system:masters"
NodesGroup = "system:nodes"
NodesClusterRoleBinding = "system:node"
// APICallRetryInterval defines how long kubeadm should wait before retrying a failed API operation
APICallRetryInterval = 500 * time.Millisecond
// DiscoveryRetryInterval specifies how long kubeadm should wait before retrying to connect to the master when doing discovery
DiscoveryRetryInterval = 5 * time.Second
// MarkMasterTimeout specifies how long kubeadm should wait for applying the label and taint on the master before timing out
MarkMasterTimeout = 2 * time.Minute
// Minimum amount of nodes the Service subnet should allow.
// We need at least ten, because the DNS service is always at the tenth cluster clusterIP
MinimumAddressesInServiceSubnet = 10
// DefaultTokenDuration specifies the default amount of time that a bootstrap token will be valid
// Default behaviour is 24 hours
DefaultTokenDuration = 24 * time.Hour
// LabelNodeRoleMaster specifies that a node is a master
// It's copied over to kubeadm until it's merged in core: https://github.com/kubernetes/kubernetes/pull/39112
LabelNodeRoleMaster = "node-role.kubernetes.io/master"
// MasterConfigurationConfigMap specifies in what ConfigMap in the kube-system namespace the `kubeadm init` configuration should be stored
MasterConfigurationConfigMap = "kubeadm-config"
// MasterConfigurationConfigMapKey specifies in what ConfigMap key the master configuration should be stored
MasterConfigurationConfigMapKey = "MasterConfiguration"
// MinExternalEtcdVersion indicates minimum external etcd version which kubeadm supports
MinExternalEtcdVersion = "3.0.14"
// DefaultEtcdVersion indicates the default etcd version that kubeadm uses
DefaultEtcdVersion = "3.0.17"
Etcd = "etcd"
KubeAPIServer = "kube-apiserver"
KubeControllerManager = "kube-controller-manager"
KubeScheduler = "kube-scheduler"
KubeProxy = "kube-proxy"
// SelfHostingPrefix describes the prefix workloads that are self-hosted by kubeadm has
SelfHostingPrefix = "self-hosted-"
// KubeCertificatesVolumeName specifies the name for the Volume that is used for injecting certificates to control plane components (can be both a hostPath volume or a projected, all-in-one volume)
KubeCertificatesVolumeName = "k8s-certs"
// KubeConfigVolumeName specifies the name for the Volume that is used for injecting the kubeconfig to talk securely to the api server for a control plane component if applicable
KubeConfigVolumeName = "kubeconfig"
// V17NodeBootstrapTokenAuthGroup specifies which group a Node Bootstrap Token should be authenticated in, in v1.7
V17NodeBootstrapTokenAuthGroup = "system:bootstrappers"
// V18NodeBootstrapTokenAuthGroup specifies which group a Node Bootstrap Token should be authenticated in, in v1.8
V18NodeBootstrapTokenAuthGroup = "system:bootstrappers:kubeadm:default-node-token"
// DefaultCIImageRepository points to image registry where CI uploads images from ci-cross build job
DefaultCIImageRepository = "gcr.io/kubernetes-ci-images"
)
var (
// MasterTaint is the taint to apply on the PodSpec for being able to run that Pod on the master
MasterTaint = v1.Taint{
Key: LabelNodeRoleMaster,
Effect: v1.TaintEffectNoSchedule,
}
// MasterToleration is the toleration to apply on the PodSpec for being able to run that Pod on the master
MasterToleration = v1.Toleration{
Key: LabelNodeRoleMaster,
Effect: v1.TaintEffectNoSchedule,
}
AuthorizationPolicyPath = filepath.Join(KubernetesDir, "abac_policy.json")
AuthorizationWebhookConfigPath = filepath.Join(KubernetesDir, "webhook_authz.conf")
// DefaultTokenUsages specifies the default functions a token will get
DefaultTokenUsages = []string{"signing", "authentication"}
// MasterComponents defines the master component names
MasterComponents = []string{KubeAPIServer, KubeControllerManager, KubeScheduler}
// MinimumControlPlaneVersion specifies the minimum control plane version kubeadm can deploy
MinimumControlPlaneVersion = version.MustParseSemantic("v1.7.0")
// MinimumCSRAutoApprovalClusterRolesVersion defines whether kubeadm can rely on the built-in CSR approval ClusterRole or not (note, the binding is always created by kubeadm!)
// TODO: Remove this when the v1.9 cycle starts and we bump the minimum supported version to v1.8.0
MinimumCSRAutoApprovalClusterRolesVersion = version.MustParseSemantic("v1.8.0-alpha.3")
// UseEnableBootstrapTokenAuthFlagVersion defines the first version where the API server supports the --enable-bootstrap-token-auth flag instead of the old and deprecated flag.
// TODO: Remove this when the v1.9 cycle starts and we bump the minimum supported version to v1.8.0
UseEnableBootstrapTokenAuthFlagVersion = version.MustParseSemantic("v1.8.0-beta.0")
)
// GetStaticPodDirectory returns the location on the disk where the Static Pod should be present
func GetStaticPodDirectory() string {
return filepath.Join(KubernetesDir, ManifestsSubDirName)
}
// GetStaticPodFilepath returns the location on the disk where the Static Pod should be present
func GetStaticPodFilepath(componentName, manifestsDir string) string {
return filepath.Join(manifestsDir, componentName+".yaml")
}
// GetAdminKubeConfigPath returns the location on the disk where admin kubeconfig is located by default
func GetAdminKubeConfigPath() string {
return filepath.Join(KubernetesDir, AdminKubeConfigFileName)
}
// AddSelfHostedPrefix adds the self-hosted- prefix to the component name
func AddSelfHostedPrefix(componentName string) string {
return fmt.Sprintf("%s%s", SelfHostingPrefix, componentName)
}
// CreateTempDirForKubeadm is a function that creates a temporary directory under /etc/kubernetes/tmp (not using /tmp as that would potentially be dangerous)
func CreateTempDirForKubeadm(dirName string) (string, error) {
// creates target folder if not already exists
if err := os.MkdirAll(TempDirForKubeadm, 0700); err != nil {
return "", fmt.Errorf("failed to create directory %q: %v", TempDirForKubeadm, err)
}
tempDir, err := ioutil.TempDir(TempDirForKubeadm, dirName)
if err != nil {
return "", fmt.Errorf("couldn't create a temporary directory: %v", err)
}
return tempDir, nil
}
// GetNodeBootstrapTokenAuthGroup gets the bootstrap token auth group conditionally based on version
func GetNodeBootstrapTokenAuthGroup(k8sVersion *version.Version) string {
if k8sVersion.AtLeast(UseEnableBootstrapTokenAuthFlagVersion) {
return V18NodeBootstrapTokenAuthGroup
}
return V17NodeBootstrapTokenAuthGroup
}

View file

@ -0,0 +1,159 @@
/*
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 constants
import (
"testing"
"k8s.io/kubernetes/pkg/util/version"
)
func TestGetStaticPodDirectory(t *testing.T) {
expected := "/etc/kubernetes/manifests"
actual := GetStaticPodDirectory()
if actual != expected {
t.Errorf(
"failed GetStaticPodDirectory:\n\texpected: %s\n\t actual: %s",
expected,
actual,
)
}
}
func TestGetAdminKubeConfigPath(t *testing.T) {
expected := "/etc/kubernetes/admin.conf"
actual := GetAdminKubeConfigPath()
if actual != expected {
t.Errorf(
"failed GetAdminKubeConfigPath:\n\texpected: %s\n\t actual: %s",
expected,
actual,
)
}
}
func TestGetStaticPodFilepath(t *testing.T) {
var tests = []struct {
componentName, manifestsDir, expected string
}{
{
componentName: "kube-apiserver",
manifestsDir: "/etc/kubernetes/manifests",
expected: "/etc/kubernetes/manifests/kube-apiserver.yaml",
},
{
componentName: "kube-controller-manager",
manifestsDir: "/etc/kubernetes/manifests/",
expected: "/etc/kubernetes/manifests/kube-controller-manager.yaml",
},
{
componentName: "foo",
manifestsDir: "/etc/bar/",
expected: "/etc/bar/foo.yaml",
},
}
for _, rt := range tests {
actual := GetStaticPodFilepath(rt.componentName, rt.manifestsDir)
if actual != rt.expected {
t.Errorf(
"failed GetStaticPodFilepath:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual,
)
}
}
}
func TestAddSelfHostedPrefix(t *testing.T) {
var tests = []struct {
componentName, expected string
}{
{
componentName: "kube-apiserver",
expected: "self-hosted-kube-apiserver",
},
{
componentName: "kube-controller-manager",
expected: "self-hosted-kube-controller-manager",
},
{
componentName: "kube-scheduler",
expected: "self-hosted-kube-scheduler",
},
{
componentName: "foo",
expected: "self-hosted-foo",
},
}
for _, rt := range tests {
actual := AddSelfHostedPrefix(rt.componentName)
if actual != rt.expected {
t.Errorf(
"failed AddSelfHostedPrefix:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual,
)
}
}
}
func TestGetNodeBootstrapTokenAuthGroup(t *testing.T) {
var tests = []struct {
k8sVersion, expected string
}{
{
k8sVersion: "v1.7.0",
expected: "system:bootstrappers",
},
{
k8sVersion: "v1.7.8",
expected: "system:bootstrappers",
},
{
k8sVersion: "v1.8.0-alpha.3",
expected: "system:bootstrappers",
},
{
k8sVersion: "v1.8.0-beta.0",
expected: "system:bootstrappers:kubeadm:default-node-token",
},
{
k8sVersion: "v1.8.0-rc.1",
expected: "system:bootstrappers:kubeadm:default-node-token",
},
{
k8sVersion: "v1.8.0",
expected: "system:bootstrappers:kubeadm:default-node-token",
},
{
k8sVersion: "v1.8.9",
expected: "system:bootstrappers:kubeadm:default-node-token",
},
}
for _, rt := range tests {
actual := GetNodeBootstrapTokenAuthGroup(version.MustParseSemantic(rt.k8sVersion))
if actual != rt.expected {
t.Errorf(
"failed GetNodeBootstrapTokenAuthGroup:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual,
)
}
}
}

View file

@ -0,0 +1,45 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["discovery.go"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/discovery/file:go_default_library",
"//cmd/kubeadm/app/discovery/https:go_default_library",
"//cmd/kubeadm/app/discovery/token:go_default_library",
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["discovery_test.go"],
library = ":go_default_library",
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",
"//cmd/kubeadm/app/discovery/file:all-srcs",
"//cmd/kubeadm/app/discovery/https:all-srcs",
"//cmd/kubeadm/app/discovery/token:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -0,0 +1,71 @@
/*
Copyright 2016 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 discovery
import (
"fmt"
"net/url"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/discovery/file"
"k8s.io/kubernetes/cmd/kubeadm/app/discovery/https"
"k8s.io/kubernetes/cmd/kubeadm/app/discovery/token"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
)
const TokenUser = "tls-bootstrap-token-user"
// For returns a KubeConfig object that can be used for doing the TLS Bootstrap with the right credentials
// Also, before returning anything, it makes sure it can trust the API Server
func For(cfg *kubeadmapi.NodeConfiguration) (*clientcmdapi.Config, error) {
// TODO: Print summary info about the CA certificate, along with the the checksum signature
// we also need an ability for the user to configure the client to validate received CA cert against a checksum
clusterinfo, err := GetValidatedClusterInfoObject(cfg)
if err != nil {
return nil, fmt.Errorf("couldn't validate the identity of the API Server: %v", err)
}
return kubeconfigutil.CreateWithToken(
clusterinfo.Server,
"kubernetes",
TokenUser,
clusterinfo.CertificateAuthorityData,
cfg.TLSBootstrapToken,
), nil
}
// GetValidatedClusterInfoObject returns a validated Cluster object that specifies where the cluster is and the CA cert to trust
func GetValidatedClusterInfoObject(cfg *kubeadmapi.NodeConfiguration) (*clientcmdapi.Cluster, error) {
switch {
case len(cfg.DiscoveryFile) != 0:
if isHTTPSURL(cfg.DiscoveryFile) {
return https.RetrieveValidatedClusterInfo(cfg.DiscoveryFile)
}
return file.RetrieveValidatedClusterInfo(cfg.DiscoveryFile)
case len(cfg.DiscoveryToken) != 0:
return token.RetrieveValidatedClusterInfo(cfg.DiscoveryToken, cfg.DiscoveryTokenAPIServers, cfg.DiscoveryTokenCACertHashes)
default:
return nil, fmt.Errorf("couldn't find a valid discovery configuration.")
}
}
// isHTTPSURL checks whether the string is parsable as an URL and whether the Scheme is https
func isHTTPSURL(s string) bool {
u, err := url.Parse(s)
return err == nil && u.Scheme == "https"
}

View file

@ -0,0 +1,66 @@
/*
Copyright 2016 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 discovery
import (
"testing"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func TestFor(t *testing.T) {
tests := []struct {
d kubeadm.NodeConfiguration
expect bool
}{
{d: kubeadm.NodeConfiguration{}, expect: false},
{
d: kubeadm.NodeConfiguration{
DiscoveryFile: "notnil",
},
expect: false,
},
{
d: kubeadm.NodeConfiguration{
DiscoveryFile: "https://localhost",
},
expect: false,
},
{
d: kubeadm.NodeConfiguration{
DiscoveryFile: "notnil",
},
expect: false,
},
{
d: kubeadm.NodeConfiguration{
DiscoveryToken: "foo.bar@foobar",
},
expect: false,
},
}
for _, rt := range tests {
_, actual := For(&rt.d)
if (actual == nil) != rt.expect {
t.Errorf(
"failed For:\n\texpected: %t\n\t actual: %t",
rt.expect,
(actual == nil),
)
}
}
}

View file

@ -0,0 +1,35 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["file.go"],
deps = [
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
"//pkg/bootstrap/api:go_default_library",
"//vendor/k8s.io/api/core/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/util/wait:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,130 @@
/*
Copyright 2016 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 file
import (
"fmt"
"k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
bootstrapapi "k8s.io/kubernetes/pkg/bootstrap/api"
)
// RetrieveValidatedClusterInfo connects to the API Server and makes sure it can talk
// securely to the API Server using the provided CA cert and
// optionally refreshes the cluster-info information from the cluster-info ConfigMap
func RetrieveValidatedClusterInfo(filepath string) (*clientcmdapi.Cluster, error) {
clusterinfo, err := clientcmd.LoadFromFile(filepath)
if err != nil {
return nil, err
}
return ValidateClusterInfo(clusterinfo)
}
// ValidateClusterInfo connects to the API Server and makes sure it can talk
// securely to the API Server using the provided CA cert and
// optionally refreshes the cluster-info information from the cluster-info ConfigMap
func ValidateClusterInfo(clusterinfo *clientcmdapi.Config) (*clientcmdapi.Cluster, error) {
err := validateClusterInfoKubeConfig(clusterinfo)
if err != nil {
return nil, err
}
// This is the cluster object we've got from the cluster-info KubeConfig file
defaultCluster := kubeconfigutil.GetClusterFromKubeConfig(clusterinfo)
// Create a new kubeconfig object from the given, just copy over the server and the CA cert
// We do this in order to not pick up other possible misconfigurations in the clusterinfo file
configFromClusterInfo := kubeconfigutil.CreateBasic(
defaultCluster.Server,
"kubernetes",
"", // no user provided
defaultCluster.CertificateAuthorityData,
)
client, err := kubeconfigutil.KubeConfigToClientSet(configFromClusterInfo)
if err != nil {
return nil, err
}
fmt.Printf("[discovery] Created cluster-info discovery client, requesting info from %q\n", defaultCluster.Server)
var clusterinfoCM *v1.ConfigMap
wait.PollInfinite(constants.DiscoveryRetryInterval, func() (bool, error) {
var err error
clusterinfoCM, err = client.CoreV1().ConfigMaps(metav1.NamespacePublic).Get(bootstrapapi.ConfigMapClusterInfo, metav1.GetOptions{})
if err != nil {
if apierrors.IsForbidden(err) {
// If the request is unauthorized, the cluster admin has not granted access to the cluster info configmap for unauthenicated users
// In that case, trust the cluster admin and do not refresh the cluster-info credentials
fmt.Printf("[discovery] Could not access the %s ConfigMap for refreshing the cluster-info information, but the TLS cert is valid so proceeding...\n", bootstrapapi.ConfigMapClusterInfo)
return true, nil
} else {
fmt.Printf("[discovery] Failed to validate the API Server's identity, will try again: [%v]\n", err)
return false, nil
}
}
return true, nil
})
// If we couldn't fetch the cluster-info ConfigMap, just return the cluster-info object the user provided
if clusterinfoCM == nil {
return defaultCluster, nil
}
// We somehow got hold of the ConfigMap, try to read some data from it. If we can't, fallback on the user-provided file
refreshedBaseKubeConfig, err := tryParseClusterInfoFromConfigMap(clusterinfoCM)
if err != nil {
fmt.Printf("[discovery] The %s ConfigMap isn't set up properly (%v), but the TLS cert is valid so proceeding...\n", bootstrapapi.ConfigMapClusterInfo, err)
return defaultCluster, nil
}
fmt.Println("[discovery] Synced cluster-info information from the API Server so we have got the latest information")
// In an HA world in the future, this will make more sense, because now we've got new information, possibly about new API Servers to talk to
return kubeconfigutil.GetClusterFromKubeConfig(refreshedBaseKubeConfig), nil
}
// tryParseClusterInfoFromConfigMap tries to parse a kubeconfig file from a ConfigMap key
func tryParseClusterInfoFromConfigMap(cm *v1.ConfigMap) (*clientcmdapi.Config, error) {
kubeConfigString, ok := cm.Data[bootstrapapi.KubeConfigKey]
if !ok || len(kubeConfigString) == 0 {
return nil, fmt.Errorf("no %s key in ConfigMap", bootstrapapi.KubeConfigKey)
}
parsedKubeConfig, err := clientcmd.Load([]byte(kubeConfigString))
if err != nil {
return nil, fmt.Errorf("couldn't parse the kubeconfig file in the %s ConfigMap: %v", bootstrapapi.ConfigMapClusterInfo, err)
}
return parsedKubeConfig, nil
}
// validateClusterInfoKubeConfig makes sure the user-provided cluster-info KubeConfig file is valid
func validateClusterInfoKubeConfig(clusterinfo *clientcmdapi.Config) error {
if len(clusterinfo.Clusters) < 1 {
return fmt.Errorf("the provided cluster-info KubeConfig file must have at least one Cluster defined")
}
defaultCluster := kubeconfigutil.GetClusterFromKubeConfig(clusterinfo)
if defaultCluster == nil {
return fmt.Errorf("the provided cluster-info KubeConfig file must have an unnamed Cluster or a CurrentContext that specifies a non-nil Cluster")
}
return nil
}

View file

@ -0,0 +1,29 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["https.go"],
deps = [
"//cmd/kubeadm/app/discovery/file:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,48 @@
/*
Copyright 2016 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 https
import (
"io/ioutil"
"net/http"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/kubernetes/cmd/kubeadm/app/discovery/file"
)
// RetrieveValidatedClusterInfo connects to the API Server and makes sure it can talk
// securely to the API Server using the provided CA cert and
// optionally refreshes the cluster-info information from the cluster-info ConfigMap
func RetrieveValidatedClusterInfo(httpsURL string) (*clientcmdapi.Cluster, error) {
response, err := http.Get(httpsURL)
if err != nil {
return nil, err
}
defer response.Body.Close()
kubeconfig, err := ioutil.ReadAll(response.Body)
if err != nil {
return nil, err
}
clusterinfo, err := clientcmd.Load(kubeconfig)
if err != nil {
return nil, err
}
return file.ValidateClusterInfo(clusterinfo)
}

View file

@ -0,0 +1,48 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["token.go"],
deps = [
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
"//cmd/kubeadm/app/util/pubkeypin:go_default_library",
"//cmd/kubeadm/app/util/token:go_default_library",
"//pkg/bootstrap/api:go_default_library",
"//pkg/controller/bootstrap: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/util/wait:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
go_test(
name = "go_default_test",
srcs = ["token_test.go"],
library = ":go_default_library",
deps = [
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
],
)

View file

@ -0,0 +1,221 @@
/*
Copyright 2016 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 (
"bytes"
"crypto/x509"
"encoding/pem"
"fmt"
"sync"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
"k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin"
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
bootstrapapi "k8s.io/kubernetes/pkg/bootstrap/api"
"k8s.io/kubernetes/pkg/controller/bootstrap"
)
const BootstrapUser = "token-bootstrap-client"
// RetrieveValidatedClusterInfo connects to the API Server and tries to fetch the cluster-info ConfigMap
// It then makes sure it can trust the API Server by looking at the JWS-signed tokens and (if rootCAPubKeys is not empty)
// validating the cluster CA against a set of pinned public keys
func RetrieveValidatedClusterInfo(discoveryToken string, tokenAPIServers, rootCAPubKeys []string) (*clientcmdapi.Cluster, error) {
tokenId, tokenSecret, err := tokenutil.ParseToken(discoveryToken)
if err != nil {
return nil, err
}
// Load the cfg.DiscoveryTokenCACertHashes into a pubkeypin.Set
pubKeyPins := pubkeypin.NewSet()
err = pubKeyPins.Allow(rootCAPubKeys...)
if err != nil {
return nil, err
}
// The function below runs for every endpoint, and all endpoints races with each other.
// The endpoint that wins the race and completes the task first gets its kubeconfig returned below
baseKubeConfig := runForEndpointsAndReturnFirst(tokenAPIServers, func(endpoint string) (*clientcmdapi.Config, error) {
insecureBootstrapConfig := buildInsecureBootstrapKubeConfig(endpoint)
clusterName := insecureBootstrapConfig.Contexts[insecureBootstrapConfig.CurrentContext].Cluster
insecureClient, err := kubeconfigutil.KubeConfigToClientSet(insecureBootstrapConfig)
if err != nil {
return nil, err
}
fmt.Printf("[discovery] Created cluster-info discovery client, requesting info from %q\n", insecureBootstrapConfig.Clusters[clusterName].Server)
// Make an initial insecure connection to get the cluster-info ConfigMap
var insecureClusterInfo *v1.ConfigMap
wait.PollImmediateInfinite(constants.DiscoveryRetryInterval, func() (bool, error) {
var err error
insecureClusterInfo, err = insecureClient.CoreV1().ConfigMaps(metav1.NamespacePublic).Get(bootstrapapi.ConfigMapClusterInfo, metav1.GetOptions{})
if err != nil {
fmt.Printf("[discovery] Failed to request cluster info, will try again: [%s]\n", err)
return false, nil
}
return true, nil
})
// Validate the MAC on the kubeconfig from the ConfigMap and load it
insecureKubeconfigString, ok := insecureClusterInfo.Data[bootstrapapi.KubeConfigKey]
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)
}
detachedJWSToken, ok := insecureClusterInfo.Data[bootstrapapi.JWSSignatureKeyPrefix+tokenId]
if !ok || len(detachedJWSToken) == 0 {
return nil, fmt.Errorf("there is no JWS signed token in the %s ConfigMap. This token id %q is invalid for this cluster, can't connect", bootstrapapi.ConfigMapClusterInfo, tokenId)
}
if !bootstrap.DetachedTokenIsValid(detachedJWSToken, insecureKubeconfigString, tokenId, tokenSecret) {
return nil, fmt.Errorf("failed to verify JWS signature of received cluster info object, can't trust this API Server")
}
insecureKubeconfigBytes := []byte(insecureKubeconfigString)
insecureConfig, err := clientcmd.Load(insecureKubeconfigBytes)
if err != nil {
return nil, fmt.Errorf("couldn't parse the kubeconfig file in the %s configmap: %v", bootstrapapi.ConfigMapClusterInfo, err)
}
// If no TLS root CA pinning was specified, we're done
if pubKeyPins.Empty() {
fmt.Printf("[discovery] Cluster info signature and contents are valid and no TLS pinning was specified, will use API Server %q\n", endpoint)
return insecureConfig, nil
}
// Load the cluster CA from the Config
if len(insecureConfig.Clusters) != 1 {
return nil, fmt.Errorf("expected the kubeconfig file in the %s configmap to have a single cluster, but it had %d", bootstrapapi.ConfigMapClusterInfo, len(insecureConfig.Clusters))
}
var clusterCABytes []byte
for _, cluster := range insecureConfig.Clusters {
clusterCABytes = cluster.CertificateAuthorityData
}
clusterCA, err := parsePEMCert(clusterCABytes)
if err != nil {
return nil, fmt.Errorf("failed to parse cluster CA from the %s configmap: %v", bootstrapapi.ConfigMapClusterInfo, err)
}
// Validate the cluster CA public key against the pinned set
err = pubKeyPins.Check(clusterCA)
if err != nil {
return nil, fmt.Errorf("cluster CA found in %s configmap is invalid: %v", bootstrapapi.ConfigMapClusterInfo, err)
}
// Now that we know the proported cluster CA, connect back a second time validating with that CA
secureBootstrapConfig := buildSecureBootstrapKubeConfig(endpoint, clusterCABytes)
secureClient, err := kubeconfigutil.KubeConfigToClientSet(secureBootstrapConfig)
if err != nil {
return nil, err
}
fmt.Printf("[discovery] Requesting info from %q again to validate TLS against the pinned public key\n", insecureBootstrapConfig.Clusters[clusterName].Server)
var secureClusterInfo *v1.ConfigMap
wait.PollImmediateInfinite(constants.DiscoveryRetryInterval, func() (bool, error) {
var err error
secureClusterInfo, err = secureClient.CoreV1().ConfigMaps(metav1.NamespacePublic).Get(bootstrapapi.ConfigMapClusterInfo, metav1.GetOptions{})
if err != nil {
fmt.Printf("[discovery] Failed to request cluster info, will try again: [%s]\n", err)
return false, nil
}
return true, nil
})
// Pull the kubeconfig from the securely-obtained ConfigMap and validate that it's the same as what we found the first time
secureKubeconfigBytes := []byte(secureClusterInfo.Data[bootstrapapi.KubeConfigKey])
if !bytes.Equal(secureKubeconfigBytes, insecureKubeconfigBytes) {
return nil, fmt.Errorf("the second kubeconfig from the %s configmap (using validated TLS) was different from the first", bootstrapapi.ConfigMapClusterInfo)
}
secureKubeconfig, err := clientcmd.Load(secureKubeconfigBytes)
if err != nil {
return nil, fmt.Errorf("couldn't parse the kubeconfig file in the %s configmap: %v", bootstrapapi.ConfigMapClusterInfo, err)
}
fmt.Printf("[discovery] Cluster info signature and contents are valid and TLS certificate validates against pinned roots, will use API Server %q\n", endpoint)
return secureKubeconfig, nil
})
return kubeconfigutil.GetClusterFromKubeConfig(baseKubeConfig), nil
}
// buildInsecureBootstrapKubeConfig makes a KubeConfig object that connects insecurely to the API Server for bootstrapping purposes
func buildInsecureBootstrapKubeConfig(endpoint string) *clientcmdapi.Config {
masterEndpoint := fmt.Sprintf("https://%s", endpoint)
clusterName := "kubernetes"
bootstrapConfig := kubeconfigutil.CreateBasic(masterEndpoint, clusterName, BootstrapUser, []byte{})
bootstrapConfig.Clusters[clusterName].InsecureSkipTLSVerify = true
return bootstrapConfig
}
// buildSecureBootstrapKubeConfig makes a KubeConfig object that connects securely to the API Server for bootstrapping purposes (validating with the specified CA)
func buildSecureBootstrapKubeConfig(endpoint string, caCert []byte) *clientcmdapi.Config {
masterEndpoint := fmt.Sprintf("https://%s", endpoint)
bootstrapConfig := kubeconfigutil.CreateBasic(masterEndpoint, "kubernetes", BootstrapUser, caCert)
return bootstrapConfig
}
// runForEndpointsAndReturnFirst loops the endpoints slice and let's the endpoints race for connecting to the master
func runForEndpointsAndReturnFirst(endpoints []string, fetchKubeConfigFunc func(string) (*clientcmdapi.Config, error)) *clientcmdapi.Config {
stopChan := make(chan struct{})
var resultingKubeConfig *clientcmdapi.Config
var once sync.Once
var wg sync.WaitGroup
for _, endpoint := range endpoints {
wg.Add(1)
go func(apiEndpoint string) {
defer wg.Done()
wait.Until(func() {
fmt.Printf("[discovery] Trying to connect to API Server %q\n", apiEndpoint)
cfg, err := fetchKubeConfigFunc(apiEndpoint)
if err != nil {
fmt.Printf("[discovery] Failed to connect to API Server %q: %v\n", apiEndpoint, err)
return
}
fmt.Printf("[discovery] Successfully established connection with API Server %q\n", apiEndpoint)
// connection established, stop all wait threads
once.Do(func() {
close(stopChan)
resultingKubeConfig = cfg
})
}, constants.DiscoveryRetryInterval, stopChan)
}(endpoint)
}
wg.Wait()
return resultingKubeConfig
}
// parsePEMCert decodes a PEM-formatted certificate and returns it as an x509.Certificate
func parsePEMCert(certData []byte) (*x509.Certificate, error) {
pemBlock, trailingData := pem.Decode(certData)
if pemBlock == nil {
return nil, fmt.Errorf("invalid PEM data")
}
if len(trailingData) != 0 {
return nil, fmt.Errorf("trailing data after first PEM block")
}
return x509.ParseCertificate(pemBlock.Bytes)
}

View file

@ -0,0 +1,115 @@
/*
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 (
"strconv"
"testing"
"time"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
)
// testCertPEM is a simple self-signed test certificate issued with the openssl CLI:
// openssl req -new -newkey rsa:2048 -days 36500 -nodes -x509 -keyout /dev/null -out test.crt
const testCertPEM = `
-----BEGIN CERTIFICATE-----
MIIDRDCCAiygAwIBAgIJAJgVaCXvC6HkMA0GCSqGSIb3DQEBBQUAMB8xHTAbBgNV
BAMTFGt1YmVhZG0ta2V5cGlucy10ZXN0MCAXDTE3MDcwNTE3NDMxMFoYDzIxMTcw
NjExMTc0MzEwWjAfMR0wGwYDVQQDExRrdWJlYWRtLWtleXBpbnMtdGVzdDCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK0ba8mHU9UtYlzM1Own2Fk/XGjR
J4uJQvSeGLtz1hID1IA0dLwruvgLCPadXEOw/f/IWIWcmT+ZmvIHZKa/woq2iHi5
+HLhXs7aG4tjKGLYhag1hLjBI7icqV7ovkjdGAt9pWkxEzhIYClFMXDjKpMSynu+
YX6nZ9tic1cOkHmx2yiZdMkuriRQnpTOa7bb03OC1VfGl7gHlOAIYaj4539WCOr8
+ACTUMJUFEHcRZ2o8a/v6F9GMK+7SC8SJUI+GuroXqlMAdhEv4lX5Co52enYaClN
+D9FJLRpBv2YfiCQdJRaiTvCBSxEFz6BN+PtP5l2Hs703ZWEkOqCByM6HV8CAwEA
AaOBgDB+MB0GA1UdDgQWBBRQgUX8MhK2rWBWQiPHWcKzoWDH5DBPBgNVHSMESDBG
gBRQgUX8MhK2rWBWQiPHWcKzoWDH5KEjpCEwHzEdMBsGA1UEAxMUa3ViZWFkbS1r
ZXlwaW5zLXRlc3SCCQCYFWgl7wuh5DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB
BQUAA4IBAQCaAUif7Pfx3X0F08cxhx8/Hdx4jcJw6MCq6iq6rsXM32ge43t8OHKC
pJW08dk58a3O1YQSMMvD6GJDAiAfXzfwcwY6j258b1ZlI9Ag0VokvhMl/XfdCsdh
AWImnL1t4hvU5jLaImUUMlYxMcSfHBGAm7WJIZ2LdEfg6YWfZh+WGbg1W7uxLxk6
y4h5rWdNnzBHWAGf7zJ0oEDV6W6RSwNXtC0JNnLaeIUm/6xdSddJlQPwUv8YH4jX
c1vuFqTnJBPcb7W//R/GI2Paicm1cmns9NLnPR35exHxFTy+D1yxmGokpoPMdife
aH+sfuxT8xeTPb3kjzF9eJTlnEquUDLM
-----END CERTIFICATE-----`
func TestRunForEndpointsAndReturnFirst(t *testing.T) {
tests := []struct {
endpoints []string
expectedEndpoint string
}{
{
endpoints: []string{"1", "2", "3"},
expectedEndpoint: "1",
},
{
endpoints: []string{"6", "5"},
expectedEndpoint: "5",
},
{
endpoints: []string{"10", "4"},
expectedEndpoint: "4",
},
}
for _, rt := range tests {
returnKubeConfig := runForEndpointsAndReturnFirst(rt.endpoints, func(endpoint string) (*clientcmdapi.Config, error) {
timeout, _ := strconv.Atoi(endpoint)
time.Sleep(time.Second * time.Duration(timeout))
return kubeconfigutil.CreateBasic(endpoint, "foo", "foo", []byte{}), nil
})
endpoint := returnKubeConfig.Clusters[returnKubeConfig.Contexts[returnKubeConfig.CurrentContext].Cluster].Server
if endpoint != rt.expectedEndpoint {
t.Errorf(
"failed TestRunForEndpointsAndReturnFirst:\n\texpected: %s\n\t actual: %s",
endpoint,
rt.expectedEndpoint,
)
}
}
}
func TestParsePEMCert(t *testing.T) {
for _, testCase := range []struct {
name string
input []byte
expectValid bool
}{
{"invalid certificate data", []byte{0}, false},
{"certificate with junk appended", []byte(testCertPEM + "\nABC"), false},
{"multiple certificates", []byte(testCertPEM + "\n" + testCertPEM), false},
{"valid", []byte(testCertPEM), true},
} {
cert, err := parsePEMCert(testCase.input)
if testCase.expectValid {
if err != nil {
t.Errorf("failed TestParsePEMCert(%s): unexpected error %v", testCase.name, err)
}
if cert == nil {
t.Errorf("failed TestParsePEMCert(%s): returned nil", testCase.name)
}
} else {
if err == nil {
t.Errorf("failed TestParsePEMCert(%s): expected an error", testCase.name)
}
if cert != nil {
t.Errorf("failed TestParsePEMCert(%s): expected not to get a certificate back, but got one", testCase.name)
}
}
}
}

View file

@ -0,0 +1,33 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["features.go"],
deps = ["//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
go_test(
name = "go_default_test",
srcs = ["features_test.go"],
library = ":go_default_library",
deps = ["//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library"],
)

View file

@ -0,0 +1,113 @@
/*
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 features
import (
"fmt"
"sort"
"strconv"
"strings"
utilfeature "k8s.io/apiserver/pkg/util/feature"
)
const (
// SelfHosting is beta in v1.8
SelfHosting utilfeature.Feature = "SelfHosting"
// StoreCertsInSecrets is alpha in v1.8
StoreCertsInSecrets utilfeature.Feature = "StoreCertsInSecrets"
)
// FeatureList represents a list of feature gates
type FeatureList map[utilfeature.Feature]utilfeature.FeatureSpec
// Enabled indicates whether a feature name has been enabled
func Enabled(featureList map[string]bool, featureName utilfeature.Feature) bool {
return featureList[string(featureName)]
}
// Supports indicates whether a feature name is supported on the given
// feature set
func Supports(featureList FeatureList, featureName string) bool {
for k := range featureList {
if featureName == string(k) {
return true
}
}
return false
}
// Keys returns a slice of feature names for a given feature set
func Keys(featureList FeatureList) []string {
var list []string
for k := range featureList {
list = append(list, string(k))
}
return list
}
// InitFeatureGates are the default feature gates for the init command
var InitFeatureGates = FeatureList{
SelfHosting: {Default: false, PreRelease: utilfeature.Alpha},
StoreCertsInSecrets: {Default: false, PreRelease: utilfeature.Alpha},
}
// KnownFeatures returns a slice of strings describing the FeatureList features.
func KnownFeatures(f *FeatureList) []string {
var known []string
for k, v := range *f {
pre := ""
if v.PreRelease != utilfeature.GA {
pre = fmt.Sprintf("%s - ", v.PreRelease)
}
known = append(known, fmt.Sprintf("%s=true|false (%sdefault=%t)", k, pre, v.Default))
}
sort.Strings(known)
return known
}
// NewFeatureGate parse a string of the form "key1=value1,key2=value2,..." into a
// map[string]bool of known keys or returns an error.
func NewFeatureGate(f *FeatureList, value string) (map[string]bool, error) {
featureGate := map[string]bool{}
for _, s := range strings.Split(value, ",") {
if len(s) == 0 {
continue
}
arr := strings.SplitN(s, "=", 2)
if len(arr) != 2 {
return nil, fmt.Errorf("missing bool value for feature-gate key:%s", s)
}
k := strings.TrimSpace(arr[0])
v := strings.TrimSpace(arr[1])
if !Supports(*f, k) {
return nil, fmt.Errorf("unrecognized feature-gate key: %s", k)
}
boolValue, err := strconv.ParseBool(v)
if err != nil {
return nil, fmt.Errorf("invalid value %v for feature-gate key: %s, use true|false instead", v, k)
}
featureGate[k] = boolValue
}
return featureGate, nil
}

View file

@ -0,0 +1,119 @@
/*
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 features
import (
"reflect"
"testing"
utilfeature "k8s.io/apiserver/pkg/util/feature"
)
func TestKnownFeatures(t *testing.T) {
var someFeatures = FeatureList{
"feature2": {Default: true, PreRelease: utilfeature.Alpha},
"feature1": {Default: false, PreRelease: utilfeature.Beta},
"feature3": {Default: false, PreRelease: utilfeature.GA},
}
r := KnownFeatures(&someFeatures)
if len(r) != 3 {
t.Errorf("KnownFeatures returned %d values, expected 3", len(r))
}
// check the first value is feature1 (the list should be sorted); prerelease and default should be present
f1 := "feature1=true|false (BETA - default=false)"
if r[0] != f1 {
t.Errorf("KnownFeatures returned %s values, expected %s", r[0], f1)
}
// check the second value is feature2; prerelease and default should be present
f2 := "feature2=true|false (ALPHA - default=true)"
if r[1] != f2 {
t.Errorf("KnownFeatures returned %s values, expected %s", r[1], f2)
}
// check the second value is feature3; prerelease should not shown fo GA features; default should be present
f3 := "feature3=true|false (default=false)"
if r[2] != f3 {
t.Errorf("KnownFeatures returned %s values, expected %s", r[2], f3)
}
}
func TestNewFeatureGate(t *testing.T) {
var someFeatures = FeatureList{
"feature1": {Default: false, PreRelease: utilfeature.Beta},
"feature2": {Default: true, PreRelease: utilfeature.Alpha},
}
var tests = []struct {
value string
expectedError bool
expectedFeaturesGate map[string]bool
}{
{ //invalid value (missing =)
value: "invalidValue",
expectedError: true,
},
{ //invalid value (missing =)
value: "feature1=true,invalidValue",
expectedError: true,
},
{ //invalid value (not a boolean)
value: "feature1=notABoolean",
expectedError: true,
},
{ //invalid value (not a boolean)
value: "feature1=true,feature2=notABoolean",
expectedError: true,
},
{ //unrecognized feature-gate key
value: "unknownFeature=false",
expectedError: true,
},
{ //unrecognized feature-gate key
value: "feature1=true,unknownFeature=false",
expectedError: true,
},
{ //one feature
value: "feature1=true",
expectedError: false,
expectedFeaturesGate: map[string]bool{"feature1": true},
},
{ //two features
value: "feature1=true,feature2=false",
expectedError: false,
expectedFeaturesGate: map[string]bool{"feature1": true, "feature2": false},
},
}
for _, test := range tests {
r, err := NewFeatureGate(&someFeatures, test.value)
if !test.expectedError && err != nil {
t.Errorf("NewFeatureGate failed when not expected: %v", err)
continue
} else if test.expectedError && err == nil {
t.Error("NewFeatureGate didn't failed when expected")
continue
}
if !reflect.DeepEqual(r, test.expectedFeaturesGate) {
t.Errorf("NewFeatureGate returned a unexpected value")
}
}
}

36
vendor/k8s.io/kubernetes/cmd/kubeadm/app/images/BUILD generated vendored Normal file
View file

@ -0,0 +1,36 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["images.go"],
deps = [
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["images_test.go"],
library = ":go_default_library",
deps = ["//cmd/kubeadm/app/constants:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,38 @@
/*
Copyright 2016 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 images
import (
"fmt"
"runtime"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
)
func GetCoreImage(image, repoPrefix, k8sVersion, overrideImage string) string {
if overrideImage != "" {
return overrideImage
}
kubernetesImageTag := kubeadmutil.KubernetesVersionToImageTag(k8sVersion)
return map[string]string{
constants.Etcd: fmt.Sprintf("%s/%s-%s:%s", repoPrefix, "etcd", runtime.GOARCH, constants.DefaultEtcdVersion),
constants.KubeAPIServer: fmt.Sprintf("%s/%s-%s:%s", repoPrefix, "kube-apiserver", runtime.GOARCH, kubernetesImageTag),
constants.KubeControllerManager: fmt.Sprintf("%s/%s-%s:%s", repoPrefix, "kube-controller-manager", runtime.GOARCH, kubernetesImageTag),
constants.KubeScheduler: fmt.Sprintf("%s/%s-%s:%s", repoPrefix, "kube-scheduler", runtime.GOARCH, kubernetesImageTag),
}[image]
}

View file

@ -0,0 +1,75 @@
/*
Copyright 2016 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 images
import (
"fmt"
"runtime"
"testing"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
)
const (
testversion = "v10.1.2-alpha.1.100+0123456789abcdef+SOMETHING"
expected = "v10.1.2-alpha.1.100_0123456789abcdef_SOMETHING"
gcrPrefix = "gcr.io/google_containers"
)
func TestGetCoreImage(t *testing.T) {
var tests = []struct {
image, repo, version, override, expected string
}{
{
override: "override",
expected: "override",
},
{
image: constants.Etcd,
repo: gcrPrefix,
expected: fmt.Sprintf("%s/%s-%s:%s", gcrPrefix, "etcd", runtime.GOARCH, constants.DefaultEtcdVersion),
},
{
image: constants.KubeAPIServer,
repo: gcrPrefix,
version: testversion,
expected: fmt.Sprintf("%s/%s-%s:%s", gcrPrefix, "kube-apiserver", runtime.GOARCH, expected),
},
{
image: constants.KubeControllerManager,
repo: gcrPrefix,
version: testversion,
expected: fmt.Sprintf("%s/%s-%s:%s", gcrPrefix, "kube-controller-manager", runtime.GOARCH, expected),
},
{
image: constants.KubeScheduler,
repo: gcrPrefix,
version: testversion,
expected: fmt.Sprintf("%s/%s-%s:%s", gcrPrefix, "kube-scheduler", runtime.GOARCH, expected),
},
}
for _, rt := range tests {
actual := GetCoreImage(rt.image, rt.repo, rt.version, rt.override)
if actual != rt.expected {
t.Errorf(
"failed GetCoreImage:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual,
)
}
}
}

35
vendor/k8s.io/kubernetes/cmd/kubeadm/app/kubeadm.go generated vendored Normal file
View file

@ -0,0 +1,35 @@
/*
Copyright 2016 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 app
import (
"os"
"github.com/spf13/pflag"
_ "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/install"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd"
)
func Run() error {
// We do not want these flags to show up in --help
pflag.CommandLine.MarkHidden("google-json-key")
pflag.CommandLine.MarkHidden("log-flush-frequency")
cmd := cmd.NewKubeadmCommand(os.Stdin, os.Stdout, os.Stderr)
return cmd.Execute()
}

50
vendor/k8s.io/kubernetes/cmd/kubeadm/app/node/BUILD generated vendored Normal file
View file

@ -0,0 +1,50 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"csr.go",
"validate.go",
],
deps = [
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
"//pkg/kubelet/util/csr:go_default_library",
"//vendor/k8s.io/api/certificates/v1beta1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
"//vendor/k8s.io/client-go/util/cert:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["validate_test.go"],
library = ":go_default_library",
deps = [
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/version:go_default_library",
"//vendor/k8s.io/client-go/discovery:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

62
vendor/k8s.io/kubernetes/cmd/kubeadm/app/node/csr.go generated vendored Normal file
View file

@ -0,0 +1,62 @@
/*
Copyright 2016 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 (
"fmt"
"k8s.io/apimachinery/pkg/types"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
certutil "k8s.io/client-go/util/cert"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
"k8s.io/kubernetes/pkg/kubelet/util/csr"
)
// CSRContextAndUser defines the context to use for the client certs in the kubelet kubeconfig file
const CSRContextAndUser = "kubelet-csr"
// PerformTLSBootstrap executes a node certificate signing request.
func PerformTLSBootstrap(cfg *clientcmdapi.Config, hostName string) error {
client, err := kubeconfigutil.KubeConfigToClientSet(cfg)
if err != nil {
return err
}
fmt.Println("[csr] Created API client to obtain unique certificate for this node, generating keys and certificate signing request")
key, err := certutil.MakeEllipticPrivateKeyPEM()
if err != nil {
return fmt.Errorf("failed to generate private key [%v]", err)
}
cert, err := csr.RequestNodeCertificate(client.CertificatesV1beta1().CertificateSigningRequests(), key, types.NodeName(hostName))
if err != nil {
return fmt.Errorf("failed to request signed certificate from the API server [%v]", err)
}
fmt.Println("[csr] Received signed certificate from the API server, generating KubeConfig...")
cfg.AuthInfos[CSRContextAndUser] = &clientcmdapi.AuthInfo{
ClientKeyData: key,
ClientCertificateData: cert,
}
cfg.Contexts[CSRContextAndUser] = &clientcmdapi.Context{
AuthInfo: CSRContextAndUser,
Cluster: cfg.Contexts[cfg.CurrentContext].Cluster,
}
cfg.CurrentContext = CSRContextAndUser
return nil
}

View file

@ -0,0 +1,51 @@
/*
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 (
"fmt"
certsapi "k8s.io/api/certificates/v1beta1"
clientset "k8s.io/client-go/kubernetes"
)
// ValidateAPIServer makes sure the server we're connecting to supports the Beta Certificates API
func ValidateAPIServer(client clientset.Interface) error {
version, err := client.Discovery().ServerVersion()
if err != nil {
return fmt.Errorf("failed to check server version: %v", err)
}
fmt.Printf("[bootstrap] Detected server version: %s\n", version.String())
// Check certificates API. If the server supports the version of the Certificates API we're using, we're good to go
serverGroups, err := client.Discovery().ServerGroups()
if err != nil {
return fmt.Errorf("certificate API check failed: failed to retrieve a list of supported API objects [%v]", err)
}
for _, group := range serverGroups.Groups {
if group.Name == certsapi.SchemeGroupVersion.Group {
for _, version := range group.Versions {
if version.Version == certsapi.SchemeGroupVersion.Version {
fmt.Printf("[bootstrap] The server supports the Certificates API (%s/%s)\n", certsapi.SchemeGroupVersion.Group, certsapi.SchemeGroupVersion.Version)
return nil
}
}
}
}
return fmt.Errorf("certificate API check failed: API server with version %s doesn't support Certificates API (%s/%s), use v1.6.0 or newer",
version.String(), certsapi.SchemeGroupVersion.Group, certsapi.SchemeGroupVersion.Version)
}

View file

@ -0,0 +1,145 @@
/*
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 (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/discovery"
clientset "k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
)
func TestValidateAPIServer(t *testing.T) {
expect := version.Info{
Major: "foo",
Minor: "bar",
GitCommit: "baz",
}
tests := []struct {
s *httptest.Server
expect bool
}{
{
s: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {})),
expect: false,
},
{
s: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
var obj interface{}
switch req.URL.Path {
case "/api":
obj = &metav1.APIVersions{
Versions: []string{
"v1.6.0",
},
}
output, err := json.Marshal(obj)
if err != nil {
t.Fatalf("unexpected encoding error: %v", err)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(output)
default:
output, err := json.Marshal(expect)
if err != nil {
t.Errorf("unexpected encoding error: %v", err)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(output)
}
})),
expect: false,
},
{
s: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
var obj interface{}
switch req.URL.Path {
case "/api":
obj = &metav1.APIVersions{
Versions: []string{
"v1.6.0",
},
}
output, err := json.Marshal(obj)
if err != nil {
t.Fatalf("unexpected encoding error: %v", err)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(output)
case "/apis":
obj = &metav1.APIGroupList{
Groups: []metav1.APIGroup{
{
Name: "certificates.k8s.io",
Versions: []metav1.GroupVersionForDiscovery{
{GroupVersion: "certificates.k8s.io/v1beta1", Version: "v1beta1"},
},
},
},
}
output, err := json.Marshal(obj)
if err != nil {
t.Fatalf("unexpected encoding error: %v", err)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(output)
default:
output, err := json.Marshal(expect)
if err != nil {
t.Errorf("unexpected encoding error: %v", err)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(output)
}
})),
expect: true,
},
}
for _, rt := range tests {
defer rt.s.Close()
rc := &restclient.Config{Host: rt.s.URL}
c, err := discovery.NewDiscoveryClientForConfig(rc)
if err != nil {
t.Fatalf("encountered an error while trying to get the new discovery client: %v", err)
}
cs := &clientset.Clientset{DiscoveryClient: c}
actual := ValidateAPIServer(cs)
if (actual == nil) != rt.expect {
t.Errorf(
"failed TestValidateAPIServer:\n\texpected: %t\n\t actual: %t",
rt.expect,
(actual == nil),
)
}
}
}

View file

@ -0,0 +1,65 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = [
"dns_test.go",
"versions_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/util:go_default_library",
"//pkg/api:go_default_library",
"//pkg/util/version:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
"//vendor/k8s.io/client-go/testing:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"dns.go",
"manifests.go",
"versions.go",
],
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
"//cmd/kubeadm/app/util/apiclient:go_default_library",
"//pkg/api:go_default_library",
"//pkg/util/version:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/api/extensions/v1beta1: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/runtime:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,147 @@
/*
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 dns
import (
"fmt"
"net"
"runtime"
"k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kuberuntime "k8s.io/apimachinery/pkg/runtime"
clientset "k8s.io/client-go/kubernetes"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/version"
)
const (
// KubeDNSServiceAccountName describes the name of the ServiceAccount for the kube-dns addon
KubeDNSServiceAccountName = "kube-dns"
)
// EnsureDNSAddon creates the kube-dns addon
func EnsureDNSAddon(cfg *kubeadmapi.MasterConfiguration, client clientset.Interface) error {
k8sVersion, err := version.ParseSemantic(cfg.KubernetesVersion)
if err != nil {
return fmt.Errorf("couldn't parse kubernetes version %q: %v", cfg.KubernetesVersion, err)
}
if err := CreateServiceAccount(client); err != nil {
return err
}
// Get the YAML manifest conditionally based on the k8s version
kubeDNSDeploymentBytes := GetKubeDNSManifest(k8sVersion)
dnsDeploymentBytes, err := kubeadmutil.ParseTemplate(kubeDNSDeploymentBytes, struct{ ImageRepository, Arch, Version, DNSDomain, MasterTaintKey string }{
ImageRepository: cfg.ImageRepository,
Arch: runtime.GOARCH,
// Get the kube-dns version conditionally based on the k8s version
Version: GetKubeDNSVersion(k8sVersion),
DNSDomain: cfg.Networking.DNSDomain,
MasterTaintKey: kubeadmconstants.LabelNodeRoleMaster,
})
if err != nil {
return fmt.Errorf("error when parsing kube-dns deployment template: %v", err)
}
dnsip, err := getDNSIP(client)
if err != nil {
return err
}
dnsServiceBytes, err := kubeadmutil.ParseTemplate(KubeDNSService, struct{ DNSIP string }{
DNSIP: dnsip.String(),
})
if err != nil {
return fmt.Errorf("error when parsing kube-proxy configmap template: %v", err)
}
if err := createKubeDNSAddon(dnsDeploymentBytes, dnsServiceBytes, client); err != nil {
return err
}
fmt.Println("[addons] Applied essential addon: kube-dns")
return nil
}
// CreateServiceAccount creates the necessary serviceaccounts that kubeadm uses/might use, if they don't already exist.
func CreateServiceAccount(client clientset.Interface) error {
return apiclient.CreateOrUpdateServiceAccount(client, &v1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: KubeDNSServiceAccountName,
Namespace: metav1.NamespaceSystem,
},
})
}
func createKubeDNSAddon(deploymentBytes, serviceBytes []byte, client clientset.Interface) error {
kubednsDeployment := &extensions.Deployment{}
if err := kuberuntime.DecodeInto(api.Codecs.UniversalDecoder(), deploymentBytes, kubednsDeployment); err != nil {
return fmt.Errorf("unable to decode kube-dns deployment %v", err)
}
// Create the Deployment for kube-dns or update it in case it already exists
if err := apiclient.CreateOrUpdateDeployment(client, kubednsDeployment); err != nil {
return err
}
kubednsService := &v1.Service{}
if err := kuberuntime.DecodeInto(api.Codecs.UniversalDecoder(), serviceBytes, kubednsService); err != nil {
return fmt.Errorf("unable to decode kube-dns service %v", err)
}
// Can't use a generic apiclient helper func here as we have to tolerate more than AlreadyExists.
if _, err := client.CoreV1().Services(metav1.NamespaceSystem).Create(kubednsService); err != nil {
// Ignore if the Service is invalid with this error message:
// Service "kube-dns" is invalid: spec.clusterIP: Invalid value: "10.96.0.10": provided IP is already allocated
if !apierrors.IsAlreadyExists(err) && !apierrors.IsInvalid(err) {
return fmt.Errorf("unable to create a new kube-dns service: %v", err)
}
if _, err := client.CoreV1().Services(metav1.NamespaceSystem).Update(kubednsService); err != nil {
return fmt.Errorf("unable to create/update the kube-dns service: %v", err)
}
}
return nil
}
// getDNSIP fetches the kubernetes service's ClusterIP and appends a "0" to it in order to get the DNS IP
func getDNSIP(client clientset.Interface) (net.IP, error) {
k8ssvc, err := client.CoreV1().Services(metav1.NamespaceDefault).Get("kubernetes", metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("couldn't fetch information about the kubernetes service: %v", err)
}
if len(k8ssvc.Spec.ClusterIP) == 0 {
return nil, fmt.Errorf("couldn't fetch a valid clusterIP from the kubernetes service")
}
// Build an IP by taking the kubernetes service's clusterIP and appending a "0" and checking that it's valid
dnsIP := net.ParseIP(fmt.Sprintf("%s0", k8ssvc.Spec.ClusterIP))
if dnsIP == nil {
return nil, fmt.Errorf("could not parse dns ip %q: %v", dnsIP, err)
}
return dnsIP, nil
}

View file

@ -0,0 +1,121 @@
/*
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 dns
import (
"testing"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
clientsetfake "k8s.io/client-go/kubernetes/fake"
core "k8s.io/client-go/testing"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/pkg/api"
)
func TestCreateServiceAccount(t *testing.T) {
tests := []struct {
name string
createErr error
expectErr bool
}{
{
"error-free case",
nil,
false,
},
{
"duplication errors should be ignored",
apierrors.NewAlreadyExists(api.Resource(""), ""),
false,
},
{
"unexpected errors should be returned",
apierrors.NewUnauthorized(""),
true,
},
}
for _, tc := range tests {
client := clientsetfake.NewSimpleClientset()
if tc.createErr != nil {
client.PrependReactor("create", "serviceaccounts", func(action core.Action) (bool, runtime.Object, error) {
return true, nil, tc.createErr
})
}
err := CreateServiceAccount(client)
if tc.expectErr {
if err == nil {
t.Errorf("CreateServiceAccounts(%s) wanted err, got nil", tc.name)
}
continue
} else if !tc.expectErr && err != nil {
t.Errorf("CreateServiceAccounts(%s) returned unexpected err: %v", tc.name, err)
}
wantResourcesCreated := 1
if len(client.Actions()) != wantResourcesCreated {
t.Errorf("CreateServiceAccounts(%s) should have made %d actions, but made %d", tc.name, wantResourcesCreated, len(client.Actions()))
}
for _, action := range client.Actions() {
if action.GetVerb() != "create" || action.GetResource().Resource != "serviceaccounts" {
t.Errorf("CreateServiceAccounts(%s) called [%v %v], but wanted [create serviceaccounts]",
tc.name, action.GetVerb(), action.GetResource().Resource)
}
}
}
}
func TestCompileManifests(t *testing.T) {
var tests = []struct {
manifest string
data interface{}
expected bool
}{
{
manifest: v170AndAboveKubeDNSDeployment,
data: struct{ ImageRepository, Arch, Version, DNSDomain, MasterTaintKey string }{
ImageRepository: "foo",
Arch: "foo",
Version: "foo",
DNSDomain: "foo",
MasterTaintKey: "foo",
},
expected: true,
},
{
manifest: KubeDNSService,
data: struct{ DNSIP string }{
DNSIP: "foo",
},
expected: true,
},
}
for _, rt := range tests {
_, actual := kubeadmutil.ParseTemplate(rt.manifest, rt.data)
if (actual == nil) != rt.expected {
t.Errorf(
"failed CompileManifests:\n\texpected: %t\n\t actual: %t",
rt.expected,
(actual == nil),
)
}
}
}

View file

@ -0,0 +1,215 @@
/*
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 dns
const (
// v170AndAboveKubeDNSDeployment is the kube-dns Deployment manifest for the kube-dns manifest for v1.7+
v170AndAboveKubeDNSDeployment = `
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: kube-dns
namespace: kube-system
labels:
k8s-app: kube-dns
spec:
# replicas: not specified here:
# 1. In order to make Addon Manager do not reconcile this replicas parameter.
# 2. Default is 1.
# 3. Will be tuned in real time if DNS horizontal auto-scaling is turned on.
strategy:
rollingUpdate:
maxSurge: 10%
maxUnavailable: 0
selector:
matchLabels:
k8s-app: kube-dns
template:
metadata:
labels:
k8s-app: kube-dns
spec:
volumes:
- name: kube-dns-config
configMap:
name: kube-dns
optional: true
containers:
- name: kubedns
image: {{ .ImageRepository }}/k8s-dns-kube-dns-{{ .Arch }}:{{ .Version }}
imagePullPolicy: IfNotPresent
resources:
# TODO: Set memory limits when we've profiled the container for large
# clusters, then set request = limit to keep this container in
# guaranteed class. Currently, this container falls into the
# "burstable" category so the kubelet doesn't backoff from restarting it.
limits:
memory: 170Mi
requests:
cpu: 100m
memory: 70Mi
livenessProbe:
httpGet:
path: /healthcheck/kubedns
port: 10054
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 5
readinessProbe:
httpGet:
path: /readiness
port: 8081
scheme: HTTP
# we poll on pod startup for the Kubernetes master service and
# only setup the /readiness HTTP server once that's available.
initialDelaySeconds: 3
timeoutSeconds: 5
args:
- --domain={{ .DNSDomain }}.
- --dns-port=10053
- --config-dir=/kube-dns-config
- --v=2
env:
- name: PROMETHEUS_PORT
value: "10055"
ports:
- containerPort: 10053
name: dns-local
protocol: UDP
- containerPort: 10053
name: dns-tcp-local
protocol: TCP
- containerPort: 10055
name: metrics
protocol: TCP
volumeMounts:
- name: kube-dns-config
mountPath: /kube-dns-config
- name: dnsmasq
image: {{ .ImageRepository }}/k8s-dns-dnsmasq-nanny-{{ .Arch }}:{{ .Version }}
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:
path: /healthcheck/dnsmasq
port: 10054
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 5
args:
- -v=2
- -logtostderr
- -configDir=/etc/k8s/dns/dnsmasq-nanny
- -restartDnsmasq=true
- --
- -k
- --cache-size=1000
- --log-facility=-
- --server=/{{ .DNSDomain }}/127.0.0.1#10053
- --server=/in-addr.arpa/127.0.0.1#10053
- --server=/ip6.arpa/127.0.0.1#10053
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
# see: https://github.com/kubernetes/kubernetes/issues/29055 for details
resources:
requests:
cpu: 150m
memory: 20Mi
volumeMounts:
- name: kube-dns-config
mountPath: /etc/k8s/dns/dnsmasq-nanny
- name: sidecar
image: {{ .ImageRepository }}/k8s-dns-sidecar-{{ .Arch }}:{{ .Version }}
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:
path: /metrics
port: 10054
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 5
args:
- --v=2
- --logtostderr
- --probe=kubedns,127.0.0.1:10053,kubernetes.default.svc.{{ .DNSDomain }},5,A
- --probe=dnsmasq,127.0.0.1:53,kubernetes.default.svc.{{ .DNSDomain }},5,A
ports:
- containerPort: 10054
name: metrics
protocol: TCP
resources:
requests:
memory: 20Mi
cpu: 10m
dnsPolicy: Default # Don't use cluster DNS.
serviceAccountName: kube-dns
tolerations:
- key: CriticalAddonsOnly
operator: Exists
- key: {{ .MasterTaintKey }}
effect: NoSchedule
# TODO: Remove this affinity field as soon as we are using manifest lists
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: beta.kubernetes.io/arch
operator: In
values:
- {{ .Arch }}
`
// KubeDNSService is the kube-dns Service manifest
KubeDNSService = `
apiVersion: v1
kind: Service
metadata:
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "KubeDNS"
name: kube-dns
namespace: kube-system
# Without this resourceVersion value, an update of the Service between versions will yield:
# Service "kube-dns" is invalid: metadata.resourceVersion: Invalid value: "": must be specified for an update
resourceVersion: "0"
spec:
clusterIP: {{ .DNSIP }}
ports:
- name: dns
port: 53
protocol: UDP
targetPort: 53
- name: dns-tcp
port: 53
protocol: TCP
targetPort: 53
selector:
k8s-app: kube-dns
`
)

View file

@ -0,0 +1,40 @@
/*
Copyright 2016 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 dns
import (
"k8s.io/kubernetes/pkg/util/version"
)
const (
kubeDNSv170AndAboveVersion = "1.14.5"
)
// GetKubeDNSVersion returns the right kube-dns version for a specific k8s version
func GetKubeDNSVersion(kubeVersion *version.Version) string {
// v1.7.0+ uses 1.14.5, just return that here
// In the future when the kube-dns version is bumped at HEAD; add conditional logic to return the right versions
// Also, the version might be bumped for different k8s releases on the same branch
return kubeDNSv170AndAboveVersion
}
// GetKubeDNSManifest returns the right kube-dns YAML manifest for a specific k8s version
func GetKubeDNSManifest(kubeVersion *version.Version) string {
// v1.7.0+ has only one known YAML manifest spec, just return that here
// In the future when the kube-dns version is bumped at HEAD; add conditional logic to return the right manifest
return v170AndAboveKubeDNSDeployment
}

View file

@ -0,0 +1,70 @@
/*
Copyright 2016 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 dns
import (
"testing"
"k8s.io/kubernetes/pkg/util/version"
)
func TestGetKubeDNSVersion(t *testing.T) {
var tests = []struct {
k8sVersion, expected string
}{
{
k8sVersion: "v1.7.0",
expected: "1.14.5",
},
{
k8sVersion: "v1.7.1",
expected: "1.14.5",
},
{
k8sVersion: "v1.7.2",
expected: "1.14.5",
},
{
k8sVersion: "v1.7.3",
expected: "1.14.5",
},
{
k8sVersion: "v1.8.0-alpha.2",
expected: "1.14.5",
},
{
k8sVersion: "v1.8.0",
expected: "1.14.5",
},
}
for _, rt := range tests {
k8sVersion, err := version.ParseSemantic(rt.k8sVersion)
if err != nil {
t.Fatalf("couldn't parse kubernetes version %q: %v", rt.k8sVersion, err)
}
actualDNSVersion := GetKubeDNSVersion(k8sVersion)
if actualDNSVersion != rt.expected {
t.Errorf(
"failed GetKubeDNSVersion:\n\texpected: %s\n\t actual: %s",
rt.expected,
actualDNSVersion,
)
}
}
}

View file

@ -0,0 +1,57 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["proxy_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/util:go_default_library",
"//pkg/api:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
"//vendor/k8s.io/client-go/testing:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"manifests.go",
"proxy.go",
],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
"//cmd/kubeadm/app/util/apiclient:go_default_library",
"//pkg/api:go_default_library",
"//plugin/pkg/scheduler/algorithm:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
"//vendor/k8s.io/api/rbac/v1beta1: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/client-go/kubernetes:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,104 @@
/*
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 proxy
const (
// KubeProxyConfigMap is the proxy ConfigMap manifest
KubeProxyConfigMap = `
kind: ConfigMap
apiVersion: v1
metadata:
name: kube-proxy
namespace: kube-system
labels:
app: kube-proxy
data:
kubeconfig.conf: |
apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
server: {{ .MasterEndpoint }}
name: default
contexts:
- context:
cluster: default
namespace: default
user: default
name: default
current-context: default
users:
- name: default
user:
tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
`
// KubeProxyDaemonSet is the proxy DaemonSet manifest
KubeProxyDaemonSet = `
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
labels:
k8s-app: kube-proxy
name: kube-proxy
namespace: kube-system
spec:
selector:
matchLabels:
k8s-app: kube-proxy
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
k8s-app: kube-proxy
spec:
containers:
- name: kube-proxy
image: {{ if .ImageOverride }}{{ .ImageOverride }}{{ else }}{{ .ImageRepository }}/kube-proxy-{{ .Arch }}:{{ .Version }}{{ end }}
imagePullPolicy: IfNotPresent
command:
- /usr/local/bin/kube-proxy
- --kubeconfig=/var/lib/kube-proxy/kubeconfig.conf
{{ .ClusterCIDR }}
securityContext:
privileged: true
volumeMounts:
- mountPath: /var/lib/kube-proxy
name: kube-proxy
- mountPath: /run/xtables.lock
name: xtables-lock
readOnly: false
hostNetwork: true
serviceAccountName: kube-proxy
tolerations:
- key: {{ .MasterTaintKey }}
effect: NoSchedule
- key: {{ .CloudTaintKey }}
value: "true"
effect: NoSchedule
volumes:
- name: kube-proxy
configMap:
name: kube-proxy
- name: xtables-lock
hostPath:
path: /run/xtables.lock
type: FileOrCreate
`
)

View file

@ -0,0 +1,156 @@
/*
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 proxy
import (
"fmt"
"runtime"
"k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1"
rbac "k8s.io/api/rbac/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kuberuntime "k8s.io/apimachinery/pkg/runtime"
clientset "k8s.io/client-go/kubernetes"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm"
)
const (
// KubeProxyClusterRoleName sets the name for the kube-proxy ClusterRole
// TODO: This k8s-generic, well-known constant should be fetchable from another source, not be in this package
KubeProxyClusterRoleName = "system:node-proxier"
// KubeProxyServiceAccountName describes the name of the ServiceAccount for the kube-proxy addon
KubeProxyServiceAccountName = "kube-proxy"
)
// EnsureProxyAddon creates the kube-proxy addons
func EnsureProxyAddon(cfg *kubeadmapi.MasterConfiguration, client clientset.Interface) error {
if err := CreateServiceAccount(client); err != nil {
return fmt.Errorf("error when creating kube-proxy service account: %v", err)
}
// Generate Master Enpoint kubeconfig file
masterEndpoint, err := kubeadmutil.GetMasterEndpoint(cfg)
if err != nil {
return err
}
proxyConfigMapBytes, err := kubeadmutil.ParseTemplate(KubeProxyConfigMap, struct{ MasterEndpoint string }{
// Fetch this value from the kubeconfig file
MasterEndpoint: masterEndpoint})
if err != nil {
return fmt.Errorf("error when parsing kube-proxy configmap template: %v", err)
}
proxyDaemonSetBytes, err := kubeadmutil.ParseTemplate(KubeProxyDaemonSet, struct{ ImageRepository, Arch, Version, ImageOverride, ClusterCIDR, MasterTaintKey, CloudTaintKey string }{
ImageRepository: cfg.GetControlPlaneImageRepository(),
Arch: runtime.GOARCH,
Version: kubeadmutil.KubernetesVersionToImageTag(cfg.KubernetesVersion),
ImageOverride: cfg.UnifiedControlPlaneImage,
ClusterCIDR: getClusterCIDR(cfg.Networking.PodSubnet),
MasterTaintKey: kubeadmconstants.LabelNodeRoleMaster,
CloudTaintKey: algorithm.TaintExternalCloudProvider,
})
if err != nil {
return fmt.Errorf("error when parsing kube-proxy daemonset template: %v", err)
}
if err := createKubeProxyAddon(proxyConfigMapBytes, proxyDaemonSetBytes, client); err != nil {
return err
}
if err := CreateRBACRules(client); err != nil {
return fmt.Errorf("error when creating kube-proxy RBAC rules: %v", err)
}
fmt.Println("[addons] Applied essential addon: kube-proxy")
return nil
}
// CreateServiceAccount creates the necessary serviceaccounts that kubeadm uses/might use, if they don't already exist.
func CreateServiceAccount(client clientset.Interface) error {
return apiclient.CreateOrUpdateServiceAccount(client, &v1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: KubeProxyServiceAccountName,
Namespace: metav1.NamespaceSystem,
},
})
}
// CreateRBACRules creates the essential RBAC rules for a minimally set-up cluster
func CreateRBACRules(client clientset.Interface) error {
if err := createClusterRoleBindings(client); err != nil {
return err
}
return nil
}
func createKubeProxyAddon(configMapBytes, daemonSetbytes []byte, client clientset.Interface) error {
kubeproxyConfigMap := &v1.ConfigMap{}
if err := kuberuntime.DecodeInto(api.Codecs.UniversalDecoder(), configMapBytes, kubeproxyConfigMap); err != nil {
return fmt.Errorf("unable to decode kube-proxy configmap %v", err)
}
// Create the ConfigMap for kube-proxy or update it in case it already exists
if err := apiclient.CreateOrUpdateConfigMap(client, kubeproxyConfigMap); err != nil {
return err
}
kubeproxyDaemonSet := &extensions.DaemonSet{}
if err := kuberuntime.DecodeInto(api.Codecs.UniversalDecoder(), daemonSetbytes, kubeproxyDaemonSet); err != nil {
return fmt.Errorf("unable to decode kube-proxy daemonset %v", err)
}
// Create the DaemonSet for kube-proxy or update it in case it already exists
if err := apiclient.CreateOrUpdateDaemonSet(client, kubeproxyDaemonSet); err != nil {
return err
}
return nil
}
func createClusterRoleBindings(client clientset.Interface) error {
return apiclient.CreateOrUpdateClusterRoleBinding(client, &rbac.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "kubeadm:node-proxier",
},
RoleRef: rbac.RoleRef{
APIGroup: rbac.GroupName,
Kind: "ClusterRole",
Name: KubeProxyClusterRoleName,
},
Subjects: []rbac.Subject{
{
Kind: rbac.ServiceAccountKind,
Name: KubeProxyServiceAccountName,
Namespace: metav1.NamespaceSystem,
},
},
})
}
func getClusterCIDR(podsubnet string) string {
if len(podsubnet) == 0 {
return ""
}
return "- --cluster-cidr=" + podsubnet
}

View file

@ -0,0 +1,140 @@
/*
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 proxy
import (
"testing"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
clientsetfake "k8s.io/client-go/kubernetes/fake"
core "k8s.io/client-go/testing"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/pkg/api"
)
func TestCreateServiceAccount(t *testing.T) {
tests := []struct {
name string
createErr error
expectErr bool
}{
{
"error-free case",
nil,
false,
},
{
"duplication errors should be ignored",
apierrors.NewAlreadyExists(api.Resource(""), ""),
false,
},
{
"unexpected errors should be returned",
apierrors.NewUnauthorized(""),
true,
},
}
for _, tc := range tests {
client := clientsetfake.NewSimpleClientset()
if tc.createErr != nil {
client.PrependReactor("create", "serviceaccounts", func(action core.Action) (bool, runtime.Object, error) {
return true, nil, tc.createErr
})
}
err := CreateServiceAccount(client)
if tc.expectErr {
if err == nil {
t.Errorf("CreateServiceAccounts(%s) wanted err, got nil", tc.name)
}
continue
} else if !tc.expectErr && err != nil {
t.Errorf("CreateServiceAccounts(%s) returned unexpected err: %v", tc.name, err)
}
wantResourcesCreated := 1
if len(client.Actions()) != wantResourcesCreated {
t.Errorf("CreateServiceAccounts(%s) should have made %d actions, but made %d", tc.name, wantResourcesCreated, len(client.Actions()))
}
for _, action := range client.Actions() {
if action.GetVerb() != "create" || action.GetResource().Resource != "serviceaccounts" {
t.Errorf("CreateServiceAccounts(%s) called [%v %v], but wanted [create serviceaccounts]",
tc.name, action.GetVerb(), action.GetResource().Resource)
}
}
}
}
func TestGetClusterCIDR(t *testing.T) {
emptyClusterCIDR := getClusterCIDR("")
if emptyClusterCIDR != "" {
t.Errorf("Invalid format: %s", emptyClusterCIDR)
}
clusterCIDR := getClusterCIDR("10.244.0.0/16")
if clusterCIDR != "- --cluster-cidr=10.244.0.0/16" {
t.Errorf("Invalid format: %s", clusterCIDR)
}
clusterIPv6CIDR := getClusterCIDR("2001:db8::/64")
if clusterIPv6CIDR != "- --cluster-cidr=2001:db8::/64" {
t.Errorf("Invalid format: %s", clusterIPv6CIDR)
}
}
func TestCompileManifests(t *testing.T) {
var tests = []struct {
manifest string
data interface{}
expected bool
}{
{
manifest: KubeProxyConfigMap,
data: struct{ MasterEndpoint string }{
MasterEndpoint: "foo",
},
expected: true,
},
{
manifest: KubeProxyDaemonSet,
data: struct{ ImageRepository, Arch, Version, ImageOverride, ClusterCIDR, MasterTaintKey, CloudTaintKey string }{
ImageRepository: "foo",
Arch: "foo",
Version: "foo",
ImageOverride: "foo",
ClusterCIDR: "foo",
MasterTaintKey: "foo",
CloudTaintKey: "foo",
},
expected: true,
},
}
for _, rt := range tests {
_, actual := kubeadmutil.ParseTemplate(rt.manifest, rt.data)
if (actual == nil) != rt.expected {
t.Errorf(
"failed CompileManifests:\n\texpected: %t\n\t actual: %t",
rt.expected,
(actual == nil),
)
}
}
}

View file

@ -0,0 +1,39 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["clusterroles_test.go"],
library = ":go_default_library",
)
go_library(
name = "go_default_library",
srcs = ["clusterroles.go"],
deps = [
"//cmd/kubeadm/app/constants:go_default_library",
"//pkg/util/version:go_default_library",
"//vendor/k8s.io/api/rbac/v1beta1: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/client-go/kubernetes:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,69 @@
/*
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 apiconfig
import (
"fmt"
rbac "k8s.io/api/rbac/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientset "k8s.io/client-go/kubernetes"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/pkg/util/version"
)
// CreateRBACRules creates the essential RBAC rules for a minimally set-up cluster
// TODO: This function and phase package is DEPRECATED.
// When the v1.9 cycle starts and deletePermissiveNodesBindingWhenUsingNodeAuthorization can be removed, this package will be removed with it.
func CreateRBACRules(client clientset.Interface, k8sVersion *version.Version) error {
if err := deletePermissiveNodesBindingWhenUsingNodeAuthorization(client, k8sVersion); err != nil {
return fmt.Errorf("failed to remove the permissive 'system:nodes' Group Subject in the 'system:node' ClusterRoleBinding: %v", err)
}
return nil
}
func deletePermissiveNodesBindingWhenUsingNodeAuthorization(client clientset.Interface, k8sVersion *version.Version) error {
// TODO: When the v1.9 cycle starts (targeting v1.9 at HEAD) and v1.8.0 is the minimum supported version, we can remove this function as the ClusterRoleBinding won't exist
// or already have no such permissive subject
nodesRoleBinding, err := client.RbacV1beta1().ClusterRoleBindings().Get(kubeadmconstants.NodesClusterRoleBinding, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
// Nothing to do; the RoleBinding doesn't exist
return nil
}
return err
}
newSubjects := []rbac.Subject{}
for _, subject := range nodesRoleBinding.Subjects {
// Skip the subject that binds to the system:nodes group
if subject.Name == kubeadmconstants.NodesGroup && subject.Kind == "Group" {
continue
}
newSubjects = append(newSubjects, subject)
}
nodesRoleBinding.Subjects = newSubjects
if _, err := client.RbacV1beta1().ClusterRoleBindings().Update(nodesRoleBinding); err != nil {
return err
}
return nil
}

Some files were not shown because too many files have changed in this diff Show more