Switch to github.com/golang/dep for vendoring

Signed-off-by: Mrunal Patel <mrunalp@gmail.com>
This commit is contained in:
Mrunal Patel 2017-01-31 16:45:59 -08:00
parent d6ab91be27
commit 8e5b17cf13
15431 changed files with 3971413 additions and 8881 deletions

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

@ -0,0 +1,39 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "kubeadm",
library = ":go_default_library",
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["kubeadm.go"],
tags = ["automanaged"],
deps = ["//cmd/kubeadm/app:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//cmd/kubeadm/app:all-srcs",
"//cmd/kubeadm/test:all-srcs",
],
tags = ["automanaged"],
)

13
vendor/k8s.io/kubernetes/cmd/kubeadm/OWNERS generated vendored Normal file
View file

@ -0,0 +1,13 @@
approvers:
- errordeveloper
- jbeda
- luxas
- mikedanese
reviewers:
- mikedanese
- luxas
- justinsb
- errordeveloper
- pires
- lukemarsden
- dgoodwin

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

@ -0,0 +1,45 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["kubeadm.go"],
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm/install:go_default_library",
"//cmd/kubeadm/app/cmd:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//vendor:github.com/spf13/pflag",
],
)
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/discovery:all-srcs",
"//cmd/kubeadm/app/images:all-srcs",
"//cmd/kubeadm/app/master:all-srcs",
"//cmd/kubeadm/app/node:all-srcs",
"//cmd/kubeadm/app/phases/certs:all-srcs",
"//cmd/kubeadm/app/phases/kubeconfig:all-srcs",
"//cmd/kubeadm/app/preflight:all-srcs",
"//cmd/kubeadm/app/util:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -0,0 +1,43 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"env.go",
"register.go",
"types.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-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,18 @@
/*
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.
*/
// +groupName=kubeadm.k8s.io
package kubeadm // import "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"

View file

@ -0,0 +1,58 @@
/*
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 (
"fmt"
"os"
"path"
"runtime"
"strings"
)
var GlobalEnvParams = SetEnvParams()
// TODO(phase1+) Move these paramaters to the API group
// we need some params for testing etc, let's keep these hidden for now
func SetEnvParams() *EnvParams {
envParams := map[string]string{
"kubernetes_dir": "/etc/kubernetes",
"host_pki_path": "/etc/kubernetes/pki",
"host_etcd_path": "/var/lib/etcd",
"hyperkube_image": "",
"repo_prefix": "gcr.io/google_containers",
"discovery_image": fmt.Sprintf("gcr.io/google_containers/kube-discovery-%s:%s", runtime.GOARCH, "1.0"),
"etcd_image": "",
}
for k := range envParams {
if v := strings.TrimSpace(os.Getenv(fmt.Sprintf("KUBE_%s", strings.ToUpper(k)))); v != "" {
envParams[k] = v
}
}
return &EnvParams{
KubernetesDir: path.Clean(envParams["kubernetes_dir"]),
HostPKIPath: path.Clean(envParams["host_pki_path"]),
HostEtcdPath: path.Clean(envParams["host_etcd_path"]),
HyperkubeImage: envParams["hyperkube_image"],
RepositoryPrefix: envParams["repo_prefix"],
DiscoveryImage: envParams["discovery_image"],
EtcdImage: envParams["etcd_image"],
}
}

View file

@ -0,0 +1,35 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"install.go",
],
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
"//pkg/apimachinery/announced: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,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,39 @@
/*
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"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/pkg/apimachinery/announced"
)
func init() {
if err := announced.NewGroupMetaFactory(
&announced.GroupMetaFactoryArgs{
GroupName: kubeadm.GroupName,
VersionPreferenceOrder: []string{v1alpha1.SchemeGroupVersion.Version},
ImportPrefix: "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm",
AddInternalObjectsToScheme: kubeadm.AddToScheme,
},
announced.VersionToSchemeFunc{
v1alpha1.SchemeGroupVersion.Version: v1alpha1.AddToScheme,
},
).Announce().RegisterAndEnable(); err != nil {
panic(err)
}
}

View file

@ -0,0 +1,61 @@
/*
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.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/api"
)
// 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{},
&ClusterInfo{},
&api.ListOptions{},
&api.DeleteOptions{},
&metav1.ExportOptions{},
)
return nil
}
func (obj *MasterConfiguration) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *NodeConfiguration) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *ClusterInfo) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }

View file

@ -0,0 +1,95 @@
/*
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"
)
type EnvParams struct {
KubernetesDir string
HostPKIPath string
HostEtcdPath string
HyperkubeImage string
RepositoryPrefix string
DiscoveryImage string
EtcdImage string
}
type MasterConfiguration struct {
metav1.TypeMeta
API API
Discovery Discovery
Etcd Etcd
Networking Networking
KubernetesVersion string
CloudProvider string
}
type API struct {
AdvertiseAddresses []string
ExternalDNSNames []string
Port int32
}
type Discovery struct {
HTTPS *HTTPSDiscovery
File *FileDiscovery
Token *TokenDiscovery
}
type HTTPSDiscovery struct {
URL string
}
type FileDiscovery struct {
Path string
}
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
}
type NodeConfiguration struct {
metav1.TypeMeta
Discovery Discovery
}
// ClusterInfo TODO add description
type ClusterInfo struct {
metav1.TypeMeta
// TODO(phase1+) this may become simply `api.Config`
CertificateAuthorities []string
Endpoints []string
}

View file

@ -0,0 +1,39 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
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.defaults.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api/v1:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
],
)
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,59 @@
/*
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 (
"k8s.io/apimachinery/pkg/runtime"
)
const (
DefaultServiceDNSDomain = "cluster.local"
DefaultServicesSubnet = "10.96.0.0/12"
DefaultKubernetesVersion = "stable"
DefaultKubernetesFallbackVersion = "v1.5.0"
DefaultAPIBindPort = 6443
DefaultDiscoveryBindPort = 9898
)
func addDefaultingFuncs(scheme *runtime.Scheme) error {
RegisterDefaults(scheme)
return scheme.AddDefaultingFuncs(
SetDefaults_MasterConfiguration,
)
}
func SetDefaults_MasterConfiguration(obj *MasterConfiguration) {
if obj.KubernetesVersion == "" {
obj.KubernetesVersion = DefaultKubernetesVersion
}
if obj.API.Port == 0 {
obj.API.Port = DefaultAPIBindPort
}
if obj.Networking.ServiceSubnet == "" {
obj.Networking.ServiceSubnet = DefaultServicesSubnet
}
if obj.Networking.DNSDomain == "" {
obj.Networking.DNSDomain = DefaultServiceDNSDomain
}
if obj.Discovery.Token == nil && obj.Discovery.File == nil && obj.Discovery.HTTPS == nil {
obj.Discovery.Token = &TokenDiscovery{}
}
}

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:defaulter-gen=TypeMeta
// +groupName=kubeadm.k8s.io
package v1alpha1 // import "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"

View file

@ -0,0 +1,61 @@
/*
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"
"k8s.io/kubernetes/pkg/api/v1"
)
// 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 (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addDefaultingFuncs)
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{},
&ClusterInfo{},
&v1.ListOptions{},
&v1.DeleteOptions{},
&metav1.ExportOptions{},
)
return nil
}
func (obj *MasterConfiguration) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *NodeConfiguration) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *ClusterInfo) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }

View file

@ -0,0 +1,85 @@
/*
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"
)
type MasterConfiguration struct {
metav1.TypeMeta `json:",inline"`
API API `json:"api"`
Discovery Discovery `json:"discovery"`
Etcd Etcd `json:"etcd"`
Networking Networking `json:"networking"`
KubernetesVersion string `json:"kubernetesVersion"`
CloudProvider string `json:"cloudProvider"`
}
type API struct {
AdvertiseAddresses []string `json:"advertiseAddresses"`
ExternalDNSNames []string `json:"externalDNSNames"`
Port int32 `json:"port"`
}
type Discovery struct {
HTTPS *HTTPSDiscovery `json:"https"`
File *FileDiscovery `json:"file"`
Token *TokenDiscovery `json:"token"`
}
type HTTPSDiscovery struct {
URL string `json:"url"`
}
type FileDiscovery struct {
Path string `json:"path"`
}
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"`
}
type NodeConfiguration struct {
metav1.TypeMeta `json:",inline"`
Discovery Discovery `json:"discovery"`
}
// ClusterInfo TODO add description
type ClusterInfo struct {
metav1.TypeMeta `json:",inline"`
// TODO(phase1+) this may become simply `api.Config`
CertificateAuthorities []string `json:"certificateAuthorities"`
Endpoints []string `json:"endpoints"`
}

View file

@ -0,0 +1,37 @@
// +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)) })
return nil
}
func SetObjectDefaults_MasterConfiguration(in *MasterConfiguration) {
SetDefaults_MasterConfiguration(in)
}

View file

@ -0,0 +1,31 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["validation.go"],
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
],
)
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,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 validation
import (
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func ValidateMasterConfiguration(c *kubeadm.MasterConfiguration) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateDiscovery(&c.Discovery, field.NewPath("discovery"))...)
return allErrs
}
func ValidateNodeConfiguration(c *kubeadm.NodeConfiguration) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateDiscovery(&c.Discovery, field.NewPath("discovery"))...)
return allErrs
}
func ValidateDiscovery(c *kubeadm.Discovery, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
var count int
if c.Token != nil {
allErrs = append(allErrs, ValidateTokenDiscovery(c.Token, fldPath)...)
count++
}
if c.File != nil {
allErrs = append(allErrs, ValidateFileDiscovery(c.File, fldPath)...)
count++
}
if c.HTTPS != nil {
allErrs = append(allErrs, ValidateHTTPSDiscovery(c.HTTPS, fldPath)...)
count++
}
if count != 1 {
allErrs = append(allErrs, field.Invalid(fldPath, nil, "exactly one discovery strategy can be provided"))
}
return allErrs
}
func ValidateFileDiscovery(c *kubeadm.FileDiscovery, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
return allErrs
}
func ValidateHTTPSDiscovery(c *kubeadm.HTTPSDiscovery, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
return allErrs
}
func ValidateTokenDiscovery(c *kubeadm.TokenDiscovery, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
return allErrs
}

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

@ -0,0 +1,74 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"cmd.go",
"init.go",
"join.go",
"reset.go",
"token.go",
"version.go",
],
tags = ["automanaged"],
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/flags:go_default_library",
"//cmd/kubeadm/app/discovery:go_default_library",
"//cmd/kubeadm/app/master:go_default_library",
"//cmd/kubeadm/app/node:go_default_library",
"//cmd/kubeadm/app/phases/certs:go_default_library",
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/util/flag:go_default_library",
"//pkg/util/initsystem:go_default_library",
"//pkg/version:go_default_library",
"//vendor:github.com/renstrom/dedent",
"//vendor:github.com/spf13/cobra",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/net",
],
)
go_test(
name = "go_default_test",
srcs = [
"reset_test.go",
"token_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = ["//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/flags:all-srcs",
],
tags = ["automanaged"],
)

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

@ -0,0 +1,94 @@
/*
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"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/util/flag"
)
func NewKubeadmCommand(f cmdutil.Factory, in 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 ALPHA, 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
Example usage:
Create a two-machine cluster with one master (which controls the cluster),
and one node (where your workloads, like Pods and ReplicaSets run).
On the first machine
master# kubeadm init
On the second machine
node# kubeadm join --token=<token> <ip-of-master>
You can then repeat the second step on as many other machines as you like.
`),
}
// TODO(phase2+) figure out how to avoid running as root
//
// TODO(phase2) detect interactive vs non-interactive use and adjust output accordingly
// i.e. make it automation friendly
//
// TODO(phase2) create an abstraction that defines files and the content that needs to
// be written to disc and write it all in one go at the end as we have a lot of
// crappy little files written from different parts of this code; this could also
// be useful for testing by having this model we can allow users to create some files before
// `kubeadm init` runs, e.g. PKI assets, we would then be able to look at files users has
// given an diff or validate if those are sane, we could also warn if any of the files had been deprecated
cmds.ResetFlags()
cmds.SetGlobalNormalizationFunc(flag.WarnWordSepNormalizeFunc)
cmds.AddCommand(NewCmdInit(out))
cmds.AddCommand(NewCmdJoin(out))
cmds.AddCommand(NewCmdReset(out))
cmds.AddCommand(NewCmdVersion(out))
// Wrap not yet usable/supported commands in experimental sub-command:
experimentalCmd := &cobra.Command{
Use: "ex",
Short: "Experimental sub-commands not yet fully functional.",
}
experimentalCmd.AddCommand(NewCmdToken(out, err))
cmds.AddCommand(experimentalCmd)
return cmds
}

View file

@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["cloudprovider.go"],
tags = ["automanaged"],
deps = ["//vendor:github.com/spf13/pflag"],
)
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,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 flags
import (
"fmt"
"github.com/spf13/pflag"
)
var cloudproviders = []string{
"aws",
"azure",
"cloudstack",
"gce",
"mesos",
"openstack",
"ovirt",
"photon",
"rackspace",
"vsphere",
}
func NewCloudProviderFlag(provider *string) pflag.Value {
return &cloudProviderValue{provider: provider}
}
type cloudProviderValue struct {
provider *string
}
func (c *cloudProviderValue) String() string {
return *c.provider
}
func (c *cloudProviderValue) Set(s string) error {
if ValidateCloudProvider(s) {
*c.provider = s
return nil
}
return fmt.Errorf("cloud provider %q is not supported, you can use any of %v", s, cloudproviders)
}
func (c *cloudProviderValue) Type() string {
return "cloudprovider"
}
func ValidateCloudProvider(provider string) bool {
for _, supported := range cloudproviders {
if provider == supported {
return true
}
}
return false
}

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

@ -0,0 +1,276 @@
/*
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"
"path"
"strconv"
"github.com/renstrom/dedent"
"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"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/flags"
"k8s.io/kubernetes/cmd/kubeadm/app/discovery"
kubemaster "k8s.io/kubernetes/cmd/kubeadm/app/master"
certphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
"k8s.io/apimachinery/pkg/runtime"
netutil "k8s.io/apimachinery/pkg/util/net"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/pkg/api"
)
var (
initDoneMsgf = dedent.Dedent(`
Your Kubernetes master has initialized successfully!
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:
kubeadm join --discovery %s
`)
)
// NewCmdInit returns "kubeadm init" command.
func NewCmdInit(out io.Writer) *cobra.Command {
versioned := &kubeadmapiext.MasterConfiguration{}
api.Scheme.Default(versioned)
cfg := kubeadmapi.MasterConfiguration{}
api.Scheme.Convert(versioned, &cfg, nil)
var cfgPath string
var skipPreFlight bool
cmd := &cobra.Command{
Use: "init",
Short: "Run this in order to set up the Kubernetes master",
Run: func(cmd *cobra.Command, args []string) {
i, err := NewInit(cfgPath, &cfg, skipPreFlight)
kubeadmutil.CheckErr(err)
kubeadmutil.CheckErr(i.Validate())
kubeadmutil.CheckErr(i.Run(out))
},
}
cmd.PersistentFlags().StringSliceVar(
&cfg.API.AdvertiseAddresses, "api-advertise-addresses", cfg.API.AdvertiseAddresses,
"The IP addresses to advertise, in case autodetection fails",
)
cmd.PersistentFlags().Int32Var(
&cfg.API.Port, "api-port", cfg.API.Port,
"Port for API to bind to",
)
cmd.PersistentFlags().StringSliceVar(
&cfg.API.ExternalDNSNames, "api-external-dns-names", cfg.API.ExternalDNSNames,
"The DNS names to advertise, in case you have configured them yourself",
)
cmd.PersistentFlags().StringVar(
&cfg.Networking.ServiceSubnet, "service-cidr", cfg.Networking.ServiceSubnet,
"Use alternative range of IP address for service VIPs",
)
cmd.PersistentFlags().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",
)
cmd.PersistentFlags().StringVar(
&cfg.Networking.DNSDomain, "service-dns-domain", cfg.Networking.DNSDomain,
`Use alternative domain for services, e.g. "myorg.internal"`,
)
cmd.PersistentFlags().Var(
flags.NewCloudProviderFlag(&cfg.CloudProvider), "cloud-provider",
`Enable cloud provider features (external load-balancers, storage, etc). Note that you have to configure all kubelets manually`,
)
cmd.PersistentFlags().StringVar(
&cfg.KubernetesVersion, "use-kubernetes-version", cfg.KubernetesVersion,
`Choose a specific Kubernetes version for the control plane`,
)
cmd.PersistentFlags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file")
cmd.PersistentFlags().BoolVar(
&skipPreFlight, "skip-preflight-checks", skipPreFlight,
"skip preflight checks normally run before modifying the system",
)
cmd.PersistentFlags().Var(
discovery.NewDiscoveryValue(&cfg.Discovery), "discovery",
"The discovery method kubeadm will use for connecting nodes to the master",
)
return cmd
}
type Init struct {
cfg *kubeadmapi.MasterConfiguration
}
func NewInit(cfgPath string, cfg *kubeadmapi.MasterConfiguration, skipPreFlight bool) (*Init, error) {
fmt.Println("[kubeadm] WARNING: kubeadm is in alpha, 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)
}
}
// Auto-detect the IP
if len(cfg.API.AdvertiseAddresses) == 0 {
ip, err := netutil.ChooseHostInterface()
if err != nil {
return nil, err
}
cfg.API.AdvertiseAddresses = []string{ip.String()}
}
if !skipPreFlight {
fmt.Println("[preflight] Running pre-flight checks")
// First, check if we're root separately from the other preflight checks and fail fast
if err := preflight.RunRootCheckOnly(); err != nil {
return nil, err
}
// Then continue with the others...
if err := preflight.RunInitMasterChecks(cfg); err != nil {
return nil, err
}
} else {
fmt.Println("[preflight] Skipping pre-flight checks")
}
// Try to start the kubelet service in case it's inactive
preflight.TryStartKubelet()
// validate version argument
ver, err := kubeadmutil.KubernetesReleaseVersion(cfg.KubernetesVersion)
if err != nil {
if cfg.KubernetesVersion != kubeadmapiext.DefaultKubernetesVersion {
return nil, err
} else {
ver = kubeadmapiext.DefaultKubernetesFallbackVersion
}
}
cfg.KubernetesVersion = ver
fmt.Println("[init] Using Kubernetes version:", ver)
// Warn about the limitations with the current cloudprovider solution.
if cfg.CloudProvider != "" {
fmt.Println("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)")
}
return &Init{cfg: cfg}, nil
}
func (i *Init) Validate() error {
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 {
// PHASE 1: Generate certificates
caCert, err := certphase.CreatePKIAssets(i.cfg, kubeadmapi.GlobalEnvParams.HostPKIPath)
if err != nil {
return err
}
// Exception:
if i.cfg.Discovery.Token != nil {
// Validate token
if valid, err := kubeadmutil.ValidateToken(i.cfg.Discovery.Token); valid == false {
return err
}
// Make sure there is at least one address
if len(i.cfg.Discovery.Token.Addresses) == 0 {
ip, err := netutil.ChooseHostInterface()
if err != nil {
return err
}
i.cfg.Discovery.Token.Addresses = []string{ip.String() + ":" + strconv.Itoa(kubeadmapiext.DefaultDiscoveryBindPort)}
}
if err := kubemaster.CreateTokenAuthFile(kubeadmutil.BearerToken(i.cfg.Discovery.Token)); err != nil {
return err
}
}
// PHASE 2: Generate kubeconfig files for the admin and the kubelet
// TODO this is not great, but there is only one address we can use here
// so we'll pick the first one, there is much of chance to have an empty
// slice by the time this gets called
masterEndpoint := fmt.Sprintf("https://%s:%d", i.cfg.API.AdvertiseAddresses[0], i.cfg.API.Port)
err = kubeconfigphase.CreateAdminAndKubeletKubeConfig(masterEndpoint, kubeadmapi.GlobalEnvParams.HostPKIPath, kubeadmapi.GlobalEnvParams.KubernetesDir)
if err != nil {
return err
}
// Phase 3: Bootstrap the control plane
if err := kubemaster.WriteStaticPodManifests(i.cfg); err != nil {
return err
}
client, err := kubemaster.CreateClientAndWaitForAPI(path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, kubeconfigphase.AdminKubeConfigFileName))
if err != nil {
return err
}
if err := kubemaster.UpdateMasterRoleLabelsAndTaints(client, false); err != nil {
return err
}
if i.cfg.Discovery.Token != nil {
fmt.Printf("[token-discovery] Using token: %s\n", kubeadmutil.BearerToken(i.cfg.Discovery.Token))
if err := kubemaster.CreateDiscoveryDeploymentAndSecret(i.cfg, client, caCert); err != nil {
return err
}
if err := kubeadmutil.UpdateOrCreateToken(client, i.cfg.Discovery.Token, kubeadmutil.DefaultTokenDuration); err != nil {
return err
}
}
if err := kubemaster.CreateEssentialAddons(i.cfg, client); err != nil {
return err
}
fmt.Fprintf(out, initDoneMsgf, generateJoinArgs(i.cfg))
return nil
}
// generateJoinArgs generates kubeadm join arguments
func generateJoinArgs(cfg *kubeadmapi.MasterConfiguration) string {
return discovery.NewDiscoveryValue(&cfg.Discovery).String()
}

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

@ -0,0 +1,145 @@
/*
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"
"path"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
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"
"k8s.io/kubernetes/cmd/kubeadm/app/discovery"
kubenode "k8s.io/kubernetes/cmd/kubeadm/app/node"
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/pkg/api"
)
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 {
versioned := &kubeadmapiext.NodeConfiguration{}
api.Scheme.Default(versioned)
cfg := kubeadmapi.NodeConfiguration{}
api.Scheme.Convert(versioned, &cfg, nil)
var skipPreFlight bool
var cfgPath string
cmd := &cobra.Command{
Use: "join <master address>",
Short: "Run this on any machine you wish to join an existing cluster",
Run: func(cmd *cobra.Command, args []string) {
j, err := NewJoin(cfgPath, args, &cfg, skipPreFlight)
kubeadmutil.CheckErr(err)
kubeadmutil.CheckErr(j.Validate())
kubeadmutil.CheckErr(j.Run(out))
},
}
cmd.PersistentFlags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file")
cmd.PersistentFlags().BoolVar(
&skipPreFlight, "skip-preflight-checks", false,
"skip preflight checks normally run before modifying the system",
)
cmd.PersistentFlags().Var(
discovery.NewDiscoveryValue(&cfg.Discovery), "discovery",
"The discovery method kubeadm will use for connecting nodes to the master",
)
return cmd
}
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 alpha, 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)
}
}
if !skipPreFlight {
fmt.Println("[preflight] Running pre-flight checks")
// First, check if we're root separately from the other preflight checks and fail fast
if err := preflight.RunRootCheckOnly(); err != nil {
return nil, err
}
// Then continue with the others...
if err := preflight.RunJoinNodeChecks(cfg); err != nil {
return nil, err
}
} else {
fmt.Println("[preflight] Skipping pre-flight checks")
}
// Try to start the kubelet service in case it's inactive
preflight.TryStartKubelet()
return &Join{cfg: cfg}, nil
}
func (j *Join) Validate() error {
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.Discovery)
if err != nil {
return err
}
if err := kubenode.PerformTLSBootstrap(cfg); err != nil {
return err
}
if err := kubeconfigphase.WriteKubeconfigToDisk(path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, kubeconfigphase.KubeletKubeConfigFileName), cfg); err != nil {
return err
}
fmt.Fprintf(out, joinDoneMsgf)
return nil
}

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

@ -0,0 +1,232 @@
/*
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"
"strings"
"github.com/spf13/cobra"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"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, removeNode bool
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, removeNode)
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().BoolVar(
&removeNode, "remove-node", true,
"Remove this node from the pool of nodes in this cluster",
)
return cmd
}
type Reset struct {
removeNode bool
}
func NewReset(skipPreFlight, removeNode bool) (*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{
removeNode: removeNode,
}, 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 drain and remove the node from the cluster
err := drainAndRemoveNode(r.removeNode)
if err != nil {
fmt.Printf("[reset] Failed to cleanup node: [%v]\n", err)
}
// 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 := "cat /proc/mounts | awk '{print $2}' | grep '/var/lib/kubelet' | 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"}
if warnings, errors := dockerCheck.Check(); len(warnings) == 0 && len(errors) == 0 {
fmt.Println("[reset] Removing kubernetes-managed containers")
if err := exec.Command("sh", "-c", "docker ps | grep 'k8s_' | awk '{print $1}' | 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"}
// 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 := path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "manifests/etcd.json")
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(kubeadmapi.GlobalEnvParams.KubernetesDir, kubeadmapi.GlobalEnvParams.HostPKIPath)
return nil
}
func drainAndRemoveNode(removeNode bool) error {
hostname, err := os.Hostname()
if err != nil {
return fmt.Errorf("failed to detect node hostname")
}
hostname = strings.ToLower(hostname)
// TODO: Use the "native" k8s client for this once we're confident the versioned is working
kubeConfigPath := path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "kubelet.conf")
getNodesCmd := fmt.Sprintf("kubectl --kubeconfig %s get nodes | grep %s", kubeConfigPath, hostname)
output, err := exec.Command("sh", "-c", getNodesCmd).Output()
if err != nil || len(output) == 0 {
// kubeadm shouldn't drain and/or remove the node when it doesn't exist anymore
return nil
}
fmt.Printf("[reset] Draining node: %q\n", hostname)
output, err = exec.Command("kubectl", "--kubeconfig", kubeConfigPath, "drain", hostname, "--delete-local-data", "--force", "--ignore-daemonsets").Output()
if err != nil {
return fmt.Errorf("failed to drain node %q [%s]", hostname, output)
}
if removeNode {
fmt.Printf("[reset] Removing node: %q\n", hostname)
output, err = exec.Command("kubectl", "--kubeconfig", kubeConfigPath, "delete", "node", hostname).Output()
if err != nil {
return fmt.Errorf("failed to remove node %q [%s]", hostname, output)
}
}
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 {
err = os.RemoveAll(path.Join(filepath, name))
if 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{
path.Join(configPathDir, "manifests"),
pkiPathDir,
}
fmt.Printf("[reset] Deleting contents of config directories: %v\n", dirsToClean)
for _, dir := range dirsToClean {
err := cleanDir(dir)
if err != nil {
fmt.Printf("[reset] Failed to remove directory: %q [%v]\n", dir, err)
}
}
filesToClean := []string{
path.Join(configPathDir, "admin.conf"),
path.Join(configPathDir, "kubelet.conf"),
}
fmt.Printf("[reset] Deleting files: %v\n", filesToClean)
for _, path := range filesToClean {
err := os.RemoveAll(path)
if err != nil {
fmt.Printf("[reset] Failed to remove file: %q [%v]\n", path, err)
}
}
}

View file

@ -0,0 +1,182 @@
/*
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"
"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.json",
"manifests/kube-apiserver.json",
"pki/ca.pem",
"admin.conf",
"kubelet.conf",
},
verifyExists: []string{
"manifests",
"pki",
},
},
"partial reset": {
setupDirs: []string{
"pki",
},
setupFiles: []string{
"pki/ca.pem",
"kubelet.conf",
},
verifyExists: []string{
"pki",
},
verifyNotExists: []string{
"manifests",
},
},
"preserve cloud-config": {
setupDirs: []string{
"manifests",
"pki",
},
setupFiles: []string{
"manifests/etcd.json",
"manifests/kube-apiserver.json",
"pki/ca.pem",
"admin.conf",
"kubelet.conf",
"cloud-config",
},
verifyExists: []string{
"manifests",
"pki",
"cloud-config",
},
},
"preserve hidden files and directories": {
setupDirs: []string{
"manifests",
"pki",
".mydir",
},
setupFiles: []string{
"manifests/etcd.json",
"manifests/kube-apiserver.json",
"pki/ca.pem",
"admin.conf",
"kubelet.conf",
".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)
defer f.Close()
if err != nil {
t.Errorf("Unable to create test file: %s", err)
}
}
resetConfigDir(tmpDir, filepath.Join(tmpDir, "pki"))
// Verify the files we cleanup implicitly in every test:
assertExists(t, tmpDir)
assertNotExists(t, filepath.Join(tmpDir, "admin.conf"))
assertNotExists(t, filepath.Join(tmpDir, "kubelet.conf"))
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))
}
}
}

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

@ -0,0 +1,230 @@
/*
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 (
"errors"
"fmt"
"io"
"path"
"text/tabwriter"
"time"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubemaster "k8s.io/kubernetes/cmd/kubeadm/app/master"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/kubectl"
)
func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
tokenCmd := &cobra.Command{
Use: "token",
Short: "Manage bootstrap tokens.",
// 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: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("missing subcommand; 'token' is not meant to be run on its own")
} else {
return fmt.Errorf("invalid subcommand: %s", args[0])
}
},
}
var token string
var tokenDuration time.Duration
createCmd := &cobra.Command{
Use: "create",
Short: "Create bootstrap tokens on the server.",
Run: func(tokenCmd *cobra.Command, args []string) {
err := RunCreateToken(out, tokenCmd, tokenDuration, token)
kubeadmutil.CheckErr(err)
},
}
createCmd.PersistentFlags().DurationVar(&tokenDuration,
"ttl", kubeadmutil.DefaultTokenDuration, "The duration before the token is automatically deleted.")
createCmd.PersistentFlags().StringVar(
&token, "token", "",
"Shared secret used to secure cluster bootstrap. If none is provided, one will be generated for you.",
)
tokenCmd.AddCommand(createCmd)
tokenCmd.AddCommand(NewCmdTokenGenerate(out))
listCmd := &cobra.Command{
Use: "list",
Short: "List bootstrap tokens on the server.",
Run: func(tokenCmd *cobra.Command, args []string) {
err := RunListTokens(out, errW, tokenCmd)
kubeadmutil.CheckErr(err)
},
}
tokenCmd.AddCommand(listCmd)
deleteCmd := &cobra.Command{
Use: "delete",
Short: "Delete bootstrap tokens on the server.",
Run: func(tokenCmd *cobra.Command, args []string) {
err := RunDeleteToken(out, tokenCmd, 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 "<6 characters>:<16 characters>". 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, cmd *cobra.Command, tokenDuration time.Duration, token string) error {
client, err := kubemaster.CreateClientFromFile(path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "admin.conf"))
if err != nil {
return err
}
parsedID, parsedSecret, err := kubeadmutil.ParseToken(token)
if err != nil {
return err
}
td := &kubeadmapi.TokenDiscovery{ID: parsedID, Secret: parsedSecret}
err = kubeadmutil.UpdateOrCreateToken(client, td, tokenDuration)
if err != nil {
return err
}
fmt.Fprintln(out, kubeadmutil.BearerToken(td))
return nil
}
func RunGenerateToken(out io.Writer) error {
td := &kubeadmapi.TokenDiscovery{}
err := kubeadmutil.GenerateToken(td)
if err != nil {
return err
}
fmt.Fprintln(out, kubeadmutil.BearerToken(td))
return nil
}
// RunListTokens lists details on all existing bootstrap tokens on the server.
func RunListTokens(out io.Writer, errW io.Writer, cmd *cobra.Command) error {
client, err := kubemaster.CreateClientFromFile(path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "admin.conf"))
if err != nil {
return err
}
tokenSelector := fields.SelectorFromSet(
map[string]string{
api.SecretTypeField: string(api.SecretTypeBootstrapToken),
},
)
listOptions := v1.ListOptions{
FieldSelector: tokenSelector.String(),
}
results, err := client.Secrets(api.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, "ID\tTOKEN\tTTL")
for _, secret := range results.Items {
tokenId, ok := secret.Data["token-id"]
if !ok {
fmt.Fprintf(errW, "[token] bootstrap token has no token-id data: %s\n", secret.Name)
continue
}
tokenSecret, ok := secret.Data["token-secret"]
if !ok {
fmt.Fprintf(errW, "[token] bootstrap token has no token-secret data: %s\n", secret.Name)
continue
}
token := fmt.Sprintf("%s.%s", tokenId, tokenSecret)
// Expiration time is optional, if not specified this implies the token
// never expires.
expires := "<never>"
secretExpiration, ok := secret.Data["expiration"]
if ok {
expireTime, err := time.Parse(time.RFC3339, string(secretExpiration))
if err != nil {
return fmt.Errorf("error parsing expiry time [%v]", err)
}
expires = kubectl.ShortHumanDuration(expireTime.Sub(time.Now()))
}
fmt.Fprintf(w, "%s\t%s\t%s\n", tokenId, token, expires)
}
w.Flush()
return nil
}
// RunDeleteToken removes a bootstrap token from the server.
func RunDeleteToken(out io.Writer, cmd *cobra.Command, tokenId string) error {
if err := kubeadmutil.ParseTokenID(tokenId); err != nil {
return err
}
client, err := kubemaster.CreateClientFromFile(path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "admin.conf"))
if err != nil {
return err
}
tokenSecretName := fmt.Sprintf("%s%s", kubeadmutil.BootstrapTokenSecretPrefix, tokenId)
if err := client.Secrets(api.NamespaceSystem).Delete(tokenSecretName, nil); err != nil {
return fmt.Errorf("failed to delete bootstrap token [%v]", err)
}
fmt.Fprintf(out, "[token] bootstrap token deleted: %s\n", tokenId)
return nil
}

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,44 @@
/*
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/spf13/cobra"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/pkg/version"
)
func NewCmdVersion(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "version",
Short: "Print the version of kubeadm",
Run: func(cmd *cobra.Command, args []string) {
err := RunVersion(out, cmd)
kubeadmutil.CheckErr(err)
},
}
return cmd
}
func RunVersion(out io.Writer, cmd *cobra.Command) error {
fmt.Fprintf(out, "kubeadm version: %#v\n", version.Get())
return nil
}

View file

@ -0,0 +1,58 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"discovery.go",
"flags.go",
],
tags = ["automanaged"],
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/node:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
"//pkg/client/unversioned/clientcmd:go_default_library",
"//pkg/client/unversioned/clientcmd/api:go_default_library",
"//vendor:github.com/spf13/pflag",
],
)
go_test(
name = "go_default_test",
srcs = ["flags_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//vendor:github.com/davecgh/go-spew/spew",
],
)
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,82 @@
/*
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"
"io/ioutil"
"net/http"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubenode "k8s.io/kubernetes/cmd/kubeadm/app/node"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
)
// For identifies and executes the desired discovery mechanism.
func For(d kubeadmapi.Discovery) (*clientcmdapi.Config, error) {
switch {
case d.File != nil:
return runFileDiscovery(d.File)
case d.HTTPS != nil:
return runHTTPSDiscovery(d.HTTPS)
case d.Token != nil:
return runTokenDiscovery(d.Token)
default:
return nil, fmt.Errorf("couldn't find a valid discovery configuration.")
}
}
// runFileDiscovery executes file-based discovery.
func runFileDiscovery(fd *kubeadmapi.FileDiscovery) (*clientcmdapi.Config, error) {
return clientcmd.LoadFromFile(fd.Path)
}
// runHTTPSDiscovery executes HTTPS-based discovery.
func runHTTPSDiscovery(hd *kubeadmapi.HTTPSDiscovery) (*clientcmdapi.Config, error) {
response, err := http.Get(hd.URL)
if err != nil {
return nil, err
}
defer response.Body.Close()
kubeconfig, err := ioutil.ReadAll(response.Body)
if err != nil {
return nil, err
}
return clientcmd.Load(kubeconfig)
}
// runTokenDiscovery executes token-based discovery.
func runTokenDiscovery(td *kubeadmapi.TokenDiscovery) (*clientcmdapi.Config, error) {
if valid, err := kubeadmutil.ValidateToken(td); valid == false {
return nil, err
}
clusterInfo, err := kubenode.RetrieveTrustedClusterInfo(td)
if err != nil {
return nil, err
}
cfg, err := kubenode.EstablishMasterConnection(td, clusterInfo)
if err != nil {
return nil, err
}
return cfg, nil
}

View file

@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["file.go"],
tags = ["automanaged"],
deps = ["//cmd/kubeadm/app/apis/kubeadm:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,30 @@
/*
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 (
"net/url"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func Parse(u *url.URL, c *kubeadm.Discovery) error {
c.File = &kubeadm.FileDiscovery{
Path: u.Path,
}
return nil
}

View file

@ -0,0 +1,92 @@
/*
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"
"strings"
"github.com/spf13/pflag"
"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"
)
type discoveryValue struct {
v *kubeadm.Discovery
}
func NewDiscoveryValue(d *kubeadm.Discovery) pflag.Value {
return &discoveryValue{
v: d,
}
}
func (d *discoveryValue) String() string {
switch {
case d.v.HTTPS != nil:
return d.v.HTTPS.URL
case d.v.File != nil:
return "file://" + d.v.File.Path
case d.v.Token != nil:
return fmt.Sprintf("token://%s:%s@%s", d.v.Token.ID, d.v.Token.Secret, strings.Join(d.v.Token.Addresses, ","))
default:
return "unknown"
}
}
func (d *discoveryValue) Set(s string) error {
var kd kubeadm.Discovery
if err := ParseURL(&kd, s); err != nil {
return err
}
*d.v = kd
return nil
}
func (d *discoveryValue) Type() string {
return "discovery"
}
func ParseURL(d *kubeadm.Discovery, s string) error {
u, err := url.Parse(s)
if err != nil {
return err
}
switch u.Scheme {
case "https":
return https.Parse(u, d)
case "file":
return file.Parse(u, d)
case "token":
// Make sure a valid RFC 3986 URL has been passed and parsed.
// See https://github.com/kubernetes/kubeadm/issues/95#issuecomment-270431296 for more details.
if !strings.Contains(s, "@") {
s := s + "@"
u, err = url.Parse(s)
if err != nil {
return err
}
}
return token.Parse(u, d)
default:
return fmt.Errorf("unknown discovery scheme")
}
}

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 discovery
import (
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func TestParseURL(t *testing.T) {
cases := []struct {
url string
expect kubeadm.Discovery
expectErr bool
}{
{
url: "token://",
expect: kubeadm.Discovery{
Token: &kubeadm.TokenDiscovery{},
},
},
{
url: "token://c05de9:ab224260fb3cd718",
expect: kubeadm.Discovery{
Token: &kubeadm.TokenDiscovery{
ID: "c05de9",
Secret: "ab224260fb3cd718",
},
},
},
{
url: "token://c05de9:ab224260fb3cd718@",
expect: kubeadm.Discovery{
Token: &kubeadm.TokenDiscovery{
ID: "c05de9",
Secret: "ab224260fb3cd718",
},
},
},
{
url: "token://c05de9:ab224260fb3cd718@192.168.0.1:6555,191.168.0.2:6443",
expect: kubeadm.Discovery{
Token: &kubeadm.TokenDiscovery{
ID: "c05de9",
Secret: "ab224260fb3cd718",
Addresses: []string{
"192.168.0.1:6555",
"191.168.0.2:6443",
},
},
},
},
{
url: "file:///foo/bar/baz",
expect: kubeadm.Discovery{
File: &kubeadm.FileDiscovery{
Path: "/foo/bar/baz",
},
},
},
{
url: "https://storage.googleapis.com/kubeadm-disco/clusters/217651295213",
expect: kubeadm.Discovery{
HTTPS: &kubeadm.HTTPSDiscovery{
URL: "https://storage.googleapis.com/kubeadm-disco/clusters/217651295213",
},
},
},
}
for _, c := range cases {
var d kubeadm.Discovery
if err := ParseURL(&d, c.url); err != nil {
if !c.expectErr {
t.Errorf("unexpected error parsing discovery url: %v", err)
}
continue
}
if !reflect.DeepEqual(d, c.expect) {
t.Errorf("expected discovery config to be equal but got:\n\tactual: %s\n\texpected: %s", spew.Sdump(d), spew.Sdump(c.expect))
}
}
}

View file

@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["https.go"],
tags = ["automanaged"],
deps = ["//cmd/kubeadm/app/apis/kubeadm:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,30 @@
/*
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 (
"net/url"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func Parse(u *url.URL, c *kubeadm.Discovery) error {
c.HTTPS = &kubeadm.HTTPSDiscovery{
URL: u.String(),
}
return nil
}

View file

@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["token.go"],
tags = ["automanaged"],
deps = ["//cmd/kubeadm/app/apis/kubeadm:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

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 token
import (
"net/url"
"strings"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func Parse(u *url.URL, c *kubeadm.Discovery) error {
var (
hosts []string
tokenID, token string
)
if u.Host != "" {
hosts = strings.Split(u.Host, ",")
}
if u.User != nil {
if p, ok := u.User.Password(); ok {
tokenID = u.User.Username()
token = p
}
}
c.Token = &kubeadm.TokenDiscovery{
ID: tokenID,
Secret: token,
Addresses: hosts,
}
return nil
}

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

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

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 images
import (
"fmt"
"runtime"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
const (
KubeEtcdImage = "etcd"
KubeAPIServerImage = "apiserver"
KubeControllerManagerImage = "controller-manager"
KubeSchedulerImage = "scheduler"
KubeProxyImage = "proxy"
KubeDNSImage = "k8s-dns-kube-dns"
KubeDNSmasqImage = "k8s-dns-dnsmasq"
KubeDNSSidecarImage = "k8s-dns-sidecar"
Pause = "pause"
gcrPrefix = "gcr.io/google_containers"
etcdVersion = "3.0.14-kubeadm"
kubeDNSVersion = "1.10.1"
dnsmasqVersion = "1.10.1"
kubeDNSSidecarVersion = "1.10.1"
pauseVersion = "3.0"
)
func GetCoreImage(image string, cfg *kubeadmapi.MasterConfiguration, overrideImage string) string {
if overrideImage != "" {
return overrideImage
}
repoPrefix := kubeadmapi.GlobalEnvParams.RepositoryPrefix
return map[string]string{
KubeEtcdImage: fmt.Sprintf("%s/%s-%s:%s", repoPrefix, "etcd", runtime.GOARCH, etcdVersion),
KubeAPIServerImage: fmt.Sprintf("%s/%s-%s:%s", repoPrefix, "kube-apiserver", runtime.GOARCH, cfg.KubernetesVersion),
KubeControllerManagerImage: fmt.Sprintf("%s/%s-%s:%s", repoPrefix, "kube-controller-manager", runtime.GOARCH, cfg.KubernetesVersion),
KubeSchedulerImage: fmt.Sprintf("%s/%s-%s:%s", repoPrefix, "kube-scheduler", runtime.GOARCH, cfg.KubernetesVersion),
KubeProxyImage: fmt.Sprintf("%s/%s-%s:%s", repoPrefix, "kube-proxy", runtime.GOARCH, cfg.KubernetesVersion),
}[image]
}
func GetAddonImage(image string) string {
repoPrefix := kubeadmapi.GlobalEnvParams.RepositoryPrefix
return map[string]string{
KubeDNSImage: fmt.Sprintf("%s/%s-%s:%s", repoPrefix, KubeDNSImage, runtime.GOARCH, kubeDNSVersion),
KubeDNSmasqImage: fmt.Sprintf("%s/%s-%s:%s", repoPrefix, KubeDNSmasqImage, runtime.GOARCH, dnsmasqVersion),
KubeDNSSidecarImage: fmt.Sprintf("%s/%s-%s:%s", repoPrefix, KubeDNSSidecarImage, runtime.GOARCH, kubeDNSSidecarVersion),
Pause: fmt.Sprintf("%s/%s-%s:%s", repoPrefix, Pause, runtime.GOARCH, pauseVersion),
}[image]
}

View file

@ -0,0 +1,112 @@
/*
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"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
type getCoreImageTest struct {
i string
c *kubeadmapi.MasterConfiguration
o string
}
const testversion = "1"
func TestGetCoreImage(t *testing.T) {
var tokenTest = []struct {
t getCoreImageTest
expected string
}{
{getCoreImageTest{o: "override"}, "override"},
{getCoreImageTest{
i: KubeEtcdImage,
c: &kubeadmapi.MasterConfiguration{}},
fmt.Sprintf("%s/%s-%s:%s", gcrPrefix, "etcd", runtime.GOARCH, etcdVersion),
},
{getCoreImageTest{
i: KubeAPIServerImage,
c: &kubeadmapi.MasterConfiguration{KubernetesVersion: testversion}},
fmt.Sprintf("%s/%s-%s:%s", gcrPrefix, "kube-apiserver", runtime.GOARCH, testversion),
},
{getCoreImageTest{
i: KubeControllerManagerImage,
c: &kubeadmapi.MasterConfiguration{KubernetesVersion: testversion}},
fmt.Sprintf("%s/%s-%s:%s", gcrPrefix, "kube-controller-manager", runtime.GOARCH, testversion),
},
{getCoreImageTest{
i: KubeSchedulerImage,
c: &kubeadmapi.MasterConfiguration{KubernetesVersion: testversion}},
fmt.Sprintf("%s/%s-%s:%s", gcrPrefix, "kube-scheduler", runtime.GOARCH, testversion),
},
{getCoreImageTest{
i: KubeProxyImage,
c: &kubeadmapi.MasterConfiguration{KubernetesVersion: testversion}},
fmt.Sprintf("%s/%s-%s:%s", gcrPrefix, "kube-proxy", runtime.GOARCH, testversion),
},
}
for _, rt := range tokenTest {
actual := GetCoreImage(rt.t.i, rt.t.c, rt.t.o)
if actual != rt.expected {
t.Errorf(
"failed GetCoreImage:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual,
)
}
}
}
func TestGetAddonImage(t *testing.T) {
var tokenTest = []struct {
t string
expected string
}{
{"matches nothing", ""},
{
KubeDNSImage,
fmt.Sprintf("%s/%s-%s:%s", gcrPrefix, KubeDNSImage, runtime.GOARCH, kubeDNSVersion),
},
{
KubeDNSmasqImage,
fmt.Sprintf("%s/%s-%s:%s", gcrPrefix, KubeDNSmasqImage, runtime.GOARCH, dnsmasqVersion),
},
{
KubeDNSSidecarImage,
fmt.Sprintf("%s/%s-%s:%s", gcrPrefix, KubeDNSSidecarImage, runtime.GOARCH, kubeDNSSidecarVersion),
},
{
Pause,
fmt.Sprintf("%s/%s-%s:%s", gcrPrefix, Pause, runtime.GOARCH, pauseVersion),
},
}
for _, rt := range tokenTest {
actual := GetAddonImage(rt.t)
if actual != rt.expected {
t.Errorf(
"failed GetCoreImage:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual,
)
}
}
}

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

@ -0,0 +1,36 @@
/*
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"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
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(cmdutil.NewFactory(nil), os.Stdin, os.Stdout, os.Stderr)
return cmd.Execute()
}

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

@ -0,0 +1,73 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"addons.go",
"apiclient.go",
"discovery.go",
"manifests.go",
"tokens.go",
],
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
"//cmd/kubeadm/app/images:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/resource:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/apis/extensions/v1beta1:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/client/unversioned/clientcmd:go_default_library",
"//pkg/client/unversioned/clientcmd/api:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/registry/core/service/ipallocator:go_default_library",
"//pkg/util/cert:go_default_library",
"//pkg/util/intstr:go_default_library",
"//pkg/util/uuid:go_default_library",
"//vendor:github.com/blang/semver",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/util/wait",
],
)
go_test(
name = "go_default_test",
srcs = [
"addons_test.go",
"apiclient_test.go",
"discovery_test.go",
"manifests_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/util/intstr: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,295 @@
/*
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 master
import (
"fmt"
"net"
"path"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/images"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
"k8s.io/kubernetes/pkg/util/intstr"
)
func createKubeProxyPodSpec(cfg *kubeadmapi.MasterConfiguration) v1.PodSpec {
privilegedTrue := true
return v1.PodSpec{
HostNetwork: true,
SecurityContext: &v1.PodSecurityContext{},
Containers: []v1.Container{{
Name: kubeProxy,
Image: images.GetCoreImage(images.KubeProxyImage, cfg, kubeadmapi.GlobalEnvParams.HyperkubeImage),
Command: append(getProxyCommand(cfg), "--kubeconfig=/run/kubeconfig"),
SecurityContext: &v1.SecurityContext{Privileged: &privilegedTrue},
VolumeMounts: []v1.VolumeMount{
{
Name: "dbus",
MountPath: "/var/run/dbus",
ReadOnly: false,
},
{
// TODO there are handful of clever options to get around this, but it's
// easier to just mount kubelet's config here; we should probably just
// make sure that proxy reads the token and CA cert from /run/secrets
// and accepts `--master` at the same time
//
// clever options include:
// - do CSR dance and create kubeconfig and mount it as a secret
// - create a service account with a second secret encoding kubeconfig
// - use init container to convert known information to kubeconfig
// - ...whatever
Name: "kubeconfig",
MountPath: "/run/kubeconfig",
ReadOnly: false,
},
},
}},
Volumes: []v1.Volume{
{
Name: "kubeconfig",
VolumeSource: v1.VolumeSource{
HostPath: &v1.HostPathVolumeSource{Path: path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "kubelet.conf")},
},
},
{
Name: "dbus",
VolumeSource: v1.VolumeSource{
HostPath: &v1.HostPathVolumeSource{Path: "/var/run/dbus"},
},
},
},
}
}
func createKubeDNSPodSpec(cfg *kubeadmapi.MasterConfiguration) v1.PodSpec {
kubeDNSPort := int32(10053)
dnsmasqPort := int32(53)
return v1.PodSpec{
Containers: []v1.Container{
// DNS server
{
Name: "kubedns",
Image: images.GetAddonImage(images.KubeDNSImage),
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName(v1.ResourceMemory): resource.MustParse("170Mi"),
},
Requests: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("100m"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("70Mi"),
},
},
LivenessProbe: &v1.Probe{
Handler: v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Path: "/healthcheck/kubedns",
Port: intstr.FromInt(10054),
Scheme: v1.URISchemeHTTP,
},
},
InitialDelaySeconds: 60,
TimeoutSeconds: 5,
SuccessThreshold: 1,
FailureThreshold: 5,
},
// # we poll on pod startup for the Kubernetes master service and
// # only setup the /readiness HTTP server once that's available.
ReadinessProbe: &v1.Probe{
Handler: v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Path: "/readiness",
Port: intstr.FromInt(8081),
Scheme: v1.URISchemeHTTP,
},
},
InitialDelaySeconds: 3,
TimeoutSeconds: 5,
},
Args: []string{
fmt.Sprintf("--domain=%s", cfg.Networking.DNSDomain),
fmt.Sprintf("--dns-port=%d", kubeDNSPort),
"--config-map=kube-dns",
"--v=2",
},
Env: []v1.EnvVar{
{
Name: "PROMETHEUS_PORT",
Value: "10055",
},
},
Ports: []v1.ContainerPort{
{
ContainerPort: kubeDNSPort,
Name: "dns-local",
Protocol: v1.ProtocolUDP,
},
{
ContainerPort: kubeDNSPort,
Name: "dns-tcp-local",
Protocol: v1.ProtocolTCP,
},
{
ContainerPort: 10055,
Name: "metrics",
Protocol: v1.ProtocolTCP,
},
},
},
// dnsmasq
{
Name: "dnsmasq",
Image: images.GetAddonImage(images.KubeDNSmasqImage),
LivenessProbe: &v1.Probe{
Handler: v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Path: "/healthcheck/dnsmasq",
Port: intstr.FromInt(10054),
Scheme: v1.URISchemeHTTP,
},
},
InitialDelaySeconds: 60,
TimeoutSeconds: 5,
SuccessThreshold: 1,
FailureThreshold: 5,
},
Args: []string{
"--cache-size=1000",
"--no-resolv",
fmt.Sprintf("--server=127.0.0.1#%d", kubeDNSPort),
"--log-facility=-",
},
Ports: []v1.ContainerPort{
{
ContainerPort: dnsmasqPort,
Name: "dns",
Protocol: v1.ProtocolUDP,
},
{
ContainerPort: dnsmasqPort,
Name: "dns-tcp",
Protocol: v1.ProtocolTCP,
},
},
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("150m"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("10Mi"),
},
},
},
{
Name: "sidecar",
Image: images.GetAddonImage(images.KubeDNSSidecarImage),
LivenessProbe: &v1.Probe{
Handler: v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Path: "/metrics",
Port: intstr.FromInt(10054),
Scheme: v1.URISchemeHTTP,
},
},
InitialDelaySeconds: 60,
TimeoutSeconds: 5,
SuccessThreshold: 1,
FailureThreshold: 5,
},
Args: []string{
"--v=2",
"--logtostderr",
fmt.Sprintf("--probe=kubedns,127.0.0.1:10053,kubernetes.default.svc.%s,5,A", cfg.Networking.DNSDomain),
fmt.Sprintf("--probe=dnsmasq,127.0.0.1:53,kubernetes.default.svc.%s,5,A", cfg.Networking.DNSDomain),
},
Ports: []v1.ContainerPort{
{
ContainerPort: 10054,
Name: "metrics",
Protocol: v1.ProtocolTCP,
},
},
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName(v1.ResourceMemory): resource.MustParse("20Mi"),
v1.ResourceName(v1.ResourceCPU): resource.MustParse("10m"),
},
},
},
},
DNSPolicy: v1.DNSDefault,
}
}
func createKubeDNSServiceSpec(cfg *kubeadmapi.MasterConfiguration) (*v1.ServiceSpec, error) {
_, n, err := net.ParseCIDR(cfg.Networking.ServiceSubnet)
if err != nil {
return nil, fmt.Errorf("could not parse %q: %v", cfg.Networking.ServiceSubnet, err)
}
ip, err := ipallocator.GetIndexedIP(n, 10)
if err != nil {
return nil, fmt.Errorf("unable to allocate IP address for kube-dns addon from the given CIDR %q: [%v]", cfg.Networking.ServiceSubnet, err)
}
return &v1.ServiceSpec{
Selector: map[string]string{"name": "kube-dns"},
Ports: []v1.ServicePort{
{Name: "dns", Port: 53, Protocol: v1.ProtocolUDP},
{Name: "dns-tcp", Port: 53, Protocol: v1.ProtocolTCP},
},
ClusterIP: ip.String(),
}, nil
}
func CreateEssentialAddons(cfg *kubeadmapi.MasterConfiguration, client *clientset.Clientset) error {
kubeProxyDaemonSet := NewDaemonSet(kubeProxy, createKubeProxyPodSpec(cfg))
SetMasterTaintTolerations(&kubeProxyDaemonSet.Spec.Template.ObjectMeta)
SetNodeAffinity(&kubeProxyDaemonSet.Spec.Template.ObjectMeta, NativeArchitectureNodeAffinity())
if _, err := client.Extensions().DaemonSets(api.NamespaceSystem).Create(kubeProxyDaemonSet); err != nil {
return fmt.Errorf("failed creating essential kube-proxy addon [%v]", err)
}
fmt.Println("[addons] Created essential addon: kube-proxy")
kubeDNSDeployment := NewDeployment("kube-dns", 1, createKubeDNSPodSpec(cfg))
SetMasterTaintTolerations(&kubeDNSDeployment.Spec.Template.ObjectMeta)
SetNodeAffinity(&kubeDNSDeployment.Spec.Template.ObjectMeta, NativeArchitectureNodeAffinity())
if _, err := client.Extensions().Deployments(api.NamespaceSystem).Create(kubeDNSDeployment); err != nil {
return fmt.Errorf("failed creating essential kube-dns addon [%v]", err)
}
kubeDNSServiceSpec, err := createKubeDNSServiceSpec(cfg)
if err != nil {
return fmt.Errorf("failed creating essential kube-dns addon [%v]", err)
}
kubeDNSService := NewService("kube-dns", *kubeDNSServiceSpec)
kubeDNSService.ObjectMeta.Labels["kubernetes.io/name"] = "KubeDNS"
if _, err := client.Services(api.NamespaceSystem).Create(kubeDNSService); err != nil {
return fmt.Errorf("failed creating essential kube-dns addon [%v]", err)
}
fmt.Println("[addons] Created essential addon: kube-dns")
return nil
}

View file

@ -0,0 +1,115 @@
/*
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 master
import (
"testing"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func TestCreateKubeProxyPodSpec(t *testing.T) {
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
expected bool
}{
{
cfg: &kubeadmapi.MasterConfiguration{},
expected: true,
},
}
for _, rt := range tests {
actual := createKubeProxyPodSpec(rt.cfg)
if (actual.Containers[0].Name != "") != rt.expected {
t.Errorf(
"failed createKubeProxyPodSpec:\n\texpected: %t\n\t actual: %t",
rt.expected,
(actual.Containers[0].Name != ""),
)
}
}
}
func TestCreateKubeDNSPodSpec(t *testing.T) {
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
expected string
}{
{
cfg: &kubeadmapi.MasterConfiguration{
Networking: kubeadm.Networking{DNSDomain: "localhost"},
},
expected: "--domain=localhost",
},
{
cfg: &kubeadmapi.MasterConfiguration{
Networking: kubeadm.Networking{DNSDomain: "foo"},
},
expected: "--domain=foo",
},
}
for _, rt := range tests {
actual := createKubeDNSPodSpec(rt.cfg)
if actual.Containers[0].Args[0] != rt.expected {
t.Errorf(
"failed createKubeDNSPodSpec:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual.Containers[0].Args[0],
)
}
}
}
func TestCreateKubeDNSServiceSpec(t *testing.T) {
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
expected bool
}{
{
cfg: &kubeadmapi.MasterConfiguration{
Networking: kubeadm.Networking{ServiceSubnet: "foo"},
},
expected: false,
},
{
cfg: &kubeadmapi.MasterConfiguration{
Networking: kubeadm.Networking{ServiceSubnet: "10.0.0.1/1"},
},
expected: false,
},
{
cfg: &kubeadmapi.MasterConfiguration{
Networking: kubeadm.Networking{ServiceSubnet: "10.0.0.1/24"},
},
expected: true,
},
}
for _, rt := range tests {
_, actual := createKubeDNSServiceSpec(rt.cfg)
if (actual == nil) != rt.expected {
t.Errorf(
"failed createKubeDNSServiceSpec:\n\texpected: %t\n\t actual: %t",
rt.expected,
(actual == nil),
)
}
}
}

View file

@ -0,0 +1,293 @@
/*
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 master
import (
"encoding/json"
"fmt"
"runtime"
"time"
apierrs "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/kubernetes/cmd/kubeadm/app/images"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
extensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
)
const apiCallRetryInterval = 500 * time.Millisecond
func createAPIClient(adminKubeconfig *clientcmdapi.Config) (*clientset.Clientset, error) {
adminClientConfig, err := clientcmd.NewDefaultClientConfig(
*adminKubeconfig,
&clientcmd.ConfigOverrides{},
).ClientConfig()
if err != nil {
return nil, fmt.Errorf("failed to create API client configuration [%v]", err)
}
client, err := clientset.NewForConfig(adminClientConfig)
if err != nil {
return nil, fmt.Errorf("failed to create API client [%v]", err)
}
return client, nil
}
func CreateClientFromFile(path string) (*clientset.Clientset, error) {
adminKubeconfig, err := clientcmd.LoadFromFile(path)
if err != nil {
return nil, fmt.Errorf("failed to load admin kubeconfig [%v]", err)
}
return createAPIClient(adminKubeconfig)
}
func CreateClientAndWaitForAPI(file string) (*clientset.Clientset, error) {
client, err := CreateClientFromFile(file)
if err != nil {
return nil, err
}
fmt.Println("[apiclient] Created API client, waiting for the control plane to become ready")
start := time.Now()
wait.PollInfinite(apiCallRetryInterval, func() (bool, error) {
cs, err := client.ComponentStatuses().List(v1.ListOptions{})
if err != nil {
return false, nil
}
// TODO(phase2) must revisit this when we implement HA
if len(cs.Items) < 3 {
fmt.Println("[apiclient] Not all control plane components are ready yet")
return false, nil
}
for _, item := range cs.Items {
for _, condition := range item.Conditions {
if condition.Type != v1.ComponentHealthy {
fmt.Printf("[apiclient] Control plane component %q is still unhealthy: %#v\n", item.ObjectMeta.Name, item.Conditions)
return false, nil
}
}
}
fmt.Printf("[apiclient] All control plane components are healthy after %f seconds\n", time.Since(start).Seconds())
return true, nil
})
fmt.Println("[apiclient] Waiting for at least one node to register and become ready")
start = time.Now()
wait.PollInfinite(apiCallRetryInterval, func() (bool, error) {
nodeList, err := client.Nodes().List(v1.ListOptions{})
if err != nil {
fmt.Println("[apiclient] Temporarily unable to list nodes (will retry)")
return false, nil
}
if len(nodeList.Items) < 1 {
return false, nil
}
n := &nodeList.Items[0]
if !v1.IsNodeReady(n) {
fmt.Println("[apiclient] First node has registered, but is not ready yet")
return false, nil
}
fmt.Printf("[apiclient] First node is ready after %f seconds\n", time.Since(start).Seconds())
return true, nil
})
createDummyDeployment(client)
return client, nil
}
func standardLabels(n string) map[string]string {
return map[string]string{
"component": n, "name": n, "k8s-app": n,
"kubernetes.io/cluster-service": "true", "tier": "node",
}
}
func NewDaemonSet(daemonName string, podSpec v1.PodSpec) *extensions.DaemonSet {
l := standardLabels(daemonName)
return &extensions.DaemonSet{
ObjectMeta: v1.ObjectMeta{Name: daemonName},
Spec: extensions.DaemonSetSpec{
Selector: &metav1.LabelSelector{MatchLabels: l},
Template: v1.PodTemplateSpec{
ObjectMeta: v1.ObjectMeta{Labels: l},
Spec: podSpec,
},
},
}
}
func NewService(serviceName string, spec v1.ServiceSpec) *v1.Service {
l := standardLabels(serviceName)
return &v1.Service{
ObjectMeta: v1.ObjectMeta{
Name: serviceName,
Labels: l,
},
Spec: spec,
}
}
func NewDeployment(deploymentName string, replicas int32, podSpec v1.PodSpec) *extensions.Deployment {
l := standardLabels(deploymentName)
return &extensions.Deployment{
ObjectMeta: v1.ObjectMeta{Name: deploymentName},
Spec: extensions.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{MatchLabels: l},
Template: v1.PodTemplateSpec{
ObjectMeta: v1.ObjectMeta{Labels: l},
Spec: podSpec,
},
},
}
}
// It's safe to do this for alpha, as we don't have HA and there is no way we can get
// more then one node here (TODO(phase1+) use os.Hostname)
func findMyself(client *clientset.Clientset) (*v1.Node, error) {
nodeList, err := client.Nodes().List(v1.ListOptions{})
if err != nil {
return nil, fmt.Errorf("unable to list nodes [%v]", err)
}
if len(nodeList.Items) < 1 {
return nil, fmt.Errorf("no nodes found")
}
node := &nodeList.Items[0]
return node, nil
}
func attemptToUpdateMasterRoleLabelsAndTaints(client *clientset.Clientset, schedulable bool) error {
n, err := findMyself(client)
if err != nil {
return err
}
n.ObjectMeta.Labels[metav1.NodeLabelKubeadmAlphaRole] = metav1.NodeLabelRoleMaster
if !schedulable {
taintsAnnotation, _ := json.Marshal([]v1.Taint{{Key: "dedicated", Value: "master", Effect: "NoSchedule"}})
n.ObjectMeta.Annotations[v1.TaintsAnnotationKey] = string(taintsAnnotation)
}
if _, err := client.Nodes().Update(n); err != nil {
if apierrs.IsConflict(err) {
fmt.Println("[apiclient] Temporarily unable to update master node metadata due to conflict (will retry)")
time.Sleep(apiCallRetryInterval)
attemptToUpdateMasterRoleLabelsAndTaints(client, schedulable)
} else {
return err
}
}
return nil
}
func UpdateMasterRoleLabelsAndTaints(client *clientset.Clientset, schedulable bool) error {
// TODO(phase1+) use iterate instead of recursion
err := attemptToUpdateMasterRoleLabelsAndTaints(client, schedulable)
if err != nil {
return fmt.Errorf("failed to update master node - [%v]", err)
}
return nil
}
func SetMasterTaintTolerations(meta *v1.ObjectMeta) {
tolerationsAnnotation, _ := json.Marshal([]v1.Toleration{{Key: "dedicated", Value: "master", Effect: "NoSchedule"}})
if meta.Annotations == nil {
meta.Annotations = map[string]string{}
}
meta.Annotations[v1.TolerationsAnnotationKey] = string(tolerationsAnnotation)
}
// SetNodeAffinity is a basic helper to set meta.Annotations[v1.AffinityAnnotationKey] for one or more v1.NodeSelectorRequirement(s)
func SetNodeAffinity(meta *v1.ObjectMeta, expr ...v1.NodeSelectorRequirement) {
nodeAffinity := &v1.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
NodeSelectorTerms: []v1.NodeSelectorTerm{{MatchExpressions: expr}},
},
}
affinityAnnotation, _ := json.Marshal(v1.Affinity{NodeAffinity: nodeAffinity})
if meta.Annotations == nil {
meta.Annotations = map[string]string{}
}
meta.Annotations[v1.AffinityAnnotationKey] = string(affinityAnnotation)
}
// MasterNodeAffinity returns v1.NodeSelectorRequirement to be used with SetNodeAffinity to set affinity to master node
func MasterNodeAffinity() v1.NodeSelectorRequirement {
return v1.NodeSelectorRequirement{
Key: metav1.NodeLabelKubeadmAlphaRole,
Operator: v1.NodeSelectorOpIn,
Values: []string{metav1.NodeLabelRoleMaster},
}
}
// NativeArchitectureNodeAffinity returns v1.NodeSelectorRequirement to be used with SetNodeAffinity to nodes with CPU architecture
// the same as master node
func NativeArchitectureNodeAffinity() v1.NodeSelectorRequirement {
return v1.NodeSelectorRequirement{
Key: "beta.kubernetes.io/arch", Operator: v1.NodeSelectorOpIn, Values: []string{runtime.GOARCH},
}
}
func createDummyDeployment(client *clientset.Clientset) {
fmt.Println("[apiclient] Creating a test deployment")
dummyDeployment := NewDeployment("dummy", 1, v1.PodSpec{
HostNetwork: true,
SecurityContext: &v1.PodSecurityContext{},
Containers: []v1.Container{{
Name: "dummy",
Image: images.GetAddonImage("pause"),
}},
})
wait.PollInfinite(apiCallRetryInterval, func() (bool, error) {
// TODO: we should check the error, as some cases may be fatal
if _, err := client.Extensions().Deployments(api.NamespaceSystem).Create(dummyDeployment); err != nil {
fmt.Printf("[apiclient] Failed to create test deployment [%v] (will retry)\n", err)
return false, nil
}
return true, nil
})
wait.PollInfinite(apiCallRetryInterval, func() (bool, error) {
d, err := client.Extensions().Deployments(api.NamespaceSystem).Get("dummy", metav1.GetOptions{})
if err != nil {
fmt.Printf("[apiclient] Failed to get test deployment [%v] (will retry)\n", err)
return false, nil
}
if d.Status.AvailableReplicas < 1 {
return false, nil
}
return true, nil
})
fmt.Println("[apiclient] Test deployment succeeded")
// TODO: In the future, make sure the ReplicaSet and Pod are garbage collected
if err := client.Extensions().Deployments(api.NamespaceSystem).Delete("dummy", &v1.DeleteOptions{}); err != nil {
fmt.Printf("[apiclient] Failed to delete test deployment [%v] (will ignore)\n", err)
}
}

View file

@ -0,0 +1,201 @@
/*
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 master
import (
"fmt"
"testing"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
apiv1 "k8s.io/kubernetes/pkg/api/v1"
)
func TestCreateClientAndWaitForAPI(t *testing.T) {
cfg := &kubeadmapi.MasterConfiguration{
Networking: kubeadm.Networking{DNSDomain: "localhost"},
}
fmt.Println(cfg)
}
func TestStandardLabels(t *testing.T) {
var tests = []struct {
n string
expected string
}{
{
n: "foo",
expected: "foo",
},
{
n: "bar",
expected: "bar",
},
}
for _, rt := range tests {
actual := standardLabels(rt.n)
if actual["component"] != rt.expected {
t.Errorf(
"failed standardLabels:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual["component"],
)
}
if actual["name"] != rt.expected {
t.Errorf(
"failed standardLabels:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual["name"],
)
}
if actual["k8s-app"] != rt.expected {
t.Errorf(
"failed standardLabels:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual["k8s-app"],
)
}
}
}
func TestNewDaemonSet(t *testing.T) {
var tests = []struct {
dn string
expected string
}{
{
dn: "foo",
expected: "foo",
},
{
dn: "bar",
expected: "bar",
},
}
for _, rt := range tests {
p := apiv1.PodSpec{}
actual := NewDaemonSet(rt.dn, p)
if actual.Spec.Selector.MatchLabels["k8s-app"] != rt.expected {
t.Errorf(
"failed NewDaemonSet:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual.Spec.Selector.MatchLabels["k8s-app"],
)
}
if actual.Spec.Selector.MatchLabels["component"] != rt.expected {
t.Errorf(
"failed NewDaemonSet:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual.Spec.Selector.MatchLabels["component"],
)
}
if actual.Spec.Selector.MatchLabels["name"] != rt.expected {
t.Errorf(
"failed NewDaemonSet:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual.Spec.Selector.MatchLabels["name"],
)
}
}
}
func TestNewService(t *testing.T) {
var tests = []struct {
dn string
expected string
}{
{
dn: "foo",
expected: "foo",
},
{
dn: "bar",
expected: "bar",
},
}
for _, rt := range tests {
p := apiv1.ServiceSpec{}
actual := NewService(rt.dn, p)
if actual.ObjectMeta.Labels["k8s-app"] != rt.expected {
t.Errorf(
"failed NewService:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual.ObjectMeta.Labels["k8s-app"],
)
}
if actual.ObjectMeta.Labels["component"] != rt.expected {
t.Errorf(
"failed NewService:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual.ObjectMeta.Labels["component"],
)
}
if actual.ObjectMeta.Labels["name"] != rt.expected {
t.Errorf(
"failed NewService:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual.ObjectMeta.Labels["name"],
)
}
}
}
func TestNewDeployment(t *testing.T) {
var tests = []struct {
dn string
expected string
}{
{
dn: "foo",
expected: "foo",
},
{
dn: "bar",
expected: "bar",
},
}
for _, rt := range tests {
p := apiv1.PodSpec{}
actual := NewDeployment(rt.dn, 1, p)
if actual.Spec.Selector.MatchLabels["k8s-app"] != rt.expected {
t.Errorf(
"failed NewDeployment:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual.Spec.Selector.MatchLabels["k8s-app"],
)
}
if actual.Spec.Selector.MatchLabels["component"] != rt.expected {
t.Errorf(
"failed NewDeployment:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual.Spec.Selector.MatchLabels["component"],
)
}
if actual.Spec.Selector.MatchLabels["name"] != rt.expected {
t.Errorf(
"failed NewDeployment:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual.Spec.Selector.MatchLabels["name"],
)
}
}
}

View file

@ -0,0 +1,150 @@
/*
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 master
import (
"crypto/x509"
"encoding/json"
"fmt"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
extensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
certutil "k8s.io/kubernetes/pkg/util/cert"
)
type kubeDiscovery struct {
Deployment *extensions.Deployment
Secret *v1.Secret
}
const (
kubeDiscoveryName = "kube-discovery"
kubeDiscoverySecretName = "clusterinfo"
)
func encodeKubeDiscoverySecretData(dcfg *kubeadmapi.TokenDiscovery, apicfg kubeadmapi.API, caCert *x509.Certificate) map[string][]byte {
var (
data = map[string][]byte{}
endpointList = []string{}
tokenMap = map[string]string{}
)
for _, addr := range apicfg.AdvertiseAddresses {
endpointList = append(endpointList, fmt.Sprintf("https://%s:%d", addr, apicfg.Port))
}
tokenMap[dcfg.ID] = dcfg.Secret
data["endpoint-list.json"], _ = json.Marshal(endpointList)
data["token-map.json"], _ = json.Marshal(tokenMap)
data["ca.pem"] = certutil.EncodeCertPEM(caCert)
return data
}
func newKubeDiscoveryPodSpec(cfg *kubeadmapi.MasterConfiguration) v1.PodSpec {
return v1.PodSpec{
// We have to use host network namespace, as `HostPort`/`HostIP` are Docker's
// business and CNI support isn't quite there yet (except for kubenet)
// (see https://github.com/kubernetes/kubernetes/issues/31307)
// TODO update this when #31307 is resolved
HostNetwork: true,
SecurityContext: &v1.PodSecurityContext{},
Containers: []v1.Container{{
Name: kubeDiscoveryName,
Image: kubeadmapi.GlobalEnvParams.DiscoveryImage,
Command: []string{"/usr/local/bin/kube-discovery"},
VolumeMounts: []v1.VolumeMount{{
Name: kubeDiscoverySecretName,
MountPath: "/tmp/secret", // TODO use a shared constant
ReadOnly: true,
}},
Ports: []v1.ContainerPort{
// TODO when CNI issue (#31307) is resolved, we should consider adding
// `HostIP: s.API.AdvertiseAddrs[0]`, if there is only one address`
{Name: "http", ContainerPort: kubeadmapiext.DefaultDiscoveryBindPort, HostPort: kubeadmutil.DiscoveryPort(cfg.Discovery.Token)},
},
SecurityContext: &v1.SecurityContext{
SELinuxOptions: &v1.SELinuxOptions{
// TODO: This implies our discovery container is not being restricted by
// SELinux. This is not optimal and would be nice to adjust in future
// so it can read /tmp/secret, but for now this avoids recommending
// setenforce 0 system-wide.
Type: "spc_t",
},
},
}},
Volumes: []v1.Volume{{
Name: kubeDiscoverySecretName,
VolumeSource: v1.VolumeSource{
Secret: &v1.SecretVolumeSource{SecretName: kubeDiscoverySecretName},
}},
},
}
}
func newKubeDiscovery(cfg *kubeadmapi.MasterConfiguration, caCert *x509.Certificate) kubeDiscovery {
kd := kubeDiscovery{
Deployment: NewDeployment(kubeDiscoveryName, 1, newKubeDiscoveryPodSpec(cfg)),
Secret: &v1.Secret{
ObjectMeta: v1.ObjectMeta{Name: kubeDiscoverySecretName},
Type: v1.SecretTypeOpaque,
Data: encodeKubeDiscoverySecretData(cfg.Discovery.Token, cfg.API, caCert),
},
}
SetMasterTaintTolerations(&kd.Deployment.Spec.Template.ObjectMeta)
SetNodeAffinity(&kd.Deployment.Spec.Template.ObjectMeta, MasterNodeAffinity(), NativeArchitectureNodeAffinity())
return kd
}
func CreateDiscoveryDeploymentAndSecret(cfg *kubeadmapi.MasterConfiguration, client *clientset.Clientset, caCert *x509.Certificate) error {
kd := newKubeDiscovery(cfg, caCert)
if _, err := client.Extensions().Deployments(api.NamespaceSystem).Create(kd.Deployment); err != nil {
return fmt.Errorf("failed to create %q deployment [%v]", kubeDiscoveryName, err)
}
if _, err := client.Secrets(api.NamespaceSystem).Create(kd.Secret); err != nil {
return fmt.Errorf("failed to create %q secret [%v]", kubeDiscoverySecretName, err)
}
fmt.Println("[token-discovery] Created the kube-discovery deployment, waiting for it to become ready")
start := time.Now()
wait.PollInfinite(apiCallRetryInterval, func() (bool, error) {
d, err := client.Extensions().Deployments(api.NamespaceSystem).Get(kubeDiscoveryName, metav1.GetOptions{})
if err != nil {
return false, nil
}
if d.Status.AvailableReplicas < 1 {
return false, nil
}
return true, nil
})
fmt.Printf("[token-discovery] kube-discovery is ready after %f seconds\n", time.Since(start).Seconds())
return nil
}

View file

@ -0,0 +1,49 @@
/*
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 master
import (
"crypto/x509"
"testing"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func TestNewKubeDiscovery(t *testing.T) {
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
caCert *x509.Certificate
expected bool
}{
{
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadmapi.API{Port: 123, AdvertiseAddresses: []string{"10.0.0.1"}},
Networking: kubeadmapi.Networking{ServiceSubnet: "10.0.0.1/1"},
Discovery: kubeadmapi.Discovery{Token: &kubeadmapi.TokenDiscovery{}},
},
caCert: &x509.Certificate{},
},
}
for _, rt := range tests {
actual := newKubeDiscovery(rt.cfg, rt.caCert)
if actual.Deployment == nil || actual.Secret == nil {
t.Errorf(
"failed newKubeDiscovery, kubeDiscovery was nil",
)
}
}
}

View file

@ -0,0 +1,408 @@
/*
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 master
import (
"bytes"
"encoding/json"
"fmt"
"os"
"path"
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/images"
"k8s.io/kubernetes/pkg/api/resource"
api "k8s.io/kubernetes/pkg/api/v1"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/util/intstr"
"github.com/blang/semver"
)
// Static pod definitions in golang form are included below so that `kubeadm init` can get going.
const (
DefaultClusterName = "kubernetes"
DefaultCloudConfigPath = "/etc/kubernetes/cloud-config"
etcd = "etcd"
apiServer = "apiserver"
controllerManager = "controller-manager"
scheduler = "scheduler"
proxy = "proxy"
kubeAPIServer = "kube-apiserver"
kubeControllerManager = "kube-controller-manager"
kubeScheduler = "kube-scheduler"
kubeProxy = "kube-proxy"
)
var (
// Minimum version of kube-apiserver that supports --kubelet-preferred-address-types
preferredAddressAPIServerMinVersion = semver.MustParse("1.5.0")
// Minimum version of kube-apiserver that has to have --anonymous-auth=false set
anonAuthDisableAPIServerMinVersion = semver.MustParse("1.5.0")
)
// WriteStaticPodManifests builds manifest objects based on user provided configuration and then dumps it to disk
// where kubelet will pick and schedule them.
func WriteStaticPodManifests(cfg *kubeadmapi.MasterConfiguration) error {
volumes := []api.Volume{k8sVolume(cfg)}
volumeMounts := []api.VolumeMount{k8sVolumeMount()}
if isCertsVolumeMountNeeded() {
volumes = append(volumes, certsVolume(cfg))
volumeMounts = append(volumeMounts, certsVolumeMount())
}
if isPkiVolumeMountNeeded() {
volumes = append(volumes, pkiVolume(cfg))
volumeMounts = append(volumeMounts, pkiVolumeMount())
}
// Prepare static pod specs
staticPodSpecs := map[string]api.Pod{
kubeAPIServer: componentPod(api.Container{
Name: kubeAPIServer,
Image: images.GetCoreImage(images.KubeAPIServerImage, cfg, kubeadmapi.GlobalEnvParams.HyperkubeImage),
Command: getAPIServerCommand(cfg),
VolumeMounts: volumeMounts,
LivenessProbe: componentProbe(8080, "/healthz"),
Resources: componentResources("250m"),
Env: getProxyEnvVars(),
}, volumes...),
kubeControllerManager: componentPod(api.Container{
Name: kubeControllerManager,
Image: images.GetCoreImage(images.KubeControllerManagerImage, cfg, kubeadmapi.GlobalEnvParams.HyperkubeImage),
Command: getControllerManagerCommand(cfg),
VolumeMounts: volumeMounts,
LivenessProbe: componentProbe(10252, "/healthz"),
Resources: componentResources("200m"),
Env: getProxyEnvVars(),
}, volumes...),
kubeScheduler: componentPod(api.Container{
Name: kubeScheduler,
Image: images.GetCoreImage(images.KubeSchedulerImage, cfg, kubeadmapi.GlobalEnvParams.HyperkubeImage),
Command: getSchedulerCommand(cfg),
LivenessProbe: componentProbe(10251, "/healthz"),
Resources: componentResources("100m"),
Env: getProxyEnvVars(),
}),
}
// Add etcd static pod spec only if external etcd is not configured
if len(cfg.Etcd.Endpoints) == 0 {
staticPodSpecs[etcd] = componentPod(api.Container{
Name: etcd,
Command: []string{
"etcd",
"--listen-client-urls=http://127.0.0.1:2379",
"--advertise-client-urls=http://127.0.0.1:2379",
"--data-dir=/var/lib/etcd",
},
VolumeMounts: []api.VolumeMount{certsVolumeMount(), etcdVolumeMount(), k8sVolumeMount()},
Image: images.GetCoreImage(images.KubeEtcdImage, cfg, kubeadmapi.GlobalEnvParams.EtcdImage),
LivenessProbe: componentProbe(2379, "/health"),
Resources: componentResources("200m"),
SecurityContext: &api.SecurityContext{
SELinuxOptions: &api.SELinuxOptions{
// TODO: This implies our etcd container is not being restricted by
// SELinux. This is not optimal and would be nice to adjust in future
// so it can create and write /var/lib/etcd, but for now this avoids
// recommending setenforce 0 system-wide.
Type: "spc_t",
},
},
}, certsVolume(cfg), etcdVolume(cfg), k8sVolume(cfg))
}
manifestsPath := path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "manifests")
if err := os.MkdirAll(manifestsPath, 0700); err != nil {
return fmt.Errorf("failed to create directory %q [%v]", manifestsPath, err)
}
for name, spec := range staticPodSpecs {
filename := path.Join(manifestsPath, name+".json")
serialized, err := json.MarshalIndent(spec, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal manifest for %q to JSON [%v]", name, err)
}
if err := cmdutil.DumpReaderToFile(bytes.NewReader(serialized), filename); err != nil {
return fmt.Errorf("failed to create static pod manifest file for %q (%q) [%v]", name, filename, err)
}
}
return nil
}
// etcdVolume exposes a path on the host in order to guarantee data survival during reboot.
func etcdVolume(cfg *kubeadmapi.MasterConfiguration) api.Volume {
return api.Volume{
Name: "etcd",
VolumeSource: api.VolumeSource{
HostPath: &api.HostPathVolumeSource{Path: kubeadmapi.GlobalEnvParams.HostEtcdPath},
},
}
}
func etcdVolumeMount() api.VolumeMount {
return api.VolumeMount{
Name: "etcd",
MountPath: "/var/lib/etcd",
}
}
func isCertsVolumeMountNeeded() bool {
// Always return true for now. We may add conditional logic here for images which do not require host mounting /etc/ssl
// hyperkube for example already has valid ca-certificates installed
return true
}
// certsVolume exposes host SSL certificates to pod containers.
func certsVolume(cfg *kubeadmapi.MasterConfiguration) api.Volume {
return api.Volume{
Name: "certs",
VolumeSource: api.VolumeSource{
// TODO(phase1+) make path configurable
HostPath: &api.HostPathVolumeSource{Path: "/etc/ssl/certs"},
},
}
}
func certsVolumeMount() api.VolumeMount {
return api.VolumeMount{
Name: "certs",
MountPath: "/etc/ssl/certs",
}
}
func isPkiVolumeMountNeeded() bool {
// On some systems were we host-mount /etc/ssl/certs, it is also required to mount /etc/pki. This is needed
// due to symlinks pointing from files in /etc/ssl/certs into /etc/pki/
if _, err := os.Stat("/etc/pki"); err == nil {
return true
}
return false
}
func pkiVolume(cfg *kubeadmapi.MasterConfiguration) api.Volume {
return api.Volume{
Name: "pki",
VolumeSource: api.VolumeSource{
// TODO(phase1+) make path configurable
HostPath: &api.HostPathVolumeSource{Path: "/etc/pki"},
},
}
}
func pkiVolumeMount() api.VolumeMount {
return api.VolumeMount{
Name: "pki",
MountPath: "/etc/pki",
}
}
func k8sVolume(cfg *kubeadmapi.MasterConfiguration) api.Volume {
return api.Volume{
Name: "k8s",
VolumeSource: api.VolumeSource{
HostPath: &api.HostPathVolumeSource{Path: kubeadmapi.GlobalEnvParams.KubernetesDir},
},
}
}
func k8sVolumeMount() api.VolumeMount {
return api.VolumeMount{
Name: "k8s",
MountPath: "/etc/kubernetes/",
ReadOnly: true,
}
}
func componentResources(cpu string) api.ResourceRequirements {
return api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse(cpu),
},
}
}
func componentProbe(port int, path string) *api.Probe {
return &api.Probe{
Handler: api.Handler{
HTTPGet: &api.HTTPGetAction{
Host: "127.0.0.1",
Path: path,
Port: intstr.FromInt(port),
},
},
InitialDelaySeconds: 15,
TimeoutSeconds: 15,
FailureThreshold: 8,
}
}
func componentPod(container api.Container, volumes ...api.Volume) api.Pod {
return api.Pod{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Pod",
},
ObjectMeta: api.ObjectMeta{
Name: container.Name,
Namespace: "kube-system",
Labels: map[string]string{"component": container.Name, "tier": "control-plane"},
},
Spec: api.PodSpec{
Containers: []api.Container{container},
HostNetwork: true,
Volumes: volumes,
},
}
}
func getComponentBaseCommand(component string) []string {
if kubeadmapi.GlobalEnvParams.HyperkubeImage != "" {
return []string{"/hyperkube", component}
}
return []string{"kube-" + component}
}
func getAPIServerCommand(cfg *kubeadmapi.MasterConfiguration) []string {
command := append(getComponentBaseCommand(apiServer),
"--insecure-bind-address=127.0.0.1",
"--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota",
"--service-cluster-ip-range="+cfg.Networking.ServiceSubnet,
"--service-account-key-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/apiserver-key.pem",
"--client-ca-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/ca.pem",
"--tls-cert-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/apiserver.pem",
"--tls-private-key-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/apiserver-key.pem",
"--token-auth-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/tokens.csv",
fmt.Sprintf("--secure-port=%d", cfg.API.Port),
"--allow-privileged",
)
// Use first address we are given
if len(cfg.API.AdvertiseAddresses) > 0 {
command = append(command, fmt.Sprintf("--advertise-address=%s", cfg.API.AdvertiseAddresses[0]))
}
if len(cfg.KubernetesVersion) != 0 {
// If the k8s version is v1.5-something, this argument is set and makes `kubectl logs` and `kubectl exec`
// work on bare-metal where hostnames aren't usually resolvable
// Omit the "v" in the beginning, otherwise semver will fail
k8sVersion, err := semver.Parse(cfg.KubernetesVersion[1:])
// If the k8s version is greater than this version, it supports telling it which way it should contact kubelets
if err == nil && k8sVersion.GTE(preferredAddressAPIServerMinVersion) {
command = append(command, "--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname")
}
// This is a critical "bugfix". Any version above this is vulnarable unless a RBAC/ABAC-authorizer is provided (which kubeadm doesn't for the time being)
if err == nil && k8sVersion.GTE(anonAuthDisableAPIServerMinVersion) {
command = append(command, "--anonymous-auth=false")
}
}
// Check if the user decided to use an external etcd cluster
if len(cfg.Etcd.Endpoints) > 0 {
command = append(command, fmt.Sprintf("--etcd-servers=%s", strings.Join(cfg.Etcd.Endpoints, ",")))
} else {
command = append(command, "--etcd-servers=http://127.0.0.1:2379")
}
// Is etcd secured?
if cfg.Etcd.CAFile != "" {
command = append(command, fmt.Sprintf("--etcd-cafile=%s", cfg.Etcd.CAFile))
}
if cfg.Etcd.CertFile != "" && cfg.Etcd.KeyFile != "" {
etcdClientFileArg := fmt.Sprintf("--etcd-certfile=%s", cfg.Etcd.CertFile)
etcdKeyFileArg := fmt.Sprintf("--etcd-keyfile=%s", cfg.Etcd.KeyFile)
command = append(command, etcdClientFileArg, etcdKeyFileArg)
}
if cfg.CloudProvider != "" {
command = append(command, "--cloud-provider="+cfg.CloudProvider)
// Only append the --cloud-config option if there's a such file
if _, err := os.Stat(DefaultCloudConfigPath); err == nil {
command = append(command, "--cloud-config="+DefaultCloudConfigPath)
}
}
return command
}
func getControllerManagerCommand(cfg *kubeadmapi.MasterConfiguration) []string {
command := append(getComponentBaseCommand(controllerManager),
"--address=127.0.0.1",
"--leader-elect",
"--master=127.0.0.1:8080",
"--cluster-name="+DefaultClusterName,
"--root-ca-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/ca.pem",
"--service-account-private-key-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/apiserver-key.pem",
"--cluster-signing-cert-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/ca.pem",
"--cluster-signing-key-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/ca-key.pem",
"--insecure-experimental-approve-all-kubelet-csrs-for-group=system:kubelet-bootstrap",
)
if cfg.CloudProvider != "" {
command = append(command, "--cloud-provider="+cfg.CloudProvider)
// Only append the --cloud-config option if there's a such file
if _, err := os.Stat(DefaultCloudConfigPath); err == nil {
command = append(command, "--cloud-config="+DefaultCloudConfigPath)
}
}
// Let the controller-manager allocate Node CIDRs for the Pod network.
// Each node will get a subspace of the address CIDR provided with --pod-network-cidr.
if cfg.Networking.PodSubnet != "" {
command = append(command, "--allocate-node-cidrs=true", "--cluster-cidr="+cfg.Networking.PodSubnet)
}
return command
}
func getSchedulerCommand(cfg *kubeadmapi.MasterConfiguration) []string {
return append(getComponentBaseCommand(scheduler),
"--address=127.0.0.1",
"--leader-elect",
"--master=127.0.0.1:8080",
)
}
func getProxyCommand(cfg *kubeadmapi.MasterConfiguration) []string {
return getComponentBaseCommand(proxy)
}
func getProxyEnvVars() []api.EnvVar {
envs := []api.EnvVar{}
for _, env := range os.Environ() {
pos := strings.Index(env, "=")
if pos == -1 {
// malformed environment variable, skip it.
continue
}
name := env[:pos]
value := env[pos+1:]
if strings.HasSuffix(strings.ToLower(name), "_proxy") && value != "" {
envVar := api.EnvVar{Name: name, Value: value}
envs = append(envs, envVar)
}
}
return envs
}

View file

@ -0,0 +1,594 @@
/*
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 master
import (
"fmt"
"io/ioutil"
"os"
"testing"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
api "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/util/intstr"
)
func TestWriteStaticPodManifests(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("Couldn't create tmpdir")
}
defer os.Remove(tmpdir)
// set up tmp GlobalEnvParams values for testing
oldEnv := kubeadmapi.GlobalEnvParams
kubeadmapi.GlobalEnvParams.KubernetesDir = fmt.Sprintf("%s/etc/kubernetes", tmpdir)
defer func() { kubeadmapi.GlobalEnvParams = oldEnv }()
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
expected bool
}{
{
cfg: &kubeadmapi.MasterConfiguration{},
expected: true,
},
}
for _, rt := range tests {
actual := WriteStaticPodManifests(rt.cfg)
if (actual == nil) != rt.expected {
t.Errorf(
"failed WriteStaticPodManifests with an error:\n\texpected: %t\n\t actual: %t",
rt.expected,
(actual == nil),
)
}
}
}
func TestEtcdVolume(t *testing.T) {
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
expected api.Volume
}{
{
cfg: &kubeadmapi.MasterConfiguration{},
expected: api.Volume{
Name: "etcd",
VolumeSource: api.VolumeSource{
HostPath: &api.HostPathVolumeSource{
Path: kubeadmapi.GlobalEnvParams.HostEtcdPath},
}},
},
}
for _, rt := range tests {
actual := etcdVolume(rt.cfg)
if actual.Name != rt.expected.Name {
t.Errorf(
"failed etcdVolume:\n\texpected: %s\n\t actual: %s",
rt.expected.Name,
actual.Name,
)
}
if actual.VolumeSource.HostPath.Path != rt.expected.VolumeSource.HostPath.Path {
t.Errorf(
"failed etcdVolume:\n\texpected: %s\n\t actual: %s",
rt.expected.VolumeSource.HostPath.Path,
actual.VolumeSource.HostPath.Path,
)
}
}
}
func TestEtcdVolumeMount(t *testing.T) {
var tests = []struct {
expected api.VolumeMount
}{
{
expected: api.VolumeMount{
Name: "etcd",
MountPath: "/var/lib/etcd",
},
},
}
for _, rt := range tests {
actual := etcdVolumeMount()
if actual.Name != rt.expected.Name {
t.Errorf(
"failed etcdVolumeMount:\n\texpected: %s\n\t actual: %s",
rt.expected.Name,
actual.Name,
)
}
if actual.MountPath != rt.expected.MountPath {
t.Errorf(
"failed etcdVolumeMount:\n\texpected: %s\n\t actual: %s",
rt.expected.MountPath,
actual.MountPath,
)
}
}
}
func TestCertsVolume(t *testing.T) {
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
expected api.Volume
}{
{
cfg: &kubeadmapi.MasterConfiguration{},
expected: api.Volume{
Name: "certs",
VolumeSource: api.VolumeSource{
HostPath: &api.HostPathVolumeSource{
Path: "/etc/ssl/certs"},
}},
},
}
for _, rt := range tests {
actual := certsVolume(rt.cfg)
if actual.Name != rt.expected.Name {
t.Errorf(
"failed certsVolume:\n\texpected: %s\n\t actual: %s",
rt.expected.Name,
actual.Name,
)
}
if actual.VolumeSource.HostPath.Path != rt.expected.VolumeSource.HostPath.Path {
t.Errorf(
"failed certsVolume:\n\texpected: %s\n\t actual: %s",
rt.expected.VolumeSource.HostPath.Path,
actual.VolumeSource.HostPath.Path,
)
}
}
}
func TestCertsVolumeMount(t *testing.T) {
var tests = []struct {
expected api.VolumeMount
}{
{
expected: api.VolumeMount{
Name: "certs",
MountPath: "/etc/ssl/certs",
},
},
}
for _, rt := range tests {
actual := certsVolumeMount()
if actual.Name != rt.expected.Name {
t.Errorf(
"failed certsVolumeMount:\n\texpected: %s\n\t actual: %s",
rt.expected.Name,
actual.Name,
)
}
if actual.MountPath != rt.expected.MountPath {
t.Errorf(
"failed certsVolumeMount:\n\texpected: %s\n\t actual: %s",
rt.expected.MountPath,
actual.MountPath,
)
}
}
}
func TestK8sVolume(t *testing.T) {
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
expected api.Volume
}{
{
cfg: &kubeadmapi.MasterConfiguration{},
expected: api.Volume{
Name: "k8s",
VolumeSource: api.VolumeSource{
HostPath: &api.HostPathVolumeSource{
Path: kubeadmapi.GlobalEnvParams.KubernetesDir},
}},
},
}
for _, rt := range tests {
actual := k8sVolume(rt.cfg)
if actual.Name != rt.expected.Name {
t.Errorf(
"failed k8sVolume:\n\texpected: %s\n\t actual: %s",
rt.expected.Name,
actual.Name,
)
}
if actual.VolumeSource.HostPath.Path != rt.expected.VolumeSource.HostPath.Path {
t.Errorf(
"failed k8sVolume:\n\texpected: %s\n\t actual: %s",
rt.expected.VolumeSource.HostPath.Path,
actual.VolumeSource.HostPath.Path,
)
}
}
}
func TestK8sVolumeMount(t *testing.T) {
var tests = []struct {
expected api.VolumeMount
}{
{
expected: api.VolumeMount{
Name: "k8s",
MountPath: "/etc/kubernetes/",
ReadOnly: true,
},
},
}
for _, rt := range tests {
actual := k8sVolumeMount()
if actual.Name != rt.expected.Name {
t.Errorf(
"failed k8sVolumeMount:\n\texpected: %s\n\t actual: %s",
rt.expected.Name,
actual.Name,
)
}
if actual.MountPath != rt.expected.MountPath {
t.Errorf(
"failed k8sVolumeMount:\n\texpected: %s\n\t actual: %s",
rt.expected.MountPath,
actual.MountPath,
)
}
if actual.ReadOnly != rt.expected.ReadOnly {
t.Errorf(
"failed k8sVolumeMount:\n\texpected: %t\n\t actual: %t",
rt.expected.ReadOnly,
actual.ReadOnly,
)
}
}
}
func TestComponentResources(t *testing.T) {
a := componentResources("250m")
if a.Requests == nil {
t.Errorf(
"failed componentResources, return value was nil",
)
}
}
func TestComponentProbe(t *testing.T) {
var tests = []struct {
port int
path string
}{
{
port: 1,
path: "foo",
},
}
for _, rt := range tests {
actual := componentProbe(rt.port, rt.path)
if actual.Handler.HTTPGet.Port != intstr.FromInt(rt.port) {
t.Errorf(
"failed componentProbe:\n\texpected: %v\n\t actual: %v",
rt.port,
actual.Handler.HTTPGet.Port,
)
}
if actual.Handler.HTTPGet.Path != rt.path {
t.Errorf(
"failed componentProbe:\n\texpected: %s\n\t actual: %s",
rt.path,
actual.Handler.HTTPGet.Path,
)
}
}
}
func TestComponentPod(t *testing.T) {
var tests = []struct {
n string
}{
{
n: "foo",
},
}
for _, rt := range tests {
c := api.Container{Name: rt.n}
v := api.Volume{}
actual := componentPod(c, v)
if actual.ObjectMeta.Name != rt.n {
t.Errorf(
"failed componentPod:\n\texpected: %s\n\t actual: %s",
rt.n,
actual.ObjectMeta.Name,
)
}
}
}
func TestGetComponentBaseCommand(t *testing.T) {
var tests = []struct {
c string
expected []string
}{
{
c: "foo",
expected: []string{"kube-foo", "--v=2"},
},
{
c: "bar",
expected: []string{"kube-bar", "--v=2"},
},
}
for _, rt := range tests {
actual := getComponentBaseCommand(rt.c)
for i := range actual {
if actual[i] != rt.expected[i] {
t.Errorf(
"failed getComponentBaseCommand:\n\texpected: %s\n\t actual: %s",
rt.expected[i],
actual[i],
)
}
}
}
}
func TestGetAPIServerCommand(t *testing.T) {
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
expected []string
}{
{
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadm.API{Port: 123},
Networking: kubeadm.Networking{ServiceSubnet: "bar"},
},
expected: []string{
"kube-apiserver",
"--insecure-bind-address=127.0.0.1",
"--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota",
"--service-cluster-ip-range=bar",
"--service-account-key-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/apiserver-key.pem",
"--client-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca.pem",
"--tls-cert-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/apiserver.pem",
"--tls-private-key-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/apiserver-key.pem",
"--token-auth-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/tokens.csv",
fmt.Sprintf("--secure-port=%d", 123),
"--allow-privileged",
"--etcd-servers=http://127.0.0.1:2379",
},
},
{
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadm.API{Port: 123, AdvertiseAddresses: []string{"foo"}},
Networking: kubeadm.Networking{ServiceSubnet: "bar"},
},
expected: []string{
"kube-apiserver",
"--insecure-bind-address=127.0.0.1",
"--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota",
"--service-cluster-ip-range=bar",
"--service-account-key-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/apiserver-key.pem",
"--client-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca.pem",
"--tls-cert-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/apiserver.pem",
"--tls-private-key-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/apiserver-key.pem",
"--token-auth-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/tokens.csv",
fmt.Sprintf("--secure-port=%d", 123),
"--allow-privileged",
"--advertise-address=foo",
"--etcd-servers=http://127.0.0.1:2379",
},
},
{
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadm.API{Port: 123},
Networking: kubeadm.Networking{ServiceSubnet: "bar"},
Etcd: kubeadm.Etcd{CertFile: "fiz", KeyFile: "faz"},
},
expected: []string{
"kube-apiserver",
"--insecure-bind-address=127.0.0.1",
"--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota",
"--service-cluster-ip-range=bar",
"--service-account-key-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/apiserver-key.pem",
"--client-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca.pem",
"--tls-cert-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/apiserver.pem",
"--tls-private-key-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/apiserver-key.pem",
"--token-auth-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/tokens.csv",
fmt.Sprintf("--secure-port=%d", 123),
"--allow-privileged",
"--etcd-servers=http://127.0.0.1:2379",
"--etcd-certfile=fiz",
"--etcd-keyfile=faz",
},
},
// Make sure --kubelet-preferred-address-types
{
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadm.API{Port: 123, AdvertiseAddresses: []string{"foo"}},
Networking: kubeadm.Networking{ServiceSubnet: "bar"},
KubernetesVersion: "v1.5.3",
},
expected: []string{
"kube-apiserver",
"--insecure-bind-address=127.0.0.1",
"--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota",
"--service-cluster-ip-range=bar",
"--service-account-key-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/apiserver-key.pem",
"--client-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca.pem",
"--tls-cert-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/apiserver.pem",
"--tls-private-key-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/apiserver-key.pem",
"--token-auth-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/tokens.csv",
fmt.Sprintf("--secure-port=%d", 123),
"--allow-privileged",
"--advertise-address=foo",
"--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname",
"--anonymous-auth=false",
"--etcd-servers=http://127.0.0.1:2379",
},
},
}
for _, rt := range tests {
actual := getAPIServerCommand(rt.cfg)
for i := range actual {
if actual[i] != rt.expected[i] {
t.Errorf(
"failed getAPIServerCommand:\n\texpected: %s\n\t actual: %s",
rt.expected[i],
actual[i],
)
}
}
}
}
func TestGetControllerManagerCommand(t *testing.T) {
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
expected []string
}{
{
cfg: &kubeadmapi.MasterConfiguration{},
expected: []string{
"kube-controller-manager",
"--address=127.0.0.1",
"--leader-elect",
"--master=127.0.0.1:8080",
"--cluster-name=" + DefaultClusterName,
"--root-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca.pem",
"--service-account-private-key-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/apiserver-key.pem",
"--cluster-signing-cert-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca.pem",
"--cluster-signing-key-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca-key.pem",
"--insecure-experimental-approve-all-kubelet-csrs-for-group=system:kubelet-bootstrap",
},
},
{
cfg: &kubeadmapi.MasterConfiguration{CloudProvider: "foo"},
expected: []string{
"kube-controller-manager",
"--address=127.0.0.1",
"--leader-elect",
"--master=127.0.0.1:8080",
"--cluster-name=" + DefaultClusterName,
"--root-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca.pem",
"--service-account-private-key-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/apiserver-key.pem",
"--cluster-signing-cert-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca.pem",
"--cluster-signing-key-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca-key.pem",
"--insecure-experimental-approve-all-kubelet-csrs-for-group=system:kubelet-bootstrap",
"--cloud-provider=foo",
},
},
{
cfg: &kubeadmapi.MasterConfiguration{Networking: kubeadm.Networking{PodSubnet: "bar"}},
expected: []string{
"kube-controller-manager",
"--address=127.0.0.1",
"--leader-elect",
"--master=127.0.0.1:8080",
"--cluster-name=" + DefaultClusterName,
"--root-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca.pem",
"--service-account-private-key-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/apiserver-key.pem",
"--cluster-signing-cert-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca.pem",
"--cluster-signing-key-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca-key.pem",
"--insecure-experimental-approve-all-kubelet-csrs-for-group=system:kubelet-bootstrap",
"--allocate-node-cidrs=true",
"--cluster-cidr=bar",
},
},
}
for _, rt := range tests {
actual := getControllerManagerCommand(rt.cfg)
for i := range actual {
if actual[i] != rt.expected[i] {
t.Errorf(
"failed getControllerManagerCommand:\n\texpected: %s\n\t actual: %s",
rt.expected[i],
actual[i],
)
}
}
}
}
func TestGetSchedulerCommand(t *testing.T) {
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
expected []string
}{
{
cfg: &kubeadmapi.MasterConfiguration{},
expected: []string{
"kube-scheduler",
"--address=127.0.0.1",
"--leader-elect",
"--master=127.0.0.1:8080",
},
},
}
for _, rt := range tests {
actual := getSchedulerCommand(rt.cfg)
for i := range actual {
if actual[i] != rt.expected[i] {
t.Errorf(
"failed getSchedulerCommand:\n\texpected: %s\n\t actual: %s",
rt.expected[i],
actual[i],
)
}
}
}
}
func TestGetProxyCommand(t *testing.T) {
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
expected []string
}{
{
cfg: &kubeadmapi.MasterConfiguration{},
expected: []string{
"kube-proxy",
},
},
}
for _, rt := range tests {
actual := getProxyCommand(rt.cfg)
for i := range actual {
if actual[i] != rt.expected[i] {
t.Errorf(
"failed getProxyCommand:\n\texpected: %s\n\t actual: %s",
rt.expected[i],
actual[i],
)
}
}
}
}

View file

@ -0,0 +1,41 @@
/*
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 master
import (
"bytes"
"fmt"
"os"
"path"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/util/uuid"
)
func CreateTokenAuthFile(bt string) error {
tokenAuthFilePath := path.Join(kubeadmapi.GlobalEnvParams.HostPKIPath, "tokens.csv")
if err := os.MkdirAll(kubeadmapi.GlobalEnvParams.HostPKIPath, 0700); err != nil {
return fmt.Errorf("failed to create directory %q [%v]", kubeadmapi.GlobalEnvParams.HostPKIPath, err)
}
serialized := []byte(fmt.Sprintf("%s,kubeadm-node-csr,%s,system:kubelet-bootstrap\n", bt, uuid.NewUUID()))
// DumpReaderToFile create a file with mode 0600
if err := cmdutil.DumpReaderToFile(bytes.NewReader(serialized), tokenAuthFilePath); err != nil {
return fmt.Errorf("failed to save token auth file (%q) [%v]", tokenAuthFilePath, err)
}
return nil
}

65
vendor/k8s.io/kubernetes/cmd/kubeadm/app/node/BUILD generated vendored Normal file
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_library(
name = "go_default_library",
srcs = [
"bootstrap.go",
"csr.go",
"discovery.go",
],
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/apis/certificates:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/client/unversioned/clientcmd:go_default_library",
"//pkg/client/unversioned/clientcmd/api:go_default_library",
"//pkg/kubelet/util/csr:go_default_library",
"//pkg/util/cert:go_default_library",
"//vendor:github.com/square/go-jose",
"//vendor:k8s.io/apimachinery/pkg/types",
"//vendor:k8s.io/apimachinery/pkg/util/wait",
],
)
go_test(
name = "go_default_test",
srcs = [
"bootstrap_test.go",
"discovery_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/client/restclient:go_default_library",
"//pkg/client/typed/discovery:go_default_library",
"//pkg/version:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
],
)
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,164 @@
/*
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"
"os"
"sync"
"time"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/apis/certificates"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
)
// retryTimeout between the subsequent attempts to connect
// to an API endpoint
const retryTimeout = 5
type apiClient struct {
clientSet *clientset.Clientset
clientConfig *clientcmdapi.Config
}
// EstablishMasterConnection establishes a connection with exactly one of the provided API endpoints.
// The function builds a client for every endpoint and concurrently keeps trying to connect to any one
// of the provided endpoints. Blocks until at least one connection is established, then it stops the
// connection attempts for other endpoints and returns the valid client configuration, if any.
func EstablishMasterConnection(c *kubeadmapi.TokenDiscovery, clusterInfo *kubeadmapi.ClusterInfo) (*clientcmdapi.Config, error) {
hostName, err := os.Hostname()
if err != nil {
return nil, fmt.Errorf("failed to get node hostname [%v]", err)
}
// TODO(phase1+) https://github.com/kubernetes/kubernetes/issues/33641
nodeName := types.NodeName(hostName)
endpoints := clusterInfo.Endpoints
caCert := []byte(clusterInfo.CertificateAuthorities[0])
stopChan := make(chan struct{})
var clientConfig *clientcmdapi.Config
var once sync.Once
var wg sync.WaitGroup
for _, endpoint := range endpoints {
ac, err := createClients(caCert, endpoint, kubeadmutil.BearerToken(c), nodeName)
if err != nil {
fmt.Printf("[bootstrap] Warning: %s. Skipping endpoint %s\n", err, endpoint)
continue
}
wg.Add(1)
go func(apiEndpoint string) {
defer wg.Done()
wait.Until(func() {
fmt.Printf("[bootstrap] Trying to connect to endpoint %s\n", apiEndpoint)
err := checkAPIEndpoint(ac.clientSet, apiEndpoint)
if err != nil {
fmt.Printf("[bootstrap] Endpoint check failed [%v]\n", err)
return
}
fmt.Printf("[bootstrap] Successfully established connection with endpoint %q\n", apiEndpoint)
// connection established, stop all wait threads
close(stopChan)
once.Do(func() {
clientConfig = ac.clientConfig
})
}, retryTimeout*time.Second, stopChan)
}(endpoint)
}
wg.Wait()
if clientConfig == nil {
return nil, fmt.Errorf("failed to create bootstrap clients for any of the provided API endpoints")
}
return clientConfig, nil
}
// creates a set of clients for this endpoint
func createClients(caCert []byte, endpoint, token string, nodeName types.NodeName) (*apiClient, error) {
clientConfig := kubeconfigphase.MakeClientConfigWithToken(
endpoint,
"kubernetes",
fmt.Sprintf("kubelet-%s", nodeName),
caCert,
token,
)
bootstrapClientConfig, err := clientcmd.NewDefaultClientConfig(*clientConfig, &clientcmd.ConfigOverrides{}).ClientConfig()
if err != nil {
return nil, fmt.Errorf("failed to create API client configuration [%v]", err)
}
clientSet, err := clientset.NewForConfig(bootstrapClientConfig)
if err != nil {
return nil, fmt.Errorf("failed to create clients for the API endpoint %q: [%v]", endpoint, err)
}
ac := &apiClient{
clientSet: clientSet,
clientConfig: clientConfig,
}
return ac, nil
}
// checkForNodeNameDuplicates checks whether there are other nodes in the cluster with identical node names.
func checkForNodeNameDuplicates(clientSet *clientset.Clientset) error {
hostName, err := os.Hostname()
if err != nil {
return fmt.Errorf("Failed to get node hostname [%v]", err)
}
nodeList, err := clientSet.Nodes().List(v1.ListOptions{})
if err != nil {
return fmt.Errorf("Failed to list the nodes in the cluster: [%v]\n", err)
}
for _, node := range nodeList.Items {
if hostName == node.Name {
return fmt.Errorf("Node with name [%q] already exists.", node.Name)
}
}
return nil
}
// checks the connection requirements for a specific API endpoint
func checkAPIEndpoint(clientSet *clientset.Clientset, endpoint string) error {
// check general connectivity
version, err := clientSet.DiscoveryClient.ServerVersion()
if err != nil {
return fmt.Errorf("failed to connect to %q [%v]", endpoint, err)
}
fmt.Printf("[bootstrap] Detected server version: %s\n", version.String())
// check certificates API
serverGroups, err := clientSet.DiscoveryClient.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 == certificates.GroupName {
return nil
}
}
return fmt.Errorf("certificate API check failed: API version %s does not support certificates API, use v1.4.0 or newer",
version.String())
}

View file

@ -0,0 +1,262 @@
/*
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 (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/client/typed/discovery"
"k8s.io/kubernetes/pkg/version"
)
func TestEstablishMasterConnection(t *testing.T) {
expect := version.Info{
Major: "foo",
Minor: "bar",
GitCommit: "baz",
}
srv := 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.4",
},
}
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: "extensions/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)
}
}))
defer srv.Close()
tests := []struct {
c string
e string
expect bool
}{
{
c: "",
e: "",
expect: false,
},
{
c: "",
e: srv.URL,
expect: true,
},
{
c: "foo",
e: srv.URL,
expect: true,
},
}
for _, rt := range tests {
s := &kubeadmapi.TokenDiscovery{}
c := &kubeadmapi.ClusterInfo{Endpoints: []string{rt.e}, CertificateAuthorities: []string{rt.c}}
_, actual := EstablishMasterConnection(s, c)
if (actual == nil) != rt.expect {
t.Errorf(
"failed EstablishMasterConnection:\n\texpected: %t\n\t actual: %t",
rt.expect,
(actual == nil),
)
}
}
}
func TestCreateClients(t *testing.T) {
tests := []struct {
e string
expect bool
}{
{
e: "",
expect: false,
},
{
e: "foo",
expect: true,
},
}
for _, rt := range tests {
_, actual := createClients(nil, rt.e, "", "")
if (actual == nil) != rt.expect {
t.Errorf(
"failed createClients:\n\texpected: %t\n\t actual: %t",
rt.expect,
(actual == nil),
)
}
}
}
func TestCheckAPIEndpoint(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.4",
},
}
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.4",
},
}
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: "extensions/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 New Discovery Client: %v", err)
}
cs := &clientset.Clientset{DiscoveryClient: c}
actual := checkAPIEndpoint(cs, "")
if (actual == nil) != rt.expect {
t.Errorf(
"failed runChecks:\n\texpected: %t\n\t actual: %t",
rt.expect,
(actual == nil),
)
}
}
}

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

@ -0,0 +1,76 @@
/*
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"
"os"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
"k8s.io/kubernetes/pkg/kubelet/util/csr"
certutil "k8s.io/kubernetes/pkg/util/cert"
)
// PerformTLSBootstrap executes a node certificate signing request.
func PerformTLSBootstrap(cfg *clientcmdapi.Config) error {
hostName, err := os.Hostname()
if err != nil {
return err
}
name := types.NodeName(hostName)
rc, err := clientcmd.NewDefaultClientConfig(*cfg, &clientcmd.ConfigOverrides{}).ClientConfig()
if err != nil {
return err
}
c, err := clientset.NewForConfig(rc)
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)
}
// Make sure there are no other nodes in the cluster with identical node name.
if err := checkForNodeNameDuplicates(c); err != nil {
return err
}
cert, err := csr.RequestNodeCertificate(c.Certificates().CertificateSigningRequests(), key, name)
if err != nil {
return fmt.Errorf("failed to request signed certificate from the API server [%v]", err)
}
fmt.Printf("[csr] Received signed certificate from the API server")
fmt.Println("[csr] Generating kubelet configuration")
cfg.AuthInfos["kubelet"] = &clientcmdapi.AuthInfo{
ClientKeyData: key,
ClientCertificateData: cert,
}
cfg.Contexts["kubelet"] = &clientcmdapi.Context{
AuthInfo: "kubelet",
Cluster: cfg.Contexts[cfg.CurrentContext].Cluster,
}
cfg.CurrentContext = "kubelet"
return nil
}

View file

@ -0,0 +1,84 @@
/*
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 (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
jose "github.com/square/go-jose"
"k8s.io/apimachinery/pkg/util/wait"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
// the amount of time to wait between each request to the discovery API
const discoveryRetryTimeout = 5 * time.Second
func RetrieveTrustedClusterInfo(d *kubeadmapi.TokenDiscovery) (*kubeadmapi.ClusterInfo, error) {
requestURL := fmt.Sprintf("http://%s/cluster-info/v1/?token-id=%s", d.Addresses[0], d.ID)
req, err := http.NewRequest("GET", requestURL, nil)
if err != nil {
return nil, fmt.Errorf("failed to consturct an HTTP request [%v]", err)
}
fmt.Printf("[discovery] Created cluster info discovery client, requesting info from %q\n", requestURL)
var res *http.Response
wait.PollInfinite(discoveryRetryTimeout, func() (bool, error) {
res, err = http.DefaultClient.Do(req)
if err != nil {
fmt.Printf("[discovery] Failed to request cluster info, will try again: [%s]\n", err)
return false, nil
}
return true, nil
})
buf := new(bytes.Buffer)
io.Copy(buf, res.Body)
res.Body.Close()
object, err := jose.ParseSigned(buf.String())
if err != nil {
return nil, fmt.Errorf("failed to parse response as JWS object [%v]", err)
}
fmt.Println("[discovery] Cluster info object received, verifying signature using given token")
output, err := object.Verify([]byte(d.Secret))
if err != nil {
return nil, fmt.Errorf("failed to verify JWS signature of received cluster info object [%v]", err)
}
clusterInfo := kubeadmapi.ClusterInfo{}
if err := json.Unmarshal(output, &clusterInfo); err != nil {
return nil, fmt.Errorf("failed to decode received cluster info object [%v]", err)
}
if len(clusterInfo.CertificateAuthorities) == 0 || len(clusterInfo.Endpoints) == 0 {
return nil, fmt.Errorf("cluster info object is invalid - no endpoint(s) and/or root CA certificate(s) found")
}
// TODO(phase1+) 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
fmt.Printf("[discovery] Cluster info signature and contents are valid, will use API endpoints %v\n", clusterInfo.Endpoints)
return &clusterInfo, nil
}

View file

@ -0,0 +1,99 @@
/*
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 (
"encoding/json"
"net"
"net/http"
"net/http/httptest"
"net/url"
"strconv"
"testing"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
type rawJsonWebSignatureFake struct {
Payload string `json:"payload,omitempty"`
Signatures string `json:"signatures,omitempty"`
Protected string `json:"protected,omitempty"`
Header string `json:"header,omitempty"`
Signature string `json:"signature,omitempty"`
}
func TestRetrieveTrustedClusterInfo(t *testing.T) {
j := rawJsonWebSignatureFake{}
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
switch req.URL.Path {
default:
output, err := json.Marshal(j)
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)
}
}))
defer srv.Close()
pURL, err := url.Parse(srv.URL)
if err != nil {
t.Fatalf("encountered an error while trying to parse httptest server url: %v", err)
}
host, port, err := net.SplitHostPort(pURL.Host)
if err != nil {
t.Fatalf("encountered an error while trying to split host and port info from httptest server: %v", err)
}
iPort, err := strconv.Atoi(port)
if err != nil {
t.Fatalf("encountered an error while trying to convert string to int (httptest server port): %v", err)
}
tests := []struct {
h string
p int
payload string
expect bool
}{
{
h: host,
p: iPort,
payload: "",
expect: false,
},
{
h: host,
p: iPort,
payload: "foo",
expect: false,
},
}
for _, rt := range tests {
j.Payload = rt.payload
nc := &kubeadmapi.TokenDiscovery{Addresses: []string{rt.h + ":" + strconv.Itoa(rt.p)}}
_, actual := RetrieveTrustedClusterInfo(nc)
if (actual == nil) != rt.expect {
t.Errorf(
"failed createClients:\n\texpected: %t\n\t actual: %t",
rt.expect,
(actual == nil),
)
}
}
}

View file

@ -0,0 +1,51 @@
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 = [
"certs_test.go",
"pki_helpers_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//pkg/util/cert:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"certs.go",
"doc.go",
"pki_helpers.go",
],
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//pkg/registry/core/service/ipallocator:go_default_library",
"//pkg/util/cert: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,102 @@
/*
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 certs
import (
"crypto/x509"
"fmt"
"net"
"os"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
certutil "k8s.io/kubernetes/pkg/util/cert"
)
// CreatePKIAssets will create and write to disk all PKI assets necessary to establish the control plane.
// It first generates a self-signed CA certificate, a server certificate (signed by the CA) and a key for
// signing service account tokens. It returns CA key and certificate, which is convenient for use with
// client config funcs.
func CreatePKIAssets(cfg *kubeadmapi.MasterConfiguration, pkiPath string) (*x509.Certificate, error) {
altNames := certutil.AltNames{}
// First, define all domains this cert should be signed for
internalAPIServerFQDN := []string{
"kubernetes",
"kubernetes.default",
"kubernetes.default.svc",
fmt.Sprintf("kubernetes.default.svc.%s", cfg.Networking.DNSDomain),
}
hostname, err := os.Hostname()
if err != nil {
return nil, fmt.Errorf("couldn't get the hostname: %v", err)
}
altNames.DNSNames = append(cfg.API.ExternalDNSNames, hostname)
altNames.DNSNames = append(altNames.DNSNames, internalAPIServerFQDN...)
// then, add all IP addresses we're bound to
for _, a := range cfg.API.AdvertiseAddresses {
if ip := net.ParseIP(a); ip != nil {
altNames.IPs = append(altNames.IPs, ip)
} else {
return nil, fmt.Errorf("could not parse ip %q", a)
}
}
// and lastly, extract the internal IP address for the API server
_, n, err := net.ParseCIDR(cfg.Networking.ServiceSubnet)
if err != nil {
return nil, fmt.Errorf("error parsing CIDR %q: %v", cfg.Networking.ServiceSubnet, err)
}
internalAPIServerVirtualIP, err := ipallocator.GetIndexedIP(n, 1)
if err != nil {
return nil, fmt.Errorf("unable to allocate IP address for the API server from the given CIDR (%q) [%v]", &cfg.Networking.ServiceSubnet, err)
}
altNames.IPs = append(altNames.IPs, internalAPIServerVirtualIP)
caKey, caCert, err := newCertificateAuthority()
if err != nil {
return nil, fmt.Errorf("failure while creating CA keys and certificate [%v]", err)
}
if err := writeKeysAndCert(pkiPath, "ca", caKey, caCert); err != nil {
return nil, fmt.Errorf("failure while saving CA keys and certificate [%v]", err)
}
fmt.Println("[certificates] Generated Certificate Authority key and certificate.")
apiKey, apiCert, err := newServerKeyAndCert(caCert, caKey, altNames)
if err != nil {
return nil, fmt.Errorf("failure while creating API server keys and certificate [%v]", err)
}
if err := writeKeysAndCert(pkiPath, "apiserver", apiKey, apiCert); err != nil {
return nil, fmt.Errorf("failure while saving API server keys and certificate [%v]", err)
}
fmt.Println("[certificates] Generated API Server key and certificate")
// Generate a private key for service accounts
saKey, err := certutil.NewPrivateKey()
if err != nil {
return nil, fmt.Errorf("failure while creating service account signing keys [%v]", err)
}
if err := writeKeysAndCert(pkiPath, "sa", saKey, nil); err != nil {
return nil, fmt.Errorf("failure while saving service account signing keys [%v]", err)
}
fmt.Println("[certificates] Generated Service Account signing keys")
fmt.Printf("[certificates] Created keys and certificates in %q\n", pkiPath)
return caCert, nil
}

View file

@ -0,0 +1,77 @@
/*
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 certs
import (
"fmt"
"io/ioutil"
"os"
"testing"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func TestCreatePKIAssets(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("Couldn't create tmpdir")
}
defer os.Remove(tmpdir)
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
expected bool
}{
{
cfg: &kubeadmapi.MasterConfiguration{},
expected: false,
},
{
// CIDR too small
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadmapi.API{AdvertiseAddresses: []string{"10.0.0.1"}},
Networking: kubeadmapi.Networking{ServiceSubnet: "10.0.0.1/1"},
},
expected: false,
},
{
// CIDR invalid
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadmapi.API{AdvertiseAddresses: []string{"10.0.0.1"}},
Networking: kubeadmapi.Networking{ServiceSubnet: "invalid"},
},
expected: false,
},
{
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadmapi.API{AdvertiseAddresses: []string{"10.0.0.1"}},
Networking: kubeadmapi.Networking{ServiceSubnet: "10.0.0.1/24"},
},
expected: true,
},
}
for _, rt := range tests {
_, actual := CreatePKIAssets(rt.cfg, fmt.Sprintf("%s/etc/kubernetes/pki", tmpdir))
if (actual == nil) != rt.expected {
t.Errorf(
"failed CreatePKIAssets with an error:\n\texpected: %t\n\t actual: %t",
rt.expected,
(actual == nil),
)
}
}
}

View file

@ -0,0 +1,41 @@
/*
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 certs
/*
PHASE: CERTIFICATES
INPUTS:
From MasterConfiguration
.API.AdvertiseAddresses is needed for knowing which IPs the certs should be signed for
.API.ExternalDNSNames is needed for knowing which DNS names the certs should be signed for
.Networking.DNSDomain is needed for knowing which DNS name the internal kubernetes service has
.Networking.ServiceSubnet is needed for knowing which IP the internal kubernetes service is going to point to
The PKIPath is required for knowing where all certificates should be stored
OUTPUTS:
Files to PKIPath (default /etc/kubernetes/pki):
- apiserver-key.pem
- apiserver-pub.pem
- apiserver.pem
- ca-key.pem
- ca-pub.pem
- ca.pem
- sa-key.pem
- sa-pub.pem
*/

View file

@ -0,0 +1,110 @@
/*
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 certs
import (
"crypto/rsa"
"crypto/x509"
"fmt"
"path"
certutil "k8s.io/kubernetes/pkg/util/cert"
)
func newCertificateAuthority() (*rsa.PrivateKey, *x509.Certificate, error) {
key, err := certutil.NewPrivateKey()
if err != nil {
return nil, nil, fmt.Errorf("unable to create private key [%v]", err)
}
config := certutil.Config{
CommonName: "kubernetes",
}
cert, err := certutil.NewSelfSignedCACert(config, key)
if err != nil {
return nil, nil, fmt.Errorf("unable to create self-signed certificate [%v]", err)
}
return key, cert, nil
}
func newServerKeyAndCert(caCert *x509.Certificate, caKey *rsa.PrivateKey, altNames certutil.AltNames) (*rsa.PrivateKey, *x509.Certificate, error) {
key, err := certutil.NewPrivateKey()
if err != nil {
return nil, nil, fmt.Errorf("unable to create private key [%v]", err)
}
config := certutil.Config{
CommonName: "kube-apiserver",
AltNames: altNames,
}
cert, err := certutil.NewSignedCert(config, key, caCert, caKey)
if err != nil {
return nil, nil, fmt.Errorf("unable to sign certificate [%v]", err)
}
return key, cert, nil
}
func NewClientKeyAndCert(caCert *x509.Certificate, caKey *rsa.PrivateKey) (*rsa.PrivateKey, *x509.Certificate, error) {
key, err := certutil.NewPrivateKey()
if err != nil {
return nil, nil, fmt.Errorf("unable to create private key [%v]", err)
}
config := certutil.Config{
CommonName: "kubernetes-client",
}
cert, err := certutil.NewSignedCert(config, key, caCert, caKey)
if err != nil {
return nil, nil, fmt.Errorf("unable to sign certificate [%v]", err)
}
return key, cert, nil
}
func writeKeysAndCert(pkiPath string, name string, key *rsa.PrivateKey, cert *x509.Certificate) error {
publicKeyPath, privateKeyPath, certificatePath := pathsKeysCerts(pkiPath, name)
if key != nil {
if err := certutil.WriteKey(privateKeyPath, certutil.EncodePrivateKeyPEM(key)); err != nil {
return fmt.Errorf("unable to write private key file (%q) [%v]", privateKeyPath, err)
}
if pubKey, err := certutil.EncodePublicKeyPEM(&key.PublicKey); err == nil {
if err := certutil.WriteKey(publicKeyPath, pubKey); err != nil {
return fmt.Errorf("unable to write public key file (%q) [%v]", publicKeyPath, err)
}
} else {
return fmt.Errorf("unable to encode public key to PEM [%v]", err)
}
}
if cert != nil {
if err := certutil.WriteCert(certificatePath, certutil.EncodeCertPEM(cert)); err != nil {
return fmt.Errorf("unable to write certificate file (%q) [%v]", certificatePath, err)
}
}
return nil
}
func pathsKeysCerts(pkiPath, name string) (string, string, string) {
return path.Join(pkiPath, fmt.Sprintf("%s-pub.pem", name)),
path.Join(pkiPath, fmt.Sprintf("%s-key.pem", name)),
path.Join(pkiPath, fmt.Sprintf("%s.pem", name))
}

View file

@ -0,0 +1,171 @@
/*
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 certs
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"io/ioutil"
"os"
"testing"
certutil "k8s.io/kubernetes/pkg/util/cert"
)
func TestNewCertificateAuthority(t *testing.T) {
r, x, err := newCertificateAuthority()
if r == nil {
t.Errorf(
"failed newCertificateAuthority, rsa key == nil",
)
}
if x == nil {
t.Errorf(
"failed newCertificateAuthority, x509 cert == nil",
)
}
if err != nil {
t.Errorf(
"failed newCertificateAuthority with an error: %v",
err,
)
}
}
func TestNewServerKeyAndCert(t *testing.T) {
var tests = []struct {
caKeySize int
expected bool
}{
{
// RSA key too small
caKeySize: 128,
expected: false,
},
{
// Should succeed
caKeySize: 2048,
expected: true,
},
}
for _, rt := range tests {
caKey, err := rsa.GenerateKey(rand.Reader, rt.caKeySize)
if err != nil {
t.Fatalf("Couldn't create rsa Private Key")
}
caCert := &x509.Certificate{}
altNames := certutil.AltNames{}
_, _, actual := newServerKeyAndCert(caCert, caKey, altNames)
if (actual == nil) != rt.expected {
t.Errorf(
"failed newServerKeyAndCert:\n\texpected: %t\n\t actual: %t",
rt.expected,
(actual == nil),
)
}
}
}
func TestNewClientKeyAndCert(t *testing.T) {
var tests = []struct {
caKeySize int
expected bool
}{
{
// RSA key too small
caKeySize: 128,
expected: false,
},
{
caKeySize: 2048,
expected: true,
},
}
for _, rt := range tests {
caKey, err := rsa.GenerateKey(rand.Reader, rt.caKeySize)
if err != nil {
t.Fatalf("Couldn't create rsa Private Key")
}
caCert := &x509.Certificate{}
_, _, actual := NewClientKeyAndCert(caCert, caKey)
if (actual == nil) != rt.expected {
t.Errorf(
"failed NewClientKeyAndCert:\n\texpected: %t\n\t actual: %t",
rt.expected,
(actual == nil),
)
}
}
}
func TestWriteKeysAndCert(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("Couldn't create tmpdir")
}
defer os.Remove(tmpdir)
caKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatalf("Couldn't create rsa Private Key")
}
caCert := &x509.Certificate{}
actual := writeKeysAndCert(tmpdir, "foo", caKey, caCert)
if actual != nil {
t.Errorf(
"failed writeKeysAndCert with an error: %v",
actual,
)
}
}
func TestPathsKeysCerts(t *testing.T) {
var tests = []struct {
pkiPath string
name string
expected []string
}{
{
pkiPath: "foo",
name: "bar",
expected: []string{"foo/bar-pub.pem", "foo/bar-key.pem", "foo/bar.pem"},
},
{
pkiPath: "bar",
name: "foo",
expected: []string{"bar/foo-pub.pem", "bar/foo-key.pem", "bar/foo.pem"},
},
}
for _, rt := range tests {
a, b, c := pathsKeysCerts(rt.pkiPath, rt.name)
all := []string{a, b, c}
for i := range all {
if all[i] != rt.expected[i] {
t.Errorf(
"failed pathsKeysCerts:\n\texpected: %s\n\t actual: %s",
rt.expected[i],
all[i],
)
}
}
}
}

View file

@ -0,0 +1,45 @@
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 = ["kubeconfig_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = ["//cmd/kubeadm/app/apis/kubeadm:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"kubeconfig.go",
],
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/phases/certs:go_default_library",
"//pkg/client/unversioned/clientcmd:go_default_library",
"//pkg/client/unversioned/clientcmd/api:go_default_library",
"//pkg/util/cert: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,33 @@
/*
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 kubeconfig
/*
PHASE: KUBECONFIG
INPUTS:
From MasterConfiguration
(.API.AdvertiseAddresses is currently needed for knowing the) MasterAPIEndpoint is required so the KubeConfig file knows where to find the master
The KubernetesDir path is required for knowing where to put the KubeConfig files
The PKIPath is required for knowing where all certificates should be stored
OUTPUTS:
Files to KubernetesDir (default /etc/kubernetes):
- admin.conf
- kubelet.conf
*/

View file

@ -0,0 +1,164 @@
/*
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 kubeconfig
import (
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"fmt"
"os"
"path"
certphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
certutil "k8s.io/kubernetes/pkg/util/cert"
)
const (
KubernetesDirPermissions = 0700
AdminKubeConfigFileName = "admin.conf"
KubeletKubeConfigFileName = "kubelet.conf"
)
// This function is called from the main init and does the work for the default phase behaviour
// TODO: Make an integration test for this function that runs after the certificates phase
// and makes sure that those two phases work well together...
func CreateAdminAndKubeletKubeConfig(masterEndpoint, pkiDir, outDir string) error {
// Parse the certificate from a file
caCertPath := path.Join(pkiDir, "ca.pem")
caCerts, err := certutil.CertsFromFile(caCertPath)
if err != nil {
return fmt.Errorf("couldn't load the CA cert file %s: %v", caCertPath, err)
}
// We are only putting one certificate in the CA certificate pem file, so it's safe to just use the first one
caCert := caCerts[0]
// Parse the rsa private key from a file
caKeyPath := path.Join(pkiDir, "ca-key.pem")
priv, err := certutil.PrivateKeyFromFile(caKeyPath)
if err != nil {
return fmt.Errorf("couldn't load the CA private key file %s: %v", caKeyPath, err)
}
var caKey *rsa.PrivateKey
switch k := priv.(type) {
case *rsa.PrivateKey:
caKey = k
case *ecdsa.PrivateKey:
// TODO: Abstract rsa.PrivateKey away and make certutil.NewSignedCert accept a ecdsa.PrivateKey as well
// After that, we can support generating kubeconfig files from ecdsa private keys as well
return fmt.Errorf("the CA private key file %s isn't in RSA format", caKeyPath)
default:
return fmt.Errorf("the CA private key file %s isn't in RSA format", caKeyPath)
}
// User admin should have full access to the cluster
if err := createKubeConfigFileForClient(masterEndpoint, "admin", outDir, caCert, caKey); err != nil {
return fmt.Errorf("couldn't create a kubeconfig file for admin: %v", err)
}
// TODO: The kubelet should have limited access to the cluster
if err := createKubeConfigFileForClient(masterEndpoint, "kubelet", outDir, caCert, caKey); err != nil {
return fmt.Errorf("couldn't create a kubeconfig file for kubelet: %v", err)
}
return nil
}
func createKubeConfigFileForClient(masterEndpoint, client, outDir string, caCert *x509.Certificate, caKey *rsa.PrivateKey) error {
key, cert, err := certphase.NewClientKeyAndCert(caCert, caKey)
if err != nil {
return fmt.Errorf("failure while creating %s client certificate [%v]", client, err)
}
config := MakeClientConfigWithCerts(
masterEndpoint,
"kubernetes",
client,
certutil.EncodeCertPEM(caCert),
certutil.EncodePrivateKeyPEM(key),
certutil.EncodeCertPEM(cert),
)
// Write it now to a file
filepath := path.Join(outDir, fmt.Sprintf("%s.conf", client))
return WriteKubeconfigToDisk(filepath, config)
}
func WriteKubeconfigToDisk(filepath string, kubeconfig *clientcmdapi.Config) error {
// Make sure the dir exists or can be created
if err := os.MkdirAll(path.Dir(filepath), KubernetesDirPermissions); err != nil {
return fmt.Errorf("failed to create directory %q [%v]", path.Dir(filepath), err)
}
// If err == nil, the file exists. Oops, we don't allow the file to exist already, fail.
if _, err := os.Stat(filepath); err == nil {
return fmt.Errorf("kubeconfig file %s already exists, but must not exist.", filepath)
}
if err := clientcmd.WriteToFile(*kubeconfig, filepath); err != nil {
return fmt.Errorf("failed to write to %q [%v]", filepath, err)
}
fmt.Printf("[kubeconfig] Wrote KubeConfig file to disk: %q\n", filepath)
return nil
}
func createBasicClientConfig(serverURL string, clusterName string, userName string, caCert []byte) *clientcmdapi.Config {
config := clientcmdapi.NewConfig()
// Make a new cluster, specify the endpoint we'd like to talk to and the ca cert we're gonna use
cluster := clientcmdapi.NewCluster()
cluster.Server = serverURL
cluster.CertificateAuthorityData = caCert
// Specify a context where we're using that cluster and the username as the auth information
contextName := fmt.Sprintf("%s@%s", userName, clusterName)
context := clientcmdapi.NewContext()
context.Cluster = clusterName
context.AuthInfo = userName
// Lastly, apply the created objects above to the config
config.Clusters[clusterName] = cluster
config.Contexts[contextName] = context
config.CurrentContext = contextName
return config
}
// Creates a clientcmdapi.Config object with access to the API server with client certificates
func MakeClientConfigWithCerts(serverURL, clusterName, userName string, caCert []byte, clientKey []byte, clientCert []byte) *clientcmdapi.Config {
config := createBasicClientConfig(serverURL, clusterName, userName, caCert)
authInfo := clientcmdapi.NewAuthInfo()
authInfo.ClientKeyData = clientKey
authInfo.ClientCertificateData = clientCert
config.AuthInfos[userName] = authInfo
return config
}
// Creates a clientcmdapi.Config object with access to the API server with a token
func MakeClientConfigWithToken(serverURL, clusterName, userName string, caCert []byte, token string) *clientcmdapi.Config {
config := createBasicClientConfig(serverURL, clusterName, userName, caCert)
authInfo := clientcmdapi.NewAuthInfo()
authInfo.Token = token
config.AuthInfos[userName] = authInfo
return config
}

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 kubeconfig
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
const (
configOut1 = `apiVersion: v1
clusters:
- cluster:
server: ""
name: k8s
contexts:
- context:
cluster: k8s
user: user1
name: user1@k8s
current-context: user1@k8s
kind: Config
preferences: {}
users:
- name: user1
user:
token: abc
`
configOut2 = `apiVersion: v1
clusters:
- cluster:
server: localhost:8080
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: user2
name: user2@kubernetes
current-context: user2@kubernetes
kind: Config
preferences: {}
users:
- name: user2
user:
token: cba
`
)
type configClient struct {
clusterName string
userName string
serverURL string
caCert []byte
}
type configClientWithCerts struct {
clientKey []byte
clientCert []byte
}
type configClientWithToken struct {
token string
}
func TestMakeClientConfigWithCerts(t *testing.T) {
var createBasicTest = []struct {
cc configClient
ccWithCerts configClientWithCerts
expected string
}{
{configClient{}, configClientWithCerts{}, ""},
{configClient{clusterName: "kubernetes"}, configClientWithCerts{}, ""},
}
for _, rt := range createBasicTest {
cwc := MakeClientConfigWithCerts(
rt.cc.serverURL,
rt.cc.clusterName,
rt.cc.userName,
rt.cc.caCert,
rt.ccWithCerts.clientKey,
rt.ccWithCerts.clientCert,
)
if cwc.Kind != rt.expected {
t.Errorf(
"failed MakeClientConfigWithCerts:\n\texpected: %s\n\t actual: %s",
rt.expected,
cwc.Kind,
)
}
}
}
func TestMakeClientConfigWithToken(t *testing.T) {
var createBasicTest = []struct {
cc configClient
ccWithToken configClientWithToken
expected string
}{
{configClient{}, configClientWithToken{}, ""},
{configClient{clusterName: "kubernetes"}, configClientWithToken{}, ""},
}
for _, rt := range createBasicTest {
cwc := MakeClientConfigWithToken(
rt.cc.serverURL,
rt.cc.clusterName,
rt.cc.userName,
rt.cc.caCert,
rt.ccWithToken.token,
)
if cwc.Kind != rt.expected {
t.Errorf(
"failed MakeClientConfigWithCerts:\n\texpected: %s\n\t actual: %s",
rt.expected,
cwc.Kind,
)
}
}
}
func TestWriteKubeconfigToDisk(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("Couldn't create tmpdir")
}
defer os.Remove(tmpdir)
// set up tmp GlobalEnvParams values for testing
oldEnv := kubeadmapi.GlobalEnvParams
kubeadmapi.GlobalEnvParams = kubeadmapi.SetEnvParams()
kubeadmapi.GlobalEnvParams.KubernetesDir = fmt.Sprintf("%s/etc/kubernetes", tmpdir)
defer func() { kubeadmapi.GlobalEnvParams = oldEnv }()
var writeConfig = []struct {
name string
cc configClient
ccWithToken configClientWithToken
expected error
file []byte
}{
{"test1", configClient{clusterName: "k8s", userName: "user1"}, configClientWithToken{token: "abc"}, nil, []byte(configOut1)},
{"test2", configClient{clusterName: "kubernetes", userName: "user2", serverURL: "localhost:8080"}, configClientWithToken{token: "cba"}, nil, []byte(configOut2)},
}
for _, rt := range writeConfig {
c := MakeClientConfigWithToken(
rt.cc.serverURL,
rt.cc.clusterName,
rt.cc.userName,
rt.cc.caCert,
rt.ccWithToken.token,
)
configPath := filepath.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, fmt.Sprintf("%s.conf", rt.name))
err := WriteKubeconfigToDisk(configPath, c)
if err != rt.expected {
t.Errorf(
"failed WriteKubeconfigToDisk with an error:\n\texpected: %s\n\t actual: %s",
rt.expected,
err,
)
}
newFile, err := ioutil.ReadFile(configPath)
if !bytes.Equal(newFile, rt.file) {
t.Errorf(
"failed WriteKubeconfigToDisk config write:\n\texpected: %s\n\t actual: %s",
rt.file,
newFile,
)
}
}
}

View file

@ -0,0 +1,43 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["checks.go"],
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//pkg/api/validation:go_default_library",
"//pkg/util/initsystem:go_default_library",
"//pkg/util/node:go_default_library",
"//test/e2e_node/system:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/util/errors",
],
)
go_test(
name = "go_default_test",
srcs = ["checks_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
)
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,416 @@
/*
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 preflight
import (
"bufio"
"bytes"
"fmt"
"io"
"net"
"net/http"
"os"
"os/exec"
"path"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/util/initsystem"
"k8s.io/kubernetes/pkg/util/node"
"k8s.io/kubernetes/test/e2e_node/system"
)
const bridgenf string = "/proc/sys/net/bridge/bridge-nf-call-iptables"
type Error struct {
Msg string
}
func (e *Error) Error() string {
return fmt.Sprintf("[preflight] Some fatal errors occurred:\n%s%s", e.Msg, "[preflight] If you know what you are doing, you can skip pre-flight checks with `--skip-preflight-checks`")
}
// Checker validates the state of the system to ensure kubeadm will be
// successful as often as possilble.
type Checker interface {
Check() (warnings, errors []error)
}
// ServiceCheck verifies that the given service is enabled and active. If we do not
// detect a supported init system however, all checks are skipped and a warning is
// returned.
type ServiceCheck struct {
Service string
CheckIfActive bool
}
func (sc ServiceCheck) Check() (warnings, errors []error) {
initSystem, err := initsystem.GetInitSystem()
if err != nil {
return []error{err}, nil
}
warnings = []error{}
if !initSystem.ServiceExists(sc.Service) {
warnings = append(warnings, fmt.Errorf("%s service does not exist", sc.Service))
return warnings, nil
}
if !initSystem.ServiceIsEnabled(sc.Service) {
warnings = append(warnings,
fmt.Errorf("%s service is not enabled, please run 'systemctl enable %s.service'",
sc.Service, sc.Service))
}
if sc.CheckIfActive && !initSystem.ServiceIsActive(sc.Service) {
errors = append(errors,
fmt.Errorf("%s service is not active, please run 'systemctl start %s.service'",
sc.Service, sc.Service))
}
return warnings, errors
}
// FirewalldCheck checks if firewalld is enabled or active, and if so outputs a warning.
type FirewalldCheck struct {
ports []int
}
func (fc FirewalldCheck) Check() (warnings, errors []error) {
initSystem, err := initsystem.GetInitSystem()
if err != nil {
return []error{err}, nil
}
warnings = []error{}
if !initSystem.ServiceExists("firewalld") {
return nil, nil
}
if initSystem.ServiceIsActive("firewalld") {
warnings = append(warnings,
fmt.Errorf("firewalld is active, please ensure ports %v are open or your cluster may not function correctly",
fc.ports))
}
return warnings, errors
}
// PortOpenCheck ensures the given port is available for use.
type PortOpenCheck struct {
port int
}
func (poc PortOpenCheck) Check() (warnings, errors []error) {
errors = []error{}
// TODO: Get IP from KubeadmConfig
ln, err := net.Listen("tcp", fmt.Sprintf(":%d", poc.port))
if err != nil {
errors = append(errors, fmt.Errorf("Port %d is in use", poc.port))
}
if ln != nil {
ln.Close()
}
return nil, errors
}
// IsRootCheck verifies user is root
type IsRootCheck struct{}
func (irc IsRootCheck) Check() (warnings, errors []error) {
errors = []error{}
if os.Getuid() != 0 {
errors = append(errors, fmt.Errorf("user is not running as root"))
}
return nil, errors
}
// DirAvailableCheck checks if the given directory either does not exist, or is empty.
type DirAvailableCheck struct {
Path string
}
func (dac DirAvailableCheck) Check() (warnings, errors []error) {
errors = []error{}
// If it doesn't exist we are good:
if _, err := os.Stat(dac.Path); os.IsNotExist(err) {
return nil, nil
}
f, err := os.Open(dac.Path)
if err != nil {
errors = append(errors, fmt.Errorf("unable to check if %s is empty: %s", dac.Path, err))
return nil, errors
}
defer f.Close()
_, err = f.Readdirnames(1)
if err != io.EOF {
errors = append(errors, fmt.Errorf("%s is not empty", dac.Path))
}
return nil, errors
}
// FileAvailableCheck checks that the given file does not already exist.
type FileAvailableCheck struct {
Path string
}
func (fac FileAvailableCheck) Check() (warnings, errors []error) {
errors = []error{}
if _, err := os.Stat(fac.Path); err == nil {
errors = append(errors, fmt.Errorf("%s already exists", fac.Path))
}
return nil, errors
}
// FileContentCheck checks that the given file contains the string Content.
type FileContentCheck struct {
Path string
Content []byte
}
func (fcc FileContentCheck) Check() (warnings, errors []error) {
f, err := os.Open(fcc.Path)
if err != nil {
return nil, []error{fmt.Errorf("%s does not exist", fcc.Path)}
}
lr := io.LimitReader(f, int64(len(fcc.Content)))
defer f.Close()
buf := &bytes.Buffer{}
_, err = io.Copy(buf, lr)
if err != nil {
return nil, []error{fmt.Errorf("%s could not be read", fcc.Path)}
}
if !bytes.Equal(buf.Bytes(), fcc.Content) {
return nil, []error{fmt.Errorf("%s contents are not set to %s", fcc.Path, fcc.Content)}
}
return nil, []error{}
}
// InPathCheck checks if the given executable is present in the path.
type InPathCheck struct {
executable string
mandatory bool
}
func (ipc InPathCheck) Check() (warnings, errors []error) {
_, err := exec.LookPath(ipc.executable)
if err != nil {
if ipc.mandatory {
// Return as an error:
return nil, []error{fmt.Errorf("%s not found in system path", ipc.executable)}
}
// Return as a warning:
return []error{fmt.Errorf("%s not found in system path", ipc.executable)}, nil
}
return nil, nil
}
// HostnameCheck checks if hostname match dns sub domain regex.
// If hostname doesn't match this regex, kubelet will not launch static pods like kube-apiserver/kube-controller-manager and so on.
type HostnameCheck struct{}
func (hc HostnameCheck) Check() (warnings, errors []error) {
errors = []error{}
hostname := node.GetHostname("")
for _, msg := range validation.ValidateNodeName(hostname, false) {
errors = append(errors, fmt.Errorf("hostname \"%s\" %s", hostname, msg))
}
return nil, errors
}
// HTTPProxyCheck checks if https connection to specific host is going
// to be done directly or over proxy. If proxy detected, it will return warning.
type HTTPProxyCheck struct {
Proto string
Host string
Port int
}
func (hst HTTPProxyCheck) Check() (warnings, errors []error) {
url := fmt.Sprintf("%s://%s:%d", hst.Proto, hst.Host, hst.Port)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, []error{err}
}
proxy, err := http.DefaultTransport.(*http.Transport).Proxy(req)
if err != nil {
return nil, []error{err}
}
if proxy != nil {
return []error{fmt.Errorf("Connection to %q uses proxy %q. If that is not intended, adjust your proxy settings", url, proxy)}, nil
}
return nil, nil
}
type SystemVerificationCheck struct{}
func (sysver SystemVerificationCheck) Check() (warnings, errors []error) {
// Create a buffered writer and choose a quite large value (1M) and suppose the output from the system verification test won't exceed the limit
// Run the system verification check, but write to out buffered writer instead of stdout
bufw := bufio.NewWriterSize(os.Stdout, 1*1024*1024)
reporter := &system.StreamReporter{WriteStream: bufw}
var errs []error
// All the validators we'd like to run:
var validators = []system.Validator{
&system.OSValidator{Reporter: reporter},
&system.KernelValidator{Reporter: reporter},
&system.CgroupsValidator{Reporter: reporter},
&system.DockerValidator{Reporter: reporter},
}
// Run all validators
for _, v := range validators {
errs = append(errs, v.Validate(system.DefaultSysSpec))
}
err := utilerrors.NewAggregate(errs)
if err != nil {
// Only print the output from the system verification check if the check failed
fmt.Println("[preflight] The system verification failed. Printing the output from the verification:")
bufw.Flush()
return nil, []error{err}
}
return nil, nil
}
func RunInitMasterChecks(cfg *kubeadmapi.MasterConfiguration) error {
checks := []Checker{
SystemVerificationCheck{},
IsRootCheck{},
HostnameCheck{},
ServiceCheck{Service: "kubelet", CheckIfActive: false},
ServiceCheck{Service: "docker", CheckIfActive: true},
FirewalldCheck{ports: []int{int(cfg.API.Port), 10250}},
PortOpenCheck{port: int(cfg.API.Port)},
PortOpenCheck{port: 8080},
PortOpenCheck{port: 10250},
PortOpenCheck{port: 10251},
PortOpenCheck{port: 10252},
HTTPProxyCheck{Proto: "https", Host: cfg.API.AdvertiseAddresses[0], Port: int(cfg.API.Port)},
DirAvailableCheck{Path: path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "manifests")},
DirAvailableCheck{Path: kubeadmapi.GlobalEnvParams.HostPKIPath},
DirAvailableCheck{Path: "/var/lib/kubelet"},
FileAvailableCheck{Path: path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "admin.conf")},
FileAvailableCheck{Path: path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "kubelet.conf")},
FileContentCheck{Path: bridgenf, Content: []byte{'1'}},
InPathCheck{executable: "ip", mandatory: true},
InPathCheck{executable: "iptables", mandatory: true},
InPathCheck{executable: "mount", mandatory: true},
InPathCheck{executable: "nsenter", mandatory: true},
InPathCheck{executable: "ebtables", mandatory: false},
InPathCheck{executable: "ethtool", mandatory: false},
InPathCheck{executable: "socat", mandatory: false},
InPathCheck{executable: "tc", mandatory: false},
InPathCheck{executable: "touch", mandatory: false},
}
if len(cfg.Etcd.Endpoints) == 0 {
// Only do etcd related checks when no external endpoints were specified
checks = append(checks,
PortOpenCheck{port: 2379},
DirAvailableCheck{Path: "/var/lib/etcd"},
)
}
return RunChecks(checks, os.Stderr)
}
func RunJoinNodeChecks(cfg *kubeadmapi.NodeConfiguration) error {
checks := []Checker{
SystemVerificationCheck{},
IsRootCheck{},
HostnameCheck{},
ServiceCheck{Service: "kubelet", CheckIfActive: false},
ServiceCheck{Service: "docker", CheckIfActive: true},
PortOpenCheck{port: 10250},
DirAvailableCheck{Path: path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "manifests")},
DirAvailableCheck{Path: "/var/lib/kubelet"},
FileAvailableCheck{Path: path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, "kubelet.conf")},
FileContentCheck{Path: bridgenf, Content: []byte{'1'}},
InPathCheck{executable: "ip", mandatory: true},
InPathCheck{executable: "iptables", mandatory: true},
InPathCheck{executable: "mount", mandatory: true},
InPathCheck{executable: "nsenter", mandatory: true},
InPathCheck{executable: "ebtables", mandatory: false},
InPathCheck{executable: "ethtool", mandatory: false},
InPathCheck{executable: "socat", mandatory: false},
InPathCheck{executable: "tc", mandatory: false},
InPathCheck{executable: "touch", mandatory: false},
}
return RunChecks(checks, os.Stderr)
}
func RunRootCheckOnly() error {
checks := []Checker{
IsRootCheck{},
}
return RunChecks(checks, os.Stderr)
}
// RunChecks runs each check, displays it's warnings/errors, and once all
// are processed will exit if any errors occurred.
func RunChecks(checks []Checker, ww io.Writer) error {
found := []error{}
for _, c := range checks {
warnings, errs := c.Check()
for _, w := range warnings {
io.WriteString(ww, fmt.Sprintf("[preflight] WARNING: %s\n", w))
}
found = append(found, errs...)
}
if len(found) > 0 {
var errs bytes.Buffer
for _, i := range found {
errs.WriteString("\t" + i.Error() + "\n")
}
return &Error{Msg: errs.String()}
}
return nil
}
func TryStartKubelet() {
// If we notice that the kubelet service is inactive, try to start it
initSystem, err := initsystem.GetInitSystem()
if err != nil {
fmt.Println("[preflight] No supported init system detected, won't ensure kubelet is running.")
} else if initSystem.ServiceExists("kubelet") && !initSystem.ServiceIsActive("kubelet") {
fmt.Println("[preflight] Starting the kubelet service")
if err := initSystem.ServiceStart("kubelet"); err != nil {
fmt.Printf("[preflight] WARNING: Unable to start the kubelet service: [%v]\n", err)
fmt.Println("[preflight] WARNING: Please ensure kubelet is running manually.")
}
}
}

View file

@ -0,0 +1,68 @@
/*
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 preflight
import (
"bytes"
"fmt"
"testing"
)
type preflightCheckTest struct {
msg string
}
func (pfct preflightCheckTest) Check() (warning, errors []error) {
if pfct.msg == "warning" {
return []error{fmt.Errorf("warning")}, nil
}
if pfct.msg != "" {
return nil, []error{fmt.Errorf("fake error")}
}
return
}
func TestRunChecks(t *testing.T) {
var tokenTest = []struct {
p []Checker
expected bool
output string
}{
{[]Checker{}, true, ""},
{[]Checker{preflightCheckTest{"warning"}}, true, "[preflight] WARNING: warning\n"}, // should just print warning
{[]Checker{preflightCheckTest{"error"}}, false, ""},
{[]Checker{preflightCheckTest{"test"}}, false, ""},
}
for _, rt := range tokenTest {
buf := new(bytes.Buffer)
actual := RunChecks(rt.p, buf)
if (actual == nil) != rt.expected {
t.Errorf(
"failed RunChecks:\n\texpected: %t\n\t actual: %t",
rt.expected,
(actual == nil),
)
}
if buf.String() != rt.output {
t.Errorf(
"failed RunChecks:\n\texpected: %s\n\t actual: %s",
rt.output,
buf.String(),
)
}
}
}

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

@ -0,0 +1,57 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"error.go",
"tokens.go",
"version.go",
],
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
],
)
go_test(
name = "go_default_test",
srcs = [
"error_test.go",
"tokens_test.go",
"version_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm: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"],
tags = ["automanaged"],
)

69
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/error.go generated vendored Normal file
View file

@ -0,0 +1,69 @@
/*
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 util
import (
"fmt"
"os"
"strings"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
)
const (
DefaultErrorExitCode = 1
PreFlightExitCode = 2
)
type debugError interface {
DebugError() (msg string, args []interface{})
}
// fatal prints the message if set and then exits.
func fatal(msg string, code int) {
if len(msg) > 0 {
// add newline if needed
if !strings.HasSuffix(msg, "\n") {
msg += "\n"
}
fmt.Fprint(os.Stderr, msg)
}
os.Exit(code)
}
// CheckErr prints a user friendly error to STDERR and exits with a non-zero
// exit code. Unrecognized errors will be printed with an "error: " prefix.
//
// This method is generic to the command in use and may be used by non-Kubectl
// commands.
func CheckErr(err error) {
checkErr("", err, fatal)
}
// checkErr formats a given error as a string and calls the passed handleErr
// func with that string and an kubectl exit code.
func checkErr(prefix string, err error, handleErr func(string, int)) {
switch err.(type) {
case nil:
return
case *preflight.Error:
handleErr(err.Error(), PreFlightExitCode)
default:
handleErr(err.Error(), DefaultErrorExitCode)
}
}

View file

@ -0,0 +1,52 @@
/*
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 util
import (
"fmt"
"testing"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
)
func TestCheckErr(t *testing.T) {
var codeReturned int
errHandle := func(err string, code int) {
codeReturned = code
}
var tokenTest = []struct {
e error
expected int
}{
{nil, 0},
{fmt.Errorf(""), DefaultErrorExitCode},
{&preflight.Error{}, PreFlightExitCode},
}
for _, rt := range tokenTest {
codeReturned = 0
checkErr("", rt.e, errHandle)
if codeReturned != rt.expected {
t.Errorf(
"failed checkErr:\n\texpected: %d\n\t actual: %d",
rt.expected,
codeReturned,
)
}
}
}

194
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/tokens.go generated vendored Normal file
View file

@ -0,0 +1,194 @@
/*
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 util
import (
"crypto/rand"
"encoding/hex"
"fmt"
"regexp"
"strconv"
"strings"
"time"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/pkg/api"
v1 "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
)
const (
TokenIDBytes = 3
TokenSecretBytes = 8
BootstrapTokenSecretPrefix = "bootstrap-token-"
DefaultTokenDuration = time.Duration(8) * time.Hour
tokenCreateRetries = 5
)
var (
tokenIDRegexpString = "^([a-z0-9]{6})$"
tokenIDRegexp = regexp.MustCompile(tokenIDRegexpString)
tokenRegexpString = "^([a-z0-9]{6})\\:([a-z0-9]{16})$"
tokenRegexp = regexp.MustCompile(tokenRegexpString)
)
func randBytes(length int) (string, error) {
b := make([]byte, length)
_, err := rand.Read(b)
if err != nil {
return "", err
}
return hex.EncodeToString(b), nil
}
// GenerateToken generates a new token with a token ID that is valid as a
// Kubernetes DNS label.
// For more info, see kubernetes/pkg/util/validation/validation.go.
func GenerateToken(d *kubeadmapi.TokenDiscovery) error {
tokenID, err := randBytes(TokenIDBytes)
if err != nil {
return err
}
token, err := randBytes(TokenSecretBytes)
if err != nil {
return err
}
d.ID = strings.ToLower(tokenID)
d.Secret = strings.ToLower(token)
return nil
}
// ParseTokenID tries and parse a valid token ID from a string.
// An error is returned in case of failure.
func ParseTokenID(s string) error {
if !tokenIDRegexp.MatchString(s) {
return fmt.Errorf("token ID [%q] was not of form [%q]", s, tokenIDRegexpString)
}
return nil
}
// ParseToken tries and parse a valid token from a string.
// A token ID and token secret are returned in case of success, an error otherwise.
func ParseToken(s string) (string, string, error) {
split := tokenRegexp.FindStringSubmatch(s)
if len(split) != 3 {
return "", "", fmt.Errorf("token [%q] was not of form [%q]", s, tokenRegexpString)
}
return split[1], split[2], nil
}
// BearerToken returns a string representation of the passed token.
func BearerToken(d *kubeadmapi.TokenDiscovery) string {
return fmt.Sprintf("%s:%s", d.ID, d.Secret)
}
// ValidateToken validates whether a token is well-formed.
// In case it's not, the corresponding error is returned as well.
func ValidateToken(d *kubeadmapi.TokenDiscovery) (bool, error) {
if _, _, err := ParseToken(d.ID + ":" + d.Secret); err != nil {
return false, err
}
return true, nil
}
func DiscoveryPort(d *kubeadmapi.TokenDiscovery) int32 {
if len(d.Addresses) == 0 {
return kubeadmapiext.DefaultDiscoveryBindPort
}
split := strings.Split(d.Addresses[0], ":")
if len(split) == 1 {
return kubeadmapiext.DefaultDiscoveryBindPort
}
if i, err := strconv.Atoi(split[1]); err != nil {
return int32(i)
}
return kubeadmapiext.DefaultDiscoveryBindPort
}
// UpdateOrCreateToken attempts to update a token with the given ID, or create if it does
// not already exist.
func UpdateOrCreateToken(client *clientset.Clientset, d *kubeadmapi.TokenDiscovery, tokenDuration time.Duration) error {
// Let's make sure
if valid, err := ValidateToken(d); !valid {
return err
}
secretName := fmt.Sprintf("%s%s", BootstrapTokenSecretPrefix, d.ID)
var lastErr error
for i := 0; i < tokenCreateRetries; i++ {
secret, err := client.Secrets(api.NamespaceSystem).Get(secretName, metav1.GetOptions{})
if err == nil {
// Secret with this ID already exists, update it:
secret.Data = encodeTokenSecretData(d, tokenDuration)
if _, err := client.Secrets(api.NamespaceSystem).Update(secret); err == nil {
return nil
} else {
lastErr = err
}
continue
}
// Secret does not already exist:
if apierrors.IsNotFound(err) {
secret = &v1.Secret{
ObjectMeta: v1.ObjectMeta{
Name: secretName,
},
Type: api.SecretTypeBootstrapToken,
Data: encodeTokenSecretData(d, tokenDuration),
}
if _, err := client.Secrets(api.NamespaceSystem).Create(secret); err == nil {
return nil
} else {
lastErr = err
}
continue
}
}
return fmt.Errorf(
"unable to create bootstrap token after %d attempts [%v]",
tokenCreateRetries,
lastErr,
)
}
func encodeTokenSecretData(d *kubeadmapi.TokenDiscovery, duration time.Duration) map[string][]byte {
var (
data = map[string][]byte{}
)
data["token-id"] = []byte(d.ID)
data["token-secret"] = []byte(d.Secret)
data["usage-bootstrap-signing"] = []byte("true")
if duration > 0 {
t := time.Now()
t = t.Add(duration)
data["expiration"] = []byte(t.Format(time.RFC3339))
}
return data
}

View file

@ -0,0 +1,143 @@
/*
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 util
import (
"testing"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func TestTokenParse(t *testing.T) {
invalidTokens := []string{
// invalid parcel size
"1234567890123456789012",
"12345:1234567890123456",
".1234567890123456",
// invalid separation
"123456:1234567890.123456",
"abcdef.1234567890123456",
// invalid token id
"Abcdef:1234567890123456",
// invalid token secret
"123456:AABBCCDDEEFFGGHH",
}
for _, token := range invalidTokens {
if _, _, err := ParseToken(token); err == nil {
t.Errorf("ParseToken did not return an error for this invalid token: [%s]", token)
}
}
validTokens := []string{
"abcdef:1234567890123456",
"123456:aabbccddeeffgghh",
}
for _, token := range validTokens {
if _, _, err := ParseToken(token); err != nil {
t.Errorf("ParseToken returned an error for this valid token: [%s]", token)
}
}
}
func TestParseTokenID(t *testing.T) {
invalidTokenIDs := []string{
"",
"1234567890123456789012",
"12345",
"Abcdef",
}
for _, tokenID := range invalidTokenIDs {
if err := ParseTokenID(tokenID); err == nil {
t.Errorf("ParseTokenID did not return an error for this invalid token ID: [%q]", tokenID)
}
}
validTokens := []string{
"abcdef",
"123456",
}
for _, tokenID := range validTokens {
if err := ParseTokenID(tokenID); err != nil {
t.Errorf("ParseTokenID failed for a valid token ID [%q], err: %+v", tokenID, err)
}
}
}
func TestValidateToken(t *testing.T) {
invalidTokens := []*kubeadmapi.TokenDiscovery{
{ID: "", Secret: ""},
{ID: "1234567890123456789012", Secret: ""},
{ID: "", Secret: "1234567890123456789012"},
{ID: "12345", Secret: "1234567890123456"},
{ID: "Abcdef", Secret: "1234567890123456"},
{ID: "123456", Secret: "AABBCCDDEEFFGGHH"},
}
for _, token := range invalidTokens {
if valid, err := ValidateToken(token); valid == true || err == nil {
t.Errorf("ValidateToken did not return an error for this invalid token: [%s]", token)
}
}
validTokens := []*kubeadmapi.TokenDiscovery{
{ID: "abcdef", Secret: "1234567890123456"},
{ID: "123456", Secret: "aabbccddeeffgghh"},
}
for _, token := range validTokens {
if valid, err := ValidateToken(token); valid == false || err != nil {
t.Errorf("ValidateToken failed for a valid token [%s], valid: %t, err: %+v", token, valid, err)
}
}
}
func TestGenerateToken(t *testing.T) {
td := &kubeadmapi.TokenDiscovery{}
if err := GenerateToken(td); err != nil {
t.Fatalf("GenerateToken returned an unexpected error: %+v", err)
}
if len(td.ID) != 6 {
t.Errorf("failed GenerateToken first part length:\n\texpected: 6\n\t actual: %d", len(td.ID))
}
if len(td.Secret) != 16 {
t.Errorf("failed GenerateToken second part length:\n\texpected: 16\n\t actual: %d", len(td.Secret))
}
}
func TestRandBytes(t *testing.T) {
var randTest = []int{
0,
1,
2,
3,
100,
}
for _, rt := range randTest {
actual, err := randBytes(rt)
if err != nil {
t.Errorf("failed randBytes: %v", err)
}
if len(actual) != rt*2 {
t.Errorf("failed randBytes:\n\texpected: %d\n\t actual: %d\n", rt*2, len(actual))
}
}
}

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 util
import (
"fmt"
"io/ioutil"
"net/http"
"regexp"
"strings"
)
var (
kubeReleaseBucketURL = "https://storage.googleapis.com/kubernetes-release/release"
kubeReleaseRegex = regexp.MustCompile(`^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)([-0-9a-zA-Z_\.+]*)?$`)
kubeReleaseLabelRegex = regexp.MustCompile(`^[[:lower:]]+(-[-\w_\.]+)?$`)
)
// KubernetesReleaseVersion is helper function that can fetch
// available version information from release servers based on
// label names, like "stable" or "latest".
//
// If argument is already semantic version string, it
// will return same string.
//
// In case of labels, it tries to fetch from release
// servers and then return actual semantic version.
//
// Available names on release servers:
// stable (latest stable release)
// stable-1 (latest stable release in 1.x)
// stable-1.0 (and similarly 1.1, 1.2, 1.3, ...)
// latest (latest release, including alpha/beta)
// latest-1 (latest release in 1.x, including alpha/beta)
// latest-1.0 (and similarly 1.1, 1.2, 1.3, ...)
func KubernetesReleaseVersion(version string) (string, error) {
if kubeReleaseRegex.MatchString(version) {
return version, nil
} else if kubeReleaseLabelRegex.MatchString(version) {
url := fmt.Sprintf("%s/%s.txt", kubeReleaseBucketURL, version)
resp, err := http.Get(url)
if err != nil {
return "", fmt.Errorf("unable to get URL %q: %s", url, err.Error())
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("unable to fetch release information. URL: %q Status: %v", url, resp.Status)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("unable to read content of URL %q: %s", url, err.Error())
}
// Re-validate received version and return.
return KubernetesReleaseVersion(strings.Trim(string(body), " \t\n"))
}
return "", fmt.Errorf("version %q doesn't match patterns for neither semantic version nor labels (stable, latest, ...)", version)
}

View file

@ -0,0 +1,122 @@
/*
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 util
import (
"net/http"
"net/http/httptest"
"path"
"strings"
"testing"
)
func TestEmptyVersion(t *testing.T) {
ver, err := KubernetesReleaseVersion("")
if err == nil {
t.Error("KubernetesReleaseVersion returned successfully, but error expected")
}
if ver != "" {
t.Error("KubernetesReleaseVersion returned value, expected only error")
}
}
func TestValidVersion(t *testing.T) {
validVersions := []string{
"v1.3.0",
"v1.4.0-alpha.0",
"v1.4.5",
"v1.4.0-beta.0",
"v2.0.0",
"v1.6.0-alpha.0.536+d60d9f3269288f",
"v1.5.0-alpha.0.1078+1044b6822497da-pull",
"v1.5.0-alpha.1.822+49b9e32fad9f32-pull-gke-gci",
}
for _, s := range validVersions {
ver, err := KubernetesReleaseVersion(s)
t.Log("Valid: ", s, ver, err)
if err != nil {
t.Errorf("KubernetesReleaseVersion unexpected error for version %q: %v", s, err)
}
if ver != s {
t.Errorf("KubernetesReleaseVersion should return same valid version string. %q != %q", s, ver)
}
}
}
func TestInvalidVersion(t *testing.T) {
invalidVersions := []string{
"v1.3",
"1.4.0",
"1.4.5+git",
"something1.2",
}
for _, s := range invalidVersions {
ver, err := KubernetesReleaseVersion(s)
t.Log("Invalid: ", s, ver, err)
if err == nil {
t.Errorf("KubernetesReleaseVersion error expected for version %q, but returned succesfully", s)
}
if ver != "" {
t.Errorf("KubernetesReleaseVersion should return empty string in case of error. Returned %q for version %q", ver, s)
}
}
}
func TestVersionFromNetwork(t *testing.T) {
type T struct {
Content string
Status int
Expected string
ErrorExpected bool
}
cases := map[string]T{
"stable": {"stable-1", http.StatusOK, "v1.4.6", false}, // recursive pointer to stable-1
"stable-1": {"v1.4.6", http.StatusOK, "v1.4.6", false},
"stable-1.3": {"v1.3.10", http.StatusOK, "v1.3.10", false},
"latest": {"v1.6.0-alpha.0", http.StatusOK, "v1.6.0-alpha.0", false},
"latest-1.3": {"v1.3.11-beta.0", http.StatusOK, "v1.3.11-beta.0", false},
"empty": {"", http.StatusOK, "", true},
"garbage": {"<?xml version='1.0'?><Error><Code>NoSuchKey</Code><Message>The specified key does not exist.</Message></Error>", http.StatusOK, "", true},
"unknown": {"The requested URL was not found on this server.", http.StatusNotFound, "", true},
}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
key := strings.TrimSuffix(path.Base(r.URL.Path), ".txt")
res, found := cases[key]
if found {
http.Error(w, res.Content, res.Status)
} else {
http.Error(w, "Unknown test case key!", http.StatusNotFound)
}
}))
defer server.Close()
kubeReleaseBucketURL = server.URL
for k, v := range cases {
ver, err := KubernetesReleaseVersion(k)
t.Logf("Key: %q. Result: %q, Error: %v", k, ver, err)
switch {
case err != nil && !v.ErrorExpected:
t.Errorf("KubernetesReleaseVersion: unexpected error for %q. Error: %v", k, err)
case err == nil && v.ErrorExpected:
t.Errorf("KubernetesReleaseVersion: error expected for key %q, but result is %q", k, ver)
case ver != v.Expected:
t.Errorf("KubernetesReleaseVersion: unexpected result for key %q. Expected: %q Actual: %q", k, v.Expected, ver)
}
}
}

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

@ -0,0 +1,30 @@
/*
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 main
import (
"os"
"k8s.io/kubernetes/cmd/kubeadm/app"
)
func main() {
if err := app.Run(); err != nil {
os.Exit(1)
}
os.Exit(0)
}

41
vendor/k8s.io/kubernetes/cmd/kubeadm/test/BUILD generated vendored Normal file
View file

@ -0,0 +1,41 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["util.go"],
tags = ["automanaged"],
)
go_test(
name = "go_default_test",
srcs = ["token_test.go"],
args = ["--kubeadm-path=../kubeadm"],
data = ["//cmd/kubeadm"],
library = ":go_default_library",
tags = [
"automanaged",
"integration",
"skip",
],
)
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,67 @@
/*
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 (
"flag"
"os"
"path/filepath"
"regexp"
"testing"
)
const (
TokenExpectedRegex = "^\\S{6}\\:\\S{16}\n$"
)
var kubeadmPath string
func init() {
flag.StringVar(&kubeadmPath, "kubeadm-path", filepath.Join(os.Getenv("KUBE_ROOT"), "cluster/kubeadm.sh"), "Location of kubeadm")
}
func TestCmdTokenGenerate(t *testing.T) {
stdout, _, err := RunCmd(kubeadmPath, "ex", "token", "generate")
if err != nil {
t.Fatalf("'kubeadm ex token generate' exited uncleanly: %v", err)
}
matched, err := regexp.MatchString(TokenExpectedRegex, stdout)
if err != nil {
t.Fatalf("encountered an error while trying to match 'kubeadm ex token generate' stdout: %v", err)
}
if !matched {
t.Errorf("'kubeadm ex token generate' stdout did not match expected regex; wanted: [%q], got: [%s]", TokenExpectedRegex, stdout)
}
}
func TestCmdTokenGenerateTypoError(t *testing.T) {
/*
Since we expect users to do things like this:
$ TOKEN=$(kubeadm ex token generate)
we want to make sure that if they have a typo in their command, we exit
with a non-zero status code after showing the command's usage, so that
the usage itself isn't captured as a token without the user noticing.
*/
_, _, err := RunCmd(kubeadmPath, "ex", "token", "genorate") // subtle typo
if err == nil {
t.Error("'kubeadm ex token genorate' (a deliberate typo) exited without an error when we expected non-zero exit status")
}
}

41
vendor/k8s.io/kubernetes/cmd/kubeadm/test/util.go generated vendored Normal file
View file

@ -0,0 +1,41 @@
/*
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 (
"bytes"
"fmt"
"io"
"os"
"os/exec"
)
// Forked from test/e2e/framework because the e2e framework is quite bloated
// for our purposes here, and modified to remove undesired logging.
func RunCmd(command string, args ...string) (string, string, error) {
var bout, berr bytes.Buffer
cmd := exec.Command(command, args...)
cmd.Stdout = io.MultiWriter(os.Stdout, &bout)
cmd.Stderr = io.MultiWriter(os.Stderr, &berr)
err := cmd.Run()
stdout, stderr := bout.String(), berr.String()
if err != nil {
return "", "", fmt.Errorf("error running %s %v; got error %v, stdout %q, stderr %q",
command, args, err, stdout, stderr)
}
return stdout, stderr, nil
}