Add glide.yaml and vendor deps

This commit is contained in:
Dalton Hubble 2016-12-03 22:43:32 -08:00
parent db918f12ad
commit 5b3d5e81bd
18880 changed files with 5166045 additions and 1 deletions

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

@ -0,0 +1,24 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_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",
"//pkg/util/logs:go_default_library",
"//vendor:github.com/spf13/pflag",
],
)

View file

@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"env.go",
"register.go",
"types.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/runtime/schema:go_default_library",
],
)

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,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 (
"fmt"
"os"
"runtime"
"strings"
)
var GlobalEnvParams = SetEnvParams()
// TODO(phase2) use componentconfig
// we need some params for testing etc, let's keep these hidden for now
func SetEnvParams() *EnvParams {
envParams := map[string]string{
// TODO(phase1+): Mode prefix and host_pki_path to another place as constants, and use them everywhere
// Right now they're used here and there, but not consequently
"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": "",
"component_loglevel": "--v=2",
}
for k := range envParams {
if v := os.Getenv(fmt.Sprintf("KUBE_%s", strings.ToUpper(k))); v != "" {
envParams[k] = v
}
}
return &EnvParams{
KubernetesDir: envParams["kubernetes_dir"],
HostPKIPath: envParams["host_pki_path"],
HostEtcdPath: envParams["host_etcd_path"],
HyperkubeImage: envParams["hyperkube_image"],
RepositoryPrefix: envParams["repo_prefix"],
DiscoveryImage: envParams["discovery_image"],
EtcdImage: envParams["etcd_image"],
ComponentLoglevel: envParams["component_loglevel"],
}
}

View file

@ -0,0 +1,25 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_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",
],
)

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 (
"k8s.io/kubernetes/pkg/api"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
)
// GroupName is the group name use in this package
const GroupName = "kubeadm.k8s.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
// Kind takes an unqualified kind and returns a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&MasterConfiguration{},
&NodeConfiguration{},
&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,91 @@
/*
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/kubernetes/pkg/apis/meta/v1"
)
type EnvParams struct {
KubernetesDir string
HostPKIPath string
HostEtcdPath string
HyperkubeImage string
RepositoryPrefix string
DiscoveryImage string
EtcdImage string
ComponentLoglevel string
}
type MasterConfiguration struct {
metav1.TypeMeta
Secrets Secrets
API API
Discovery Discovery
Etcd Etcd
Networking Networking
KubernetesVersion string
CloudProvider string
}
type API struct {
AdvertiseAddresses []string
ExternalDNSNames []string
BindPort int32
}
type Discovery struct {
BindPort int32
}
type Networking struct {
ServiceSubnet string
PodSubnet string
DNSDomain string
}
type Etcd struct {
Endpoints []string
CAFile string
CertFile string
KeyFile string
}
type Secrets struct {
GivenToken string // dot-separated `<TokenID>.<Token>` set by the user
TokenID string // optional on master side, will be generated if not specified
Token []byte // optional on master side, will be generated if not specified
BearerToken string // set based on Token
}
type NodeConfiguration struct {
metav1.TypeMeta
MasterAddresses []string
Secrets Secrets
APIPort int32
DiscoveryPort int32
}
// ClusterInfo TODO add description
type ClusterInfo struct {
metav1.TypeMeta
// TODO(phase1+) this may become simply `api.Config`
CertificateAuthorities []string `json:"certificateAuthorities"`
Endpoints []string `json:"endpoints"`
}

View file

@ -0,0 +1,29 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_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",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/runtime/schema:go_default_library",
],
)

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 v1alpha1
import (
"k8s.io/kubernetes/pkg/runtime"
)
const (
DefaultServiceDNSDomain = "cluster.local"
DefaultServicesSubnet = "10.96.0.0/12"
DefaultKubernetesVersion = "stable"
DefaultKubernetesFallbackVersion = "v1.4.6"
DefaultAPIBindPort = 6443
DefaultDiscoveryBindPort = 9898
)
func addDefaultingFuncs(scheme *runtime.Scheme) error {
RegisterDefaults(scheme)
return scheme.AddDefaultingFuncs(
SetDefaults_MasterConfiguration,
SetDefaults_NodeConfiguration,
)
}
func SetDefaults_MasterConfiguration(obj *MasterConfiguration) {
if obj.KubernetesVersion == "" {
obj.KubernetesVersion = DefaultKubernetesVersion
}
if obj.API.BindPort == 0 {
obj.API.BindPort = DefaultAPIBindPort
}
if obj.Discovery.BindPort == 0 {
obj.Discovery.BindPort = DefaultDiscoveryBindPort
}
if obj.Networking.ServiceSubnet == "" {
obj.Networking.ServiceSubnet = DefaultServicesSubnet
}
if obj.Networking.DNSDomain == "" {
obj.Networking.DNSDomain = DefaultServiceDNSDomain
}
}
func SetDefaults_NodeConfiguration(obj *NodeConfiguration) {
if obj.APIPort == 0 {
obj.APIPort = DefaultAPIBindPort
}
if obj.DiscoveryPort == 0 {
obj.DiscoveryPort = DefaultDiscoveryBindPort
}
}

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 (
"k8s.io/kubernetes/pkg/api/v1"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
)
// GroupName is the group name use in this package
const GroupName = "kubeadm.k8s.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
var (
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,80 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
)
type MasterConfiguration struct {
metav1.TypeMeta `json:",inline"`
Secrets Secrets `json:"secrets"`
API API `json:"api"`
Etcd Etcd `json:"etcd"`
Discovery Discovery `json:"discovery"`
Networking Networking `json:"networking"`
KubernetesVersion string `json:"kubernetesVersion"`
CloudProvider string `json:"cloudProvider"`
}
type API struct {
AdvertiseAddresses []string `json:"advertiseAddresses"`
ExternalDNSNames []string `json:"externalDNSNames"`
BindPort int32 `json:"bindPort"`
}
type Discovery struct {
BindPort int32 `json:"bindPort"`
}
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 Secrets struct {
GivenToken string `json:"givenToken"` // dot-separated `<TokenID>.<Token>` set by the user
TokenID string `json:"tokenID"` // optional on master side, will be generated if not specified
Token []byte `json:"token"` // optional on master side, will be generated if not specified
BearerToken string `json:"bearerToken"` // set based on Token
}
type NodeConfiguration struct {
metav1.TypeMeta `json:",inline"`
MasterAddresses []string `json:"masterAddresses"`
Secrets Secrets `json:"secrets"`
APIPort int32 `json:"apiPort"`
DiscoveryPort int32 `json:"discoveryPort"`
}
// 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,42 @@
// +build !ignore_autogenerated
/*
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.
*/
// This file was autogenerated by defaulter-gen. Do not edit it manually!
package v1alpha1
import (
runtime "k8s.io/kubernetes/pkg/runtime"
)
// RegisterDefaults adds defaulters functions to the given scheme.
// Public to allow building arbitrary schemes.
// All generated defaulters are covering - they call all nested defaulters.
func RegisterDefaults(scheme *runtime.Scheme) error {
scheme.AddTypeDefaultingFunc(&MasterConfiguration{}, func(obj interface{}) { SetObjectDefaults_MasterConfiguration(obj.(*MasterConfiguration)) })
scheme.AddTypeDefaultingFunc(&NodeConfiguration{}, func(obj interface{}) { SetObjectDefaults_NodeConfiguration(obj.(*NodeConfiguration)) })
return nil
}
func SetObjectDefaults_MasterConfiguration(in *MasterConfiguration) {
SetDefaults_MasterConfiguration(in)
}
func SetObjectDefaults_NodeConfiguration(in *NodeConfiguration) {
SetDefaults_NodeConfiguration(in)
}

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

@ -0,0 +1,54 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
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/master:go_default_library",
"//cmd/kubeadm/app/node:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
"//pkg/api:go_default_library",
"//pkg/cloudprovider:go_default_library",
"//pkg/cloudprovider/providers:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/util/flag:go_default_library",
"//pkg/util/initsystem:go_default_library",
"//pkg/util/net:go_default_library",
"//pkg/version:go_default_library",
"//vendor:github.com/renstrom/dedent",
"//vendor:github.com/spf13/cobra",
],
)
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"],
)

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

@ -0,0 +1,88 @@
/*
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/kubernetes/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 workloads, like pods and replica sets 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
// crapy 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(NewCmdToken(out))
cmds.AddCommand(NewCmdVersion(out))
return cmds
}

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

@ -0,0 +1,296 @@
/*
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"
"fmt"
"html/template"
"io"
"io/ioutil"
"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"
kubemaster "k8s.io/kubernetes/cmd/kubeadm/app/master"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/cloudprovider"
_ "k8s.io/kubernetes/pkg/cloudprovider/providers"
"k8s.io/kubernetes/pkg/runtime"
netutil "k8s.io/kubernetes/pkg/util/net"
)
const (
joinArgsTemplateLiteral = `--token={{.Cfg.Secrets.GivenToken -}}
{{if ne .Cfg.API.BindPort .DefaultAPIBindPort -}}
{{" --api-port="}}{{.Cfg.API.BindPort -}}
{{end -}}
{{if ne .Cfg.Discovery.BindPort .DefaultDiscoveryBindPort -}}
{{" --discovery-port="}}{{.Cfg.Discovery.BindPort -}}
{{end -}}
{{" "}}{{index .Cfg.API.AdvertiseAddresses 0 -}}
`
)
var (
initDoneMsgf = dedent.Dedent(`
Kubernetes master initialised successfully!
You can now join any number of machines by running the following on each node:
kubeadm join %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.Run(out))
},
}
cmd.PersistentFlags().StringVar(
&cfg.Secrets.GivenToken, "token", cfg.Secrets.GivenToken,
"Shared secret used to secure cluster bootstrap; if none is provided, one will be generated for you",
)
cmd.PersistentFlags().StringSliceVar(
&cfg.API.AdvertiseAddresses, "api-advertise-addresses", cfg.API.AdvertiseAddresses,
"The IP addresses to advertise, in case autodetection fails",
)
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().StringVar(
&cfg.CloudProvider, "cloud-provider", cfg.CloudProvider,
`Enable cloud provider features (external load-balancers, storage, etc), e.g. "gce"`,
)
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")
// TODO (phase1+) @errordeveloper make the flags below not show up in --help but rather on --advanced-help
cmd.PersistentFlags().StringSliceVar(
&cfg.Etcd.Endpoints, "external-etcd-endpoints", cfg.Etcd.Endpoints,
"etcd endpoints to use, in case you have an external cluster",
)
cmd.PersistentFlags().MarkDeprecated("external-etcd-endpoints", "this flag will be removed when componentconfig exists")
cmd.PersistentFlags().StringVar(
&cfg.Etcd.CAFile, "external-etcd-cafile", cfg.Etcd.CAFile,
"etcd certificate authority certificate file. Note: The path must be in /etc/ssl/certs",
)
cmd.PersistentFlags().MarkDeprecated("external-etcd-cafile", "this flag will be removed when componentconfig exists")
cmd.PersistentFlags().StringVar(
&cfg.Etcd.CertFile, "external-etcd-certfile", cfg.Etcd.CertFile,
"etcd client certificate file. Note: The path must be in /etc/ssl/certs",
)
cmd.PersistentFlags().MarkDeprecated("external-etcd-certfile", "this flag will be removed when componentconfig exists")
cmd.PersistentFlags().StringVar(
&cfg.Etcd.KeyFile, "external-etcd-keyfile", cfg.Etcd.KeyFile,
"etcd client key file. Note: The path must be in /etc/ssl/certs",
)
cmd.PersistentFlags().MarkDeprecated("external-etcd-keyfile", "this flag will be removed when componentconfig exists")
cmd.PersistentFlags().BoolVar(
&skipPreFlight, "skip-preflight-checks", skipPreFlight,
"skip preflight checks normally run before modifying the system",
)
cmd.PersistentFlags().Int32Var(
&cfg.API.BindPort, "api-port", cfg.API.BindPort,
"Port for API to bind to",
)
cmd.PersistentFlags().Int32Var(
&cfg.Discovery.BindPort, "discovery-port", cfg.Discovery.BindPort,
"Port for JWS discovery service to bind to",
)
return cmd
}
type Init struct {
cfg *kubeadmapi.MasterConfiguration
}
func NewInit(cfgPath string, cfg *kubeadmapi.MasterConfiguration, skipPreFlight bool) (*Init, error) {
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 {
// TODO(phase1+) perhaps we could actually grab eth0 and eth1
ip, err := netutil.ChooseHostInterface()
if err != nil {
return nil, err
}
cfg.API.AdvertiseAddresses = []string{ip.String()}
}
if !skipPreFlight {
fmt.Println("Running pre-flight checks")
err := preflight.RunInitMasterChecks(cfg)
if err != nil {
return nil, &preflight.PreFlightError{Msg: err.Error()}
}
} else {
fmt.Println("Skipping pre-flight checks")
}
// 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("Using Kubernetes version:", ver)
// TODO(phase1+) create a custom flag
if cfg.CloudProvider != "" {
if cloudprovider.IsCloudProvider(cfg.CloudProvider) {
fmt.Printf("cloud provider %q initialized for the control plane. Remember to set the same cloud provider flag on the kubelet.\n", cfg.CloudProvider)
} else {
return nil, fmt.Errorf("cloud provider %q is not supported, you can use any of %v, or leave it unset.\n", cfg.CloudProvider, cloudprovider.CloudProviders())
}
}
return &Init{cfg: cfg}, nil
}
// joinArgsData denotes a data object which is needed by function generateJoinArgs to generate kubeadm join arguments.
type joinArgsData struct {
Cfg *kubeadmapi.MasterConfiguration
DefaultAPIBindPort int32
DefaultDiscoveryBindPort int32
}
// Run executes master node provisioning, including certificates, needed static pod manifests, etc.
func (i *Init) Run(out io.Writer) error {
if err := kubemaster.CreateTokenAuthFile(&i.cfg.Secrets); err != nil {
return err
}
if err := kubemaster.WriteStaticPodManifests(i.cfg); err != nil {
return err
}
caKey, caCert, err := kubemaster.CreatePKIAssets(i.cfg)
if err != nil {
return err
}
kubeconfigs, err := kubemaster.CreateCertsAndConfigForClients(i.cfg.API, []string{"kubelet", "admin"}, caKey, caCert)
if err != nil {
return err
}
// kubeadm is responsible for writing the following kubeconfig file, which
// kubelet should be waiting for. Help user avoid foot-shooting by refusing to
// write a file that has already been written (the kubelet will be up and
// running in that case - they'd need to stop the kubelet, remove the file, and
// start it again in that case).
// TODO(phase1+) this is no longer the right place to guard agains foo-shooting,
// we need to decide how to handle existing files (it may be handy to support
// importing existing files, may be we could even make our command idempotant,
// or at least allow for external PKI and stuff)
for name, kubeconfig := range kubeconfigs {
if err := kubeadmutil.WriteKubeconfigIfNotExists(name, kubeconfig); err != nil {
return err
}
}
client, err := kubemaster.CreateClientAndWaitForAPI(kubeconfigs["admin"])
if err != nil {
return err
}
schedulePodsOnMaster := false
if err := kubemaster.UpdateMasterRoleLabelsAndTaints(client, schedulePodsOnMaster); err != nil {
return err
}
if err := kubemaster.CreateDiscoveryDeploymentAndSecret(i.cfg, client, caCert); err != nil {
return err
}
if err := kubemaster.CreateEssentialAddons(i.cfg, client); err != nil {
return err
}
data := joinArgsData{i.cfg, kubeadmapiext.DefaultAPIBindPort, kubeadmapiext.DefaultDiscoveryBindPort}
if joinArgs, err := generateJoinArgs(data); err != nil {
return err
} else {
fmt.Fprintf(out, initDoneMsgf, joinArgs)
}
return nil
}
// generateJoinArgs generates kubeadm join arguments
func generateJoinArgs(data joinArgsData) (string, error) {
joinArgsTemplate := template.Must(template.New("joinArgsTemplate").Parse(joinArgsTemplateLiteral))
var b bytes.Buffer
if err := joinArgsTemplate.Execute(&b, data); err != nil {
return "", err
}
return b.String(), nil
}

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

@ -0,0 +1,160 @@
/*
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"
"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"
kubenode "k8s.io/kubernetes/cmd/kubeadm/app/node"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/runtime"
)
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.Run(out))
},
}
cmd.PersistentFlags().StringVar(
&cfg.Secrets.GivenToken, "token", cfg.Secrets.GivenToken,
"(required) Shared secret used to secure bootstrap. Must match the output of 'kubeadm init'",
)
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().Int32Var(
&cfg.APIPort, "api-port", cfg.APIPort,
"(optional) API server port on the master",
)
cmd.PersistentFlags().Int32Var(
&cfg.DiscoveryPort, "discovery-port", cfg.DiscoveryPort,
"(optional) Discovery port on the master",
)
return cmd
}
type Join struct {
cfg *kubeadmapi.NodeConfiguration
}
func NewJoin(cfgPath string, args []string, cfg *kubeadmapi.NodeConfiguration, skipPreFlight bool) (*Join, error) {
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 len(args) == 0 && len(cfg.MasterAddresses) == 0 {
return nil, fmt.Errorf("must specify master address (see --help)")
}
cfg.MasterAddresses = append(cfg.MasterAddresses, args...)
if len(cfg.MasterAddresses) > 1 {
return nil, fmt.Errorf("Must not specify more than one master address (see --help)")
}
if !skipPreFlight {
fmt.Println("Running pre-flight checks")
err := preflight.RunJoinNodeChecks(cfg)
if err != nil {
return nil, &preflight.PreFlightError{Msg: err.Error()}
}
} else {
fmt.Println("Skipping pre-flight checks")
}
ok, err := kubeadmutil.UseGivenTokenIfValid(&cfg.Secrets)
if !ok {
if err != nil {
return nil, fmt.Errorf("%v (see --help)\n", err)
}
return nil, fmt.Errorf("Must specify --token (see --help)\n")
}
return &Join{cfg: cfg}, nil
}
// Run executes worked node provisioning and tries to join an existing cluster.
func (j *Join) Run(out io.Writer) error {
clusterInfo, err := kubenode.RetrieveTrustedClusterInfo(j.cfg)
if err != nil {
return err
}
connectionDetails, err := kubenode.EstablishMasterConnection(j.cfg, clusterInfo)
if err != nil {
return err
}
kubeconfig, err := kubenode.PerformTLSBootstrap(connectionDetails)
if err != nil {
return err
}
err = kubeadmutil.WriteKubeconfigIfNotExists("kubelet", kubeconfig)
if err != nil {
return err
}
fmt.Fprintf(out, joinDoneMsgf)
return nil
}

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

@ -0,0 +1,165 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"github.com/spf13/cobra"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/pkg/util/initsystem"
)
// NewCmdReset returns "kubeadm reset" command.
func NewCmdReset(out io.Writer) *cobra.Command {
var skipPreFlight 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)
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",
)
return cmd
}
type Reset struct{}
func NewReset(skipPreFlight bool) (*Reset, error) {
if !skipPreFlight {
fmt.Println("Running pre-flight checks")
err := preflight.RunResetCheck()
if err != nil {
return nil, &preflight.PreFlightError{Msg: err.Error()}
}
} else {
fmt.Println("Skipping pre-flight checks")
}
return &Reset{}, nil
}
// cleanDir removes everything in a directory, but not the directory itself:
func cleanDir(path string) {
// If the directory doesn't even exist there's nothing to do, and we do
// not consider this an error:
if _, err := os.Stat(path); os.IsNotExist(err) {
return
}
d, err := os.Open(path)
if err != nil {
fmt.Printf("failed to remove directory: [%v]\n", err)
}
defer d.Close()
names, err := d.Readdirnames(-1)
if err != nil {
fmt.Printf("failed to remove directory: [%v]\n", err)
}
for _, name := range names {
err = os.RemoveAll(filepath.Join(path, name))
if err != nil {
fmt.Printf("failed to remove directory: [%v]\n", err)
}
}
}
// resetConfigDir is used to cleanup the files kubeadm writes in /etc/kubernetes/.
func resetConfigDir(configDirPath string) {
dirsToClean := []string{
filepath.Join(configDirPath, "manifests"),
filepath.Join(configDirPath, "pki"),
}
fmt.Printf("Deleting contents of config directories: %v\n", dirsToClean)
for _, dir := range dirsToClean {
cleanDir(dir)
}
filesToClean := []string{
filepath.Join(configDirPath, "admin.conf"),
filepath.Join(configDirPath, "kubelet.conf"),
}
fmt.Printf("Deleting files: %v\n", filesToClean)
for _, path := range filesToClean {
err := os.RemoveAll(path)
if err != nil {
fmt.Printf("failed to remove file: [%v]\n", err)
}
}
}
// Run reverts any changes made to this host by "kubeadm init" or "kubeadm join".
func (r *Reset) Run(out io.Writer) error {
serviceToStop := "kubelet"
initSystem, err := initsystem.GetInitSystem()
if err != nil {
fmt.Printf("%v", err)
} else {
fmt.Printf("Stopping the %s service...\n", serviceToStop)
if err := initSystem.ServiceStop(serviceToStop); err != nil {
fmt.Printf("failed to stop the %s service", serviceToStop)
}
}
fmt.Printf("Unmounting directories in /var/lib/kubelet...\n")
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("failed to unmount directories in /var/lib/kubelet, %s", string(umountOutputBytes))
}
dirsToClean := []string{"/var/lib/kubelet"}
// 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
if _, err := os.Stat("/etc/kubernetes/manifests/etcd.json"); os.IsNotExist(err) {
dirsToClean = append(dirsToClean, "/var/lib/etcd")
}
resetConfigDir("/etc/kubernetes/")
fmt.Printf("Deleting contents of stateful directories: %v\n", dirsToClean)
for _, dir := range dirsToClean {
cleanDir(dir)
}
dockerCheck := preflight.ServiceCheck{Service: "docker"}
if warnings, errors := dockerCheck.Check(); len(warnings) == 0 && len(errors) == 0 {
fmt.Println("Stopping all running docker containers...")
if err := exec.Command("sh", "-c", "docker ps | grep 'k8s_' | awk '{print $1}' | xargs docker rm --force --volumes").Run(); err != nil {
fmt.Println("failed to stop the running containers")
}
} else {
fmt.Println("docker doesn't seem to be running, skipping the removal of kubernetes containers")
}
return nil
}

View file

@ -0,0 +1,182 @@
/*
Copyright 2014 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.json": {
setupDirs: []string{
"manifests",
"pki",
},
setupFiles: []string{
"manifests/etcd.json",
"manifests/kube-apiserver.json",
"pki/ca.pem",
"admin.conf",
"kubelet.conf",
"cloud-config.json",
},
verifyExists: []string{
"manifests",
"pki",
"cloud-config.json",
},
},
"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.json",
".mydir/.myfile",
},
verifyExists: []string{
"manifests",
"pki",
".cloud-config.json",
".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)
// 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))
}
}
}

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

@ -0,0 +1,86 @@
/*
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"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/util"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
)
func NewCmdToken(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "token",
Short: "Manage tokens used by init/join",
// 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])
}
},
}
cmd.AddCommand(NewCmdTokenGenerate(out))
return cmd
}
func NewCmdTokenGenerate(out io.Writer) *cobra.Command {
return &cobra.Command{
Use: "generate",
Short: "Generate and print a token suitable for use with init/join",
Long: dedent.Dedent(`
This command will print out a randomly-generated token that you can use 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)
},
}
}
func RunGenerateToken(out io.Writer) error {
s := &kubeadmapi.Secrets{}
err := util.GenerateToken(s)
if err != nil {
return err
}
fmt.Fprintln(out, s.GivenToken)
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 2014 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
}

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

@ -0,0 +1,26 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
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"],
)

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 = "kube-dns"
KubeDNSmasqImage = "dnsmasq"
KubeExechealthzImage = "exechealthz"
Pause = "pause"
gcrPrefix = "gcr.io/google_containers"
etcdVersion = "3.0.14-kubeadm"
kubeDNSVersion = "1.7"
dnsmasqVersion = "1.3"
exechealthzVersion = "1.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, "kubedns", runtime.GOARCH, kubeDNSVersion),
KubeDNSmasqImage: fmt.Sprintf("%s/%s-%s:%s", repoPrefix, "kube-dnsmasq", runtime.GOARCH, dnsmasqVersion),
KubeExechealthzImage: fmt.Sprintf("%s/%s-%s:%s", repoPrefix, "exechealthz", runtime.GOARCH, exechealthzVersion),
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, "kubedns", runtime.GOARCH, kubeDNSVersion),
},
{
KubeDNSmasqImage,
fmt.Sprintf("%s/%s-%s:%s", gcrPrefix, "kube-dnsmasq", runtime.GOARCH, dnsmasqVersion),
},
{
KubeExechealthzImage,
fmt.Sprintf("%s/%s-%s:%s", gcrPrefix, "exechealthz", runtime.GOARCH, exechealthzVersion),
},
{
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,
)
}
}
}

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

@ -0,0 +1,40 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package 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"
"k8s.io/kubernetes/pkg/util/logs"
)
func Run() error {
logs.InitLogs()
defer logs.FlushLogs()
// 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()
}

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

@ -0,0 +1,67 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = [
"addons.go",
"apiclient.go",
"discovery.go",
"kubeconfig.go",
"manifests.go",
"pki.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/errors:go_default_library",
"//pkg/api/resource:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/apis/extensions/v1beta1:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/client/clientset_generated/release_1_5: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",
"//pkg/util/wait:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"addons_test.go",
"apiclient_test.go",
"discovery_test.go",
"kubeconfig_test.go",
"manifests_test.go",
"pki_test.go",
"tokens_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/cert:go_default_library",
"//pkg/util/intstr:go_default_library",
],
)

View file

@ -0,0 +1,264 @@
/*
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"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
ipallocator "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 {
dnsPodResources := v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("100m"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("170Mi"),
}
healthzPodResources := v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("10m"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("50Mi"),
}
kubeDNSPort := int32(10053)
dnsmasqPort := int32(53)
nslookup := fmt.Sprintf("nslookup kubernetes.default.svc.%s 127.0.0.1", cfg.Networking.DNSDomain)
nslookup = fmt.Sprintf("-cmd=%s:%d >/dev/null && %s:%d >/dev/null",
nslookup, dnsmasqPort,
nslookup, kubeDNSPort,
)
return v1.PodSpec{
Containers: []v1.Container{
// DNS server
{
Name: "kube-dns",
Image: images.GetAddonImage(images.KubeDNSImage),
Resources: v1.ResourceRequirements{
Limits: dnsPodResources,
Requests: dnsPodResources,
},
Args: []string{
fmt.Sprintf("--domain=%s", cfg.Networking.DNSDomain),
fmt.Sprintf("--dns-port=%d", kubeDNSPort),
// TODO __PILLAR__FEDERATIONS__DOMAIN__MAP__
},
LivenessProbe: &v1.Probe{
Handler: v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Path: "/healthz",
Port: intstr.FromInt(8080),
Scheme: v1.URISchemeHTTP,
},
},
InitialDelaySeconds: 60,
TimeoutSeconds: 5,
SuccessThreshold: 1,
FailureThreshold: 1,
},
// # 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: 30,
TimeoutSeconds: 5,
},
Ports: []v1.ContainerPort{
{
ContainerPort: kubeDNSPort,
Name: "dns-local",
Protocol: v1.ProtocolUDP,
},
{
ContainerPort: kubeDNSPort,
Name: "dns-tcp-local",
Protocol: v1.ProtocolTCP,
},
},
},
// dnsmasq
{
Name: "dnsmasq",
Image: images.GetAddonImage(images.KubeDNSmasqImage),
Resources: v1.ResourceRequirements{
Limits: dnsPodResources,
Requests: dnsPodResources,
},
Args: []string{
"--cache-size=1000",
"--no-resolv",
fmt.Sprintf("--server=127.0.0.1#%d", kubeDNSPort),
},
Ports: []v1.ContainerPort{
{
ContainerPort: dnsmasqPort,
Name: "dns",
Protocol: v1.ProtocolUDP,
},
{
ContainerPort: dnsmasqPort,
Name: "dns-tcp",
Protocol: v1.ProtocolTCP,
},
},
},
// healthz
{
Name: "healthz",
Image: images.GetAddonImage(images.KubeExechealthzImage),
Resources: v1.ResourceRequirements{
Limits: healthzPodResources,
Requests: healthzPodResources,
},
Args: []string{
nslookup,
"-port=8080",
"-quiet",
},
Ports: []v1.ContainerPort{{
ContainerPort: 8080,
Protocol: v1.ProtocolTCP,
}},
},
},
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)
}
svc := &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(),
}
return svc, 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("<master/addons> failed creating essential kube-proxy addon [%v]", err)
}
fmt.Println("<master/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("<master/addons> failed creating essential kube-dns addon [%v]", err)
}
kubeDNSServiceSpec, err := createKubeDNSServiceSpec(cfg)
if err != nil {
return fmt.Errorf("<master/addons> failed creating essential kube-dns addon - %v", err)
}
kubeDNSService := NewService("kube-dns", *kubeDNSServiceSpec)
if _, err := client.Services(api.NamespaceSystem).Create(kubeDNSService); err != nil {
return fmt.Errorf("<master/addons> failed creating essential kube-dns addon [%v]", err)
}
fmt.Println("<master/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,279 @@
/*
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"
"k8s.io/kubernetes/cmd/kubeadm/app/images"
"k8s.io/kubernetes/pkg/api"
apierrs "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/v1"
extensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
"k8s.io/kubernetes/pkg/util/wait"
)
const apiCallRetryInterval = 500 * time.Millisecond
func CreateClientAndWaitForAPI(adminConfig *clientcmdapi.Config) (*clientset.Clientset, error) {
adminClientConfig, err := clientcmd.NewDefaultClientConfig(
*adminConfig,
&clientcmd.ConfigOverrides{},
).ClientConfig()
if err != nil {
return nil, fmt.Errorf("<master/apiclient> failed to create API client configuration [%v]", err)
}
fmt.Println("<master/apiclient> created API client configuration")
client, err := clientset.NewForConfig(adminClientConfig)
if err != nil {
return nil, fmt.Errorf("<master/apiclient> failed to create API client [%v]", err)
}
fmt.Println("<master/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("<master/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("<master/apiclient> control plane component %q is still unhealthy: %#v\n", item.ObjectMeta.Name, item.Conditions)
return false, nil
}
}
}
fmt.Printf("<master/apiclient> all control plane components are healthy after %f seconds\n", time.Since(start).Seconds())
return true, nil
})
fmt.Println("<master/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("<master/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("<master/apiclient> first node has registered, but is not ready yet")
return false, nil
}
fmt.Printf("<master/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("<master/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("<master/apiclient> 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("<master/apiclient> attempting 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("<master/apiclient> failed to create test deployment [%v] (will retry)", err)
return false, nil
}
return true, nil
})
wait.PollInfinite(apiCallRetryInterval, func() (bool, error) {
d, err := client.Extensions().Deployments(api.NamespaceSystem).Get("dummy")
if err != nil {
fmt.Printf("<master/apiclient> failed to get test deployment [%v] (will retry)", err)
return false, nil
}
if d.Status.AvailableReplicas < 1 {
return false, nil
}
return true, nil
})
fmt.Println("<master/apiclient> test deployment succeeded")
if err := client.Extensions().Deployments(api.NamespaceSystem).Delete("dummy", &v1.DeleteOptions{}); err != nil {
fmt.Printf("<master/apiclient> failed to delete test deployment [%v] (will ignore)", 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,148 @@
/*
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"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
extensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
certutil "k8s.io/kubernetes/pkg/util/cert"
"k8s.io/kubernetes/pkg/util/wait"
)
type kubeDiscovery struct {
Deployment *extensions.Deployment
Secret *v1.Secret
}
const (
kubeDiscoveryName = "kube-discovery"
kubeDiscoverySecretName = "clusterinfo"
)
func encodeKubeDiscoverySecretData(cfg *kubeadmapi.MasterConfiguration, caCert *x509.Certificate) map[string][]byte {
var (
data = map[string][]byte{}
endpointList = []string{}
tokenMap = map[string]string{}
)
for _, addr := range cfg.API.AdvertiseAddresses {
endpointList = append(endpointList, fmt.Sprintf("https://%s:%d", addr, cfg.API.BindPort))
}
tokenMap[cfg.Secrets.TokenID] = cfg.Secrets.BearerToken
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
// buisness 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: cfg.Discovery.BindPort},
},
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, 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("<master/discovery> failed to create %q deployment [%v]", kubeDiscoveryName, err)
}
if _, err := client.Secrets(api.NamespaceSystem).Create(kd.Secret); err != nil {
return fmt.Errorf("<master/discovery> failed to create %q secret [%v]", kubeDiscoverySecretName, err)
}
fmt.Println("<master/discovery> created essential addon: kube-discovery, waiting for it to become ready")
start := time.Now()
wait.PollInfinite(apiCallRetryInterval, func() (bool, error) {
d, err := client.Extensions().Deployments(api.NamespaceSystem).Get(kubeDiscoveryName)
if err != nil {
return false, nil
}
if d.Status.AvailableReplicas < 1 {
return false, nil
}
return true, nil
})
fmt.Printf("<master/discovery> kube-discovery is ready after %f seconds\n", time.Since(start).Seconds())
return nil
}

View file

@ -0,0 +1,104 @@
/*
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 TestEncodeKubeDiscoverySecretData(t *testing.T) {
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
expected bool
}{
{
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadmapi.API{BindPort: 123, AdvertiseAddresses: []string{"10.0.0.1"}},
Networking: kubeadmapi.Networking{ServiceSubnet: "10.0.0.1/1"},
},
expected: true,
},
}
for _, rt := range tests {
caCert := &x509.Certificate{}
actual := encodeKubeDiscoverySecretData(rt.cfg, caCert)
if (actual != nil) != rt.expected {
t.Errorf(
"failed encodeKubeDiscoverySecretData, return map[string][]byte was nil",
)
}
}
}
func TestNewKubeDiscoveryPodSpec(t *testing.T) {
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
p int32
expected bool
}{
{
cfg: &kubeadmapi.MasterConfiguration{
Discovery: kubeadmapi.Discovery{BindPort: 123},
},
p: 123,
},
{
cfg: &kubeadmapi.MasterConfiguration{
Discovery: kubeadmapi.Discovery{BindPort: 456},
},
p: 456,
},
}
for _, rt := range tests {
actual := newKubeDiscoveryPodSpec(rt.cfg)
if actual.Containers[0].Ports[0].HostPort != rt.p {
t.Errorf(
"failed newKubeDiscoveryPodSpec:\n\texpected: %d\n\t actual: %d",
rt.p,
actual.Containers[0].Ports[0].HostPort,
)
}
}
}
func TestNewKubeDiscovery(t *testing.T) {
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
caCert *x509.Certificate
expected bool
}{
{
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadmapi.API{BindPort: 123, AdvertiseAddresses: []string{"10.0.0.1"}},
Networking: kubeadmapi.Networking{ServiceSubnet: "10.0.0.1/1"},
},
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,60 @@
/*
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/rsa"
"crypto/x509"
"fmt"
// TODO: "k8s.io/client-go/client/tools/clientcmd/api"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
certutil "k8s.io/kubernetes/pkg/util/cert"
)
func CreateCertsAndConfigForClients(cfg kubeadmapi.API, clientNames []string, caKey *rsa.PrivateKey, caCert *x509.Certificate) (map[string]*clientcmdapi.Config, error) {
basicClientConfig := kubeadmutil.CreateBasicClientConfig(
"kubernetes",
// 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
fmt.Sprintf("https://%s:%d", cfg.AdvertiseAddresses[0], cfg.BindPort),
certutil.EncodeCertPEM(caCert),
)
configs := map[string]*clientcmdapi.Config{}
for _, client := range clientNames {
key, cert, err := newClientKeyAndCert(caCert, caKey)
if err != nil {
return nil, fmt.Errorf("<master/kubeconfig> failure while creating %s client certificate - %v", client, err)
}
config := kubeadmutil.MakeClientConfigWithCerts(
basicClientConfig,
"kubernetes",
client,
certutil.EncodePrivateKeyPEM(key),
certutil.EncodeCertPEM(cert),
)
configs[client] = config
}
return configs, nil
}

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 master
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"testing"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func TestCreateCertsAndConfigForClients(t *testing.T) {
var tests = []struct {
a kubeadmapi.API
cn []string
caKeySize int
expected bool
}{
{
a: kubeadmapi.API{AdvertiseAddresses: []string{"foo"}},
cn: []string{"localhost"},
caKeySize: 128,
expected: false,
},
{
a: kubeadmapi.API{AdvertiseAddresses: []string{"foo"}},
cn: []string{},
caKeySize: 128,
expected: true,
},
{
a: kubeadmapi.API{AdvertiseAddresses: []string{"foo"}},
cn: []string{"localhost"},
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 := CreateCertsAndConfigForClients(rt.a, rt.cn, caKey, caCert)
if (actual == nil) != rt.expected {
t.Errorf(
"failed CreateCertsAndConfigForClients:\n\texpected: %t\n\t actual: %t",
rt.expected,
(actual == nil),
)
}
}
}

View file

@ -0,0 +1,316 @@
/*
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"
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"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/util/intstr"
)
// Static pod definitions in golang form are included below so that `kubeadm init` can get going.
const (
DefaultClusterName = "kubernetes"
DefaultCloudConfigPath = "/etc/kubernetes/cloud-config.json"
etcd = "etcd"
apiServer = "apiserver"
controllerManager = "controller-manager"
scheduler = "scheduler"
proxy = "proxy"
kubeAPIServer = "kube-apiserver"
kubeControllerManager = "kube-controller-manager"
kubeScheduler = "kube-scheduler"
kubeProxy = "kube-proxy"
pkiDir = "/etc/kubernetes/pki"
)
// 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 {
// 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: []api.VolumeMount{certsVolumeMount(), k8sVolumeMount()},
LivenessProbe: componentProbe(8080, "/healthz"),
Resources: componentResources("250m"),
}, certsVolume(cfg), k8sVolume(cfg)),
kubeControllerManager: componentPod(api.Container{
Name: kubeControllerManager,
Image: images.GetCoreImage(images.KubeControllerManagerImage, cfg, kubeadmapi.GlobalEnvParams.HyperkubeImage),
Command: getControllerManagerCommand(cfg),
VolumeMounts: []api.VolumeMount{certsVolumeMount(), k8sVolumeMount()},
LivenessProbe: componentProbe(10252, "/healthz"),
Resources: componentResources("200m"),
}, certsVolume(cfg), k8sVolume(cfg)),
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"),
}),
}
// 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/etcd/data",
},
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("<master/manifests> 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("<master/manifests> failed to marshall manifest for %q to JSON [%v]", name, err)
}
if err := cmdutil.DumpReaderToFile(bytes.NewReader(serialized), filename); err != nil {
return fmt.Errorf("<master/manifests> 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/etcd",
}
}
// 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 k8sVolume(cfg *kubeadmapi.MasterConfiguration) api.Volume {
return api.Volume{
Name: "pki",
VolumeSource: api.VolumeSource{
HostPath: &api.HostPathVolumeSource{Path: kubeadmapi.GlobalEnvParams.KubernetesDir},
},
}
}
func k8sVolumeMount() api.VolumeMount {
return api.VolumeMount{
Name: "pki",
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) (command []string) {
if kubeadmapi.GlobalEnvParams.HyperkubeImage != "" {
command = []string{"/hyperkube", component}
} else {
command = []string{"kube-" + component}
}
command = append(command, kubeadmapi.GlobalEnvParams.ComponentLoglevel)
return
}
func getAPIServerCommand(cfg *kubeadmapi.MasterConfiguration) (command []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="+pkiDir+"/apiserver-key.pem",
"--client-ca-file="+pkiDir+"/ca.pem",
"--tls-cert-file="+pkiDir+"/apiserver.pem",
"--tls-private-key-file="+pkiDir+"/apiserver-key.pem",
"--token-auth-file="+pkiDir+"/tokens.csv",
fmt.Sprintf("--secure-port=%d", cfg.API.BindPort),
"--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]))
}
// 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)
}
return
}
func getControllerManagerCommand(cfg *kubeadmapi.MasterConfiguration) (command []string) {
command = append(getComponentBaseCommand(controllerManager),
"--address=127.0.0.1",
"--leader-elect",
"--master=127.0.0.1:8080",
"--cluster-name="+DefaultClusterName,
"--root-ca-file="+pkiDir+"/ca.pem",
"--service-account-private-key-file="+pkiDir+"/apiserver-key.pem",
"--cluster-signing-cert-file="+pkiDir+"/ca.pem",
"--cluster-signing-key-file="+pkiDir+"/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
// TODO(phase1+) this won't work unless it's in one of the few directories we bind-mount
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
}
func getSchedulerCommand(cfg *kubeadmapi.MasterConfiguration) (command []string) {
command = append(getComponentBaseCommand(scheduler),
"--address=127.0.0.1",
"--leader-elect",
"--master=127.0.0.1:8080",
)
return
}
func getProxyCommand(cfg *kubeadmapi.MasterConfiguration) (command []string) {
command = getComponentBaseCommand(proxy)
return
}

File diff suppressed because it is too large Load diff

200
vendor/k8s.io/kubernetes/cmd/kubeadm/app/master/pki.go generated vendored Normal file
View file

@ -0,0 +1,200 @@
/*
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/rsa"
"crypto/x509"
"fmt"
"net"
"path"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
ipallocator "k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
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(cfg *kubeadmapi.MasterConfiguration, 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)
}
internalAPIServerFQDN := []string{
"kubernetes",
"kubernetes.default",
"kubernetes.default.svc",
fmt.Sprintf("kubernetes.default.svc.%s", cfg.Networking.DNSDomain),
}
_, n, err := net.ParseCIDR(cfg.Networking.ServiceSubnet)
if err != nil {
return nil, nil, fmt.Errorf("error parsing CIDR %q: %v", cfg.Networking.ServiceSubnet, err)
}
internalAPIServerVirtualIP, err := ipallocator.GetIndexedIP(n, 1)
if err != nil {
return nil, 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)
altNames.DNSNames = append(altNames.DNSNames, internalAPIServerFQDN...)
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-admin",
}
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))
}
func newServiceAccountKey() (*rsa.PrivateKey, error) {
key, err := certutil.NewPrivateKey()
if err != nil {
return nil, err
}
return key, nil
}
// 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) (*rsa.PrivateKey, *x509.Certificate, error) {
var (
err error
altNames certutil.AltNames
)
for _, a := range cfg.API.AdvertiseAddresses {
if ip := net.ParseIP(a); ip != nil {
altNames.IPs = append(altNames.IPs, ip)
} else {
return nil, nil, fmt.Errorf("could not parse ip %q", a)
}
}
altNames.DNSNames = append(altNames.DNSNames, cfg.API.ExternalDNSNames...)
pkiPath := path.Join(kubeadmapi.GlobalEnvParams.HostPKIPath)
caKey, caCert, err := newCertificateAuthority()
if err != nil {
return nil, nil, fmt.Errorf("<master/pki> failure while creating CA keys and certificate - %v", err)
}
if err := writeKeysAndCert(pkiPath, "ca", caKey, caCert); err != nil {
return nil, nil, fmt.Errorf("<master/pki> failure while saving CA keys and certificate - %v", err)
}
fmt.Printf("<master/pki> generated Certificate Authority key and certificate:\n%s\n", certutil.FormatCert(caCert))
pub, prv, cert := pathsKeysCerts(pkiPath, "ca")
fmt.Printf("Public: %s\nPrivate: %s\nCert: %s\n", pub, prv, cert)
apiKey, apiCert, err := newServerKeyAndCert(cfg, caCert, caKey, altNames)
if err != nil {
return nil, nil, fmt.Errorf("<master/pki> failure while creating API server keys and certificate - %v", err)
}
if err := writeKeysAndCert(pkiPath, "apiserver", apiKey, apiCert); err != nil {
return nil, nil, fmt.Errorf("<master/pki> failure while saving API server keys and certificate - %v", err)
}
fmt.Printf("<master/pki> generated API Server key and certificate:\n%s\n", certutil.FormatCert(apiCert))
pub, prv, cert = pathsKeysCerts(pkiPath, "apiserver")
fmt.Printf("Public: %s\nPrivate: %s\nCert: %s\n", pub, prv, cert)
saKey, err := newServiceAccountKey()
if err != nil {
return nil, nil, fmt.Errorf("<master/pki> failure while creating service account signing keys [%v]", err)
}
if err := writeKeysAndCert(pkiPath, "sa", saKey, nil); err != nil {
return nil, nil, fmt.Errorf("<master/pki> failure while saving service account signing keys - %v", err)
}
fmt.Printf("<master/pki> generated Service Account Signing keys:\n")
pub, prv, _ = pathsKeysCerts(pkiPath, "sa")
fmt.Printf("Public: %s\nPrivate: %s\n", pub, prv)
fmt.Printf("<master/pki> created keys and certificates in %q\n", pkiPath)
return caKey, caCert, nil
}

View file

@ -0,0 +1,258 @@
/*
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/rand"
"crypto/rsa"
"crypto/x509"
"fmt"
"io/ioutil"
"os"
"testing"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
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 {
cfg *kubeadmapi.MasterConfiguration
caKeySize int
expected bool
}{
{
// given CIDR too small
cfg: &kubeadmapi.MasterConfiguration{
Networking: kubeadm.Networking{ServiceSubnet: "10.0.0.1/1"},
},
caKeySize: 2048,
expected: false,
},
{
// bad CIDR
cfg: &kubeadmapi.MasterConfiguration{
Networking: kubeadm.Networking{ServiceSubnet: "foo"},
},
caKeySize: 2048,
expected: false,
},
{
// RSA key too small
cfg: &kubeadmapi.MasterConfiguration{
Networking: kubeadm.Networking{ServiceSubnet: "10.0.0.1/24"},
},
caKeySize: 128,
expected: false,
},
{
cfg: &kubeadmapi.MasterConfiguration{
Networking: kubeadm.Networking{ServiceSubnet: "10.0.0.1/24"},
},
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(rt.cfg, 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],
)
}
}
}
}
func TestNewServiceAccountKey(t *testing.T) {
r, err := newServiceAccountKey()
if r == nil {
t.Errorf(
"failed newServiceAccountKey, rsa key == nil",
)
}
if err != nil {
t.Errorf(
"failed newServiceAccountKey with an error: %v",
err,
)
}
}
func TestCreatePKIAssets(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.HostPKIPath = fmt.Sprintf("%s/etc/kubernetes/pki", tmpdir)
defer func() { kubeadmapi.GlobalEnvParams = oldEnv }()
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
expected bool
}{
{
cfg: &kubeadmapi.MasterConfiguration{},
expected: false,
},
{
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadm.API{AdvertiseAddresses: []string{"10.0.0.1"}},
Networking: kubeadm.Networking{ServiceSubnet: "10.0.0.1/1"},
},
expected: false,
},
{
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadm.API{AdvertiseAddresses: []string{"10.0.0.1"}},
Networking: kubeadm.Networking{ServiceSubnet: "10.0.0.1/24"},
},
expected: true,
},
}
for _, rt := range tests {
_, _, actual := CreatePKIAssets(rt.cfg)
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,64 @@
/*
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"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/util/uuid"
)
func generateTokenIfNeeded(s *kubeadmapi.Secrets) error {
ok, err := kubeadmutil.UseGivenTokenIfValid(s)
// TODO(phase1+) @krousey: I know it won't happen with the way it is currently implemented, but this doesn't handle case where ok is true and err is non-nil.
if !ok {
if err != nil {
return err
}
err = kubeadmutil.GenerateToken(s)
if err != nil {
return err
}
fmt.Printf("<master/tokens> generated token: %q\n", s.GivenToken)
} else {
fmt.Println("<master/tokens> accepted provided token")
}
return nil
}
func CreateTokenAuthFile(s *kubeadmapi.Secrets) error {
tokenAuthFilePath := path.Join(kubeadmapi.GlobalEnvParams.HostPKIPath, "tokens.csv")
if err := generateTokenIfNeeded(s); err != nil {
return fmt.Errorf("<master/tokens> failed to generate token(s) [%v]", err)
}
if err := os.MkdirAll(kubeadmapi.GlobalEnvParams.HostPKIPath, 0700); err != nil {
return fmt.Errorf("<master/tokens> failed to create directory %q [%v]", kubeadmapi.GlobalEnvParams.HostPKIPath, err)
}
serialized := []byte(fmt.Sprintf("%s,kubeadm-node-csr,%s,system:kubelet-bootstrap\n", s.BearerToken, uuid.NewUUID()))
// DumpReaderToFile create a file with mode 0600
if err := cmdutil.DumpReaderToFile(bytes.NewReader(serialized), tokenAuthFilePath); err != nil {
return fmt.Errorf("<master/tokens> failed to save token auth file (%q) [%v]", tokenAuthFilePath, err)
}
return nil
}

View file

@ -0,0 +1,83 @@
/*
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"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func TestGenerateTokenIfNeeded(t *testing.T) {
var tests = []struct {
s kubeadmapi.Secrets
expected bool
}{
{kubeadmapi.Secrets{GivenToken: "noperiod"}, false}, // not 2-part '.' format
{kubeadmapi.Secrets{GivenToken: "abcd.a"}, false}, // len(tokenID) != 6
{kubeadmapi.Secrets{GivenToken: "abcdef.a"}, true},
{kubeadmapi.Secrets{GivenToken: ""}, true},
}
for _, rt := range tests {
actual := generateTokenIfNeeded(&rt.s)
if (actual == nil) != rt.expected {
t.Errorf(
"failed UseGivenTokenIfValid:\n\texpected: %t\n\t actual: %t\n\t token:%s",
rt.expected,
(actual == nil),
rt.s.GivenToken,
)
}
}
}
func TestCreateTokenAuthFile(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.HostPKIPath = fmt.Sprintf("%s/etc/kubernetes/pki", tmpdir)
defer func() { kubeadmapi.GlobalEnvParams = oldEnv }()
var tests = []struct {
s kubeadmapi.Secrets
expected bool
}{
{kubeadmapi.Secrets{GivenToken: "noperiod"}, false}, // not 2-part '.' format
{kubeadmapi.Secrets{GivenToken: "abcd.a"}, false}, // len(tokenID) != 6
{kubeadmapi.Secrets{GivenToken: "abcdef.a"}, true},
{kubeadmapi.Secrets{GivenToken: ""}, true},
}
for _, rt := range tests {
actual := CreateTokenAuthFile(&rt.s)
if (actual == nil) != rt.expected {
t.Errorf(
"failed WriteKubeconfigIfNotExists with an error:\n\texpected: %t\n\t actual: %t",
rt.expected,
(actual == nil),
)
}
}
}

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

@ -0,0 +1,55 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
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/util:go_default_library",
"//pkg/apis/certificates:go_default_library",
"//pkg/client/clientset_generated/release_1_5:go_default_library",
"//pkg/client/clientset_generated/release_1_5/typed/certificates/v1alpha1: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/types:go_default_library",
"//pkg/util/cert:go_default_library",
"//pkg/util/wait:go_default_library",
"//vendor:github.com/square/go-jose",
],
)
go_test(
name = "go_default_test",
srcs = [
"bootstrap_test.go",
"csr_test.go",
"discovery_test.go",
],
library = "go_default_library",
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/client/clientset_generated/release_1_5:go_default_library",
"//pkg/client/clientset_generated/release_1_5/typed/certificates/v1alpha1:go_default_library",
"//pkg/client/restclient:go_default_library",
"//pkg/client/typed/discovery:go_default_library",
"//pkg/version:go_default_library",
],
)

View file

@ -0,0 +1,148 @@
/*
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"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/pkg/apis/certificates"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
certclient "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5/typed/certificates/v1alpha1"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
"k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util/wait"
)
// ConnectionDetails represents a master API endpoint connection
type ConnectionDetails struct {
CertClient *certclient.CertificatesV1alpha1Client
Endpoint string
CACert []byte
NodeName types.NodeName
}
// retryTimeout between the subsequent attempts to connect
// to an API endpoint
const retryTimeout = 5
// 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.
func EstablishMasterConnection(s *kubeadmapi.NodeConfiguration, clusterInfo *kubeadmapi.ClusterInfo) (*ConnectionDetails, error) {
hostName, err := os.Hostname()
if err != nil {
return nil, fmt.Errorf("<node/bootstrap> 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{})
result := make(chan *ConnectionDetails)
var wg sync.WaitGroup
for _, endpoint := range endpoints {
clientSet, err := createClients(caCert, endpoint, s.Secrets.BearerToken, nodeName)
if err != nil {
fmt.Printf("<node/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("<node/bootstrap> trying to connect to endpoint %s\n", apiEndpoint)
err := checkAPIEndpoint(clientSet, apiEndpoint)
if err != nil {
fmt.Printf("<node/bootstrap> endpoint check failed [%v]\n", err)
return
}
fmt.Printf("<node/bootstrap> successfully established connection with endpoint %s\n", apiEndpoint)
// connection established, stop all wait threads
close(stopChan)
result <- &ConnectionDetails{
CertClient: clientSet.CertificatesV1alpha1Client,
Endpoint: apiEndpoint,
CACert: caCert,
NodeName: nodeName,
}
}, retryTimeout*time.Second, stopChan)
}(endpoint)
}
go func() {
wg.Wait()
// all wait.Until() calls have finished now
close(result)
}()
establishedConnection, ok := <-result
if !ok {
return nil, fmt.Errorf("<node/bootstrap> failed to create bootstrap clients " +
"for any of the provided API endpoints")
}
return establishedConnection, nil
}
// creates a set of clients for this endpoint
func createClients(caCert []byte, endpoint, token string, nodeName types.NodeName) (*clientset.Clientset, error) {
bareClientConfig := kubeadmutil.CreateBasicClientConfig("kubernetes", endpoint, caCert)
bootstrapClientConfig, err := clientcmd.NewDefaultClientConfig(
*kubeadmutil.MakeClientConfigWithToken(
bareClientConfig, "kubernetes", fmt.Sprintf("kubelet-%s", nodeName), token,
),
&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 %s [%v]", endpoint, err)
}
return clientSet, 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 %s [%v]", endpoint, err)
}
fmt.Printf("<node/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"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
"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.NodeConfiguration{}
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),
)
}
}
}

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

@ -0,0 +1,57 @@
/*
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"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
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 certificate signing request with the
// provided connection details.
func PerformTLSBootstrap(connection *ConnectionDetails) (*clientcmdapi.Config, error) {
csrClient := connection.CertClient.CertificateSigningRequests()
fmt.Println("<node/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 nil, fmt.Errorf("<node/csr> failed to generating private key [%v]", err)
}
cert, err := csr.RequestNodeCertificate(csrClient, key, connection.NodeName)
if err != nil {
return nil, fmt.Errorf("<node/csr> failed to request signed certificate from the API server [%v]", err)
}
fmtCert, err := certutil.FormatBytesCert(cert)
if err != nil {
return nil, fmt.Errorf("<node/csr> failed to format certificate [%v]", err)
}
fmt.Printf("<node/csr> received signed certificate from the API server:\n%s\n", fmtCert)
fmt.Println("<node/csr> generating kubelet configuration")
bareClientConfig := kubeadmutil.CreateBasicClientConfig("kubernetes", connection.Endpoint, connection.CACert)
finalConfig := kubeadmutil.MakeClientConfigWithCerts(
bareClientConfig, "kubernetes", fmt.Sprintf("kubelet-%s", connection.NodeName),
key, cert,
)
return finalConfig, nil
}

View file

@ -0,0 +1,79 @@
/*
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"
certclient "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5/typed/certificates/v1alpha1"
restclient "k8s.io/kubernetes/pkg/client/restclient"
)
func TestPerformTLSBootstrap(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
switch req.URL.Path {
default:
output, err := json.Marshal(nil)
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 {
h string
expect bool
}{
{
h: "",
expect: false,
},
{
h: "localhost",
expect: false,
},
{
h: srv.URL,
expect: false,
},
}
for _, rt := range tests {
cd := &ConnectionDetails{}
r := &restclient.Config{Host: rt.h}
tmpConfig, err := certclient.NewForConfig(r)
if err != nil {
t.Fatalf("encountered an error while trying to get New Cert Client: %v", err)
}
cd.CertClient = tmpConfig
_, actual := PerformTLSBootstrap(cd)
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,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 node
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
jose "github.com/square/go-jose"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/pkg/util/wait"
)
// the amount of time to wait between each request to the discovery API
const discoveryRetryTimeout = 5 * time.Second
func RetrieveTrustedClusterInfo(s *kubeadmapi.NodeConfiguration) (*kubeadmapi.ClusterInfo, error) {
host, port := s.MasterAddresses[0], s.DiscoveryPort
requestURL := fmt.Sprintf("http://%s:%d/cluster-info/v1/?token-id=%s", host, port, s.Secrets.TokenID)
req, err := http.NewRequest("GET", requestURL, nil)
if err != nil {
return nil, fmt.Errorf("<node/discovery> failed to consturct an HTTP request [%v]", err)
}
fmt.Printf("<node/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("<node/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("<node/discovery> failed to parse response as JWS object [%v]", err)
}
fmt.Println("<node/discovery> cluster info object received, verifying signature using given token")
output, err := object.Verify(s.Secrets.Token)
if err != nil {
return nil, fmt.Errorf("<node/discovery> 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("<node/discovery> failed to decode received cluster info object [%v]", err)
}
if len(clusterInfo.CertificateAuthorities) == 0 || len(clusterInfo.Endpoints) == 0 {
return nil, fmt.Errorf("<node/discovery> 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("<node/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 int32
payload string
expect bool
}{
{
h: host,
p: int32(iPort),
payload: "",
expect: false,
},
{
h: host,
p: int32(iPort),
payload: "foo",
expect: false,
},
}
for _, rt := range tests {
j.Payload = rt.payload
nc := &kubeadmapi.NodeConfiguration{MasterAddresses: []string{host}, DiscoveryPort: int32(iPort)}
_, 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,32 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
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",
],
)
go_test(
name = "go_default_test",
srcs = ["checks_test.go"],
library = "go_default_library",
tags = ["automanaged"],
deps = [],
)

View file

@ -0,0 +1,359 @@
/*
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"
"errors"
"fmt"
"io"
"net"
"net/http"
"os"
"os/exec"
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"
)
type PreFlightError struct {
Msg string
}
func (e *PreFlightError) Error() string {
return fmt.Sprintf("preflight check errors:\n%s", e.Msg)
}
// PreFlightCheck validates the state of the system to ensure kubeadm will be
// successful as often as possilble.
type PreFlightCheck 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
}
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 !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 {
root bool
}
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
}
// InPathChecks 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
bufw := bufio.NewWriterSize(os.Stdout, 1*1024*1024)
// Run the system verification check, but write to out buffered writer instead of stdout
err := system.Validate(system.DefaultSysSpec, &system.StreamReporter{WriteStream: bufw})
if err != nil {
// Only print the output from the system verification check if the check failed
fmt.Println("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 := []PreFlightCheck{
SystemVerificationCheck{},
IsRootCheck{root: true},
HostnameCheck{},
ServiceCheck{Service: "kubelet"},
ServiceCheck{Service: "docker"},
FirewalldCheck{ports: []int{int(cfg.API.BindPort), int(cfg.Discovery.BindPort), 10250}},
PortOpenCheck{port: int(cfg.API.BindPort)},
PortOpenCheck{port: 8080},
PortOpenCheck{port: int(cfg.Discovery.BindPort)},
PortOpenCheck{port: 10250},
PortOpenCheck{port: 10251},
PortOpenCheck{port: 10252},
HttpProxyCheck{Proto: "https", Host: cfg.API.AdvertiseAddresses[0], Port: int(cfg.API.BindPort)},
DirAvailableCheck{Path: "/etc/kubernetes/manifests"},
DirAvailableCheck{Path: "/etc/kubernetes/pki"},
DirAvailableCheck{Path: "/var/lib/kubelet"},
FileAvailableCheck{Path: "/etc/kubernetes/admin.conf"},
FileAvailableCheck{Path: "/etc/kubernetes/kubelet.conf"},
InPathCheck{executable: "ebtables", mandatory: true},
InPathCheck{executable: "ethtool", mandatory: true},
InPathCheck{executable: "ip", mandatory: true},
InPathCheck{executable: "iptables", mandatory: true},
InPathCheck{executable: "mount", mandatory: true},
InPathCheck{executable: "nsenter", mandatory: true},
InPathCheck{executable: "socat", mandatory: true},
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 := []PreFlightCheck{
SystemVerificationCheck{},
IsRootCheck{root: true},
HostnameCheck{},
ServiceCheck{Service: "docker"},
ServiceCheck{Service: "kubelet"},
PortOpenCheck{port: 10250},
HttpProxyCheck{Proto: "https", Host: cfg.MasterAddresses[0], Port: int(cfg.APIPort)},
HttpProxyCheck{Proto: "http", Host: cfg.MasterAddresses[0], Port: int(cfg.DiscoveryPort)},
DirAvailableCheck{Path: "/etc/kubernetes/manifests"},
DirAvailableCheck{Path: "/var/lib/kubelet"},
FileAvailableCheck{Path: "/etc/kubernetes/kubelet.conf"},
InPathCheck{executable: "ebtables", mandatory: true},
InPathCheck{executable: "ethtool", mandatory: true},
InPathCheck{executable: "ip", mandatory: true},
InPathCheck{executable: "iptables", mandatory: true},
InPathCheck{executable: "mount", mandatory: true},
InPathCheck{executable: "nsenter", mandatory: true},
InPathCheck{executable: "socat", mandatory: true},
InPathCheck{executable: "tc", mandatory: false},
InPathCheck{executable: "touch", mandatory: false},
}
return runChecks(checks, os.Stderr)
}
func RunResetCheck() error {
checks := []PreFlightCheck{
IsRootCheck{root: true},
}
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 []PreFlightCheck, ww io.Writer) error {
found := []error{}
for _, c := range checks {
warnings, errs := c.Check()
for _, w := range warnings {
io.WriteString(ww, fmt.Sprintf("WARNING: %s\n", w))
}
for _, e := range errs {
found = append(found, e)
}
}
if len(found) > 0 {
errs := ""
for _, i := range found {
errs += "\t" + i.Error() + "\n"
}
return errors.New(errs)
}
return nil
}

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 []PreFlightCheck
expected bool
output string
}{
{[]PreFlightCheck{}, true, ""},
{[]PreFlightCheck{preflightCheckTest{"warning"}}, true, "WARNING: warning\n"}, // should just print warning
{[]PreFlightCheck{preflightCheckTest{"error"}}, false, ""},
{[]PreFlightCheck{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(),
)
}
}
}

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

@ -0,0 +1,47 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = [
"error.go",
"kubeconfig.go",
"tokens.go",
"version.go",
],
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library",
"//pkg/client/unversioned/clientcmd:go_default_library",
"//pkg/client/unversioned/clientcmd/api:go_default_library",
"//vendor:github.com/golang/glog",
"//vendor:github.com/renstrom/dedent",
],
)
go_test(
name = "go_default_test",
srcs = [
"error_test.go",
"kubeconfig_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",
"//pkg/client/unversioned/clientcmd/api:go_default_library",
],
)

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

@ -0,0 +1,92 @@
/*
Copyright 2014 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"
"github.com/golang/glog"
"github.com/renstrom/dedent"
)
const (
DefaultErrorExitCode = 1
PreFlight = 2
)
var AlphaWarningOnExit = dedent.Dedent(`
kubeadm: I am an alpha version, my authors welcome your feedback and bug reports
kubeadm: please create an issue using https://github.com/kubernetes/kubernetes/issues/new
kubeadm: and make sure to mention @kubernetes/sig-cluster-lifecycle. Thank you!
`)
type debugError interface {
DebugError() (msg string, args []interface{})
}
var fatalErrHandler = fatal
// BehaviorOnFatal allows you to override the default behavior when a fatal
// error occurs, which is to call os.Exit(code). You can pass 'panic' as a function
// here if you prefer the panic() over os.Exit(1).
func BehaviorOnFatal(f func(string, int)) {
fatalErrHandler = f
}
// fatal prints the message if set and then exits. If V(2) or greater, glog.Fatal
// is invoked for extended information.
func fatal(msg string, code int) {
if len(msg) > 0 {
// add newline if needed
if !strings.HasSuffix(msg, "\n") {
msg += "\n"
}
if glog.V(2) {
glog.FatalDepth(2, msg)
}
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, fatalErrHandler)
}
// 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.PreFlightError:
handleErr(err.Error(), PreFlight)
default:
fmt.Printf(AlphaWarningOnExit)
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.PreFlightError{}, PreFlight},
}
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,
)
}
}
}

View file

@ -0,0 +1,97 @@
/*
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"
"path"
// TODO: "k8s.io/client-go/client/tools/clientcmd/api"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
)
func CreateBasicClientConfig(clusterName string, serverURL string, caCert []byte) *clientcmdapi.Config {
cluster := clientcmdapi.NewCluster()
cluster.Server = serverURL
cluster.CertificateAuthorityData = caCert
config := clientcmdapi.NewConfig()
config.Clusters[clusterName] = cluster
return config
}
func MakeClientConfigWithCerts(config *clientcmdapi.Config, clusterName string, userName string, clientKey []byte, clientCert []byte) *clientcmdapi.Config {
newConfig := config
name := fmt.Sprintf("%s@%s", userName, clusterName)
authInfo := clientcmdapi.NewAuthInfo()
authInfo.ClientKeyData = clientKey
authInfo.ClientCertificateData = clientCert
context := clientcmdapi.NewContext()
context.Cluster = clusterName
context.AuthInfo = userName
newConfig.AuthInfos[userName] = authInfo
newConfig.Contexts[name] = context
newConfig.CurrentContext = name
return newConfig
}
func MakeClientConfigWithToken(config *clientcmdapi.Config, clusterName string, userName string, token string) *clientcmdapi.Config {
newConfig := config
name := fmt.Sprintf("%s@%s", userName, clusterName)
authInfo := clientcmdapi.NewAuthInfo()
authInfo.Token = token
context := clientcmdapi.NewContext()
context.Cluster = clusterName
context.AuthInfo = userName
newConfig.AuthInfos[userName] = authInfo
newConfig.Contexts[name] = context
newConfig.CurrentContext = name
return newConfig
}
func WriteKubeconfigIfNotExists(name string, kubeconfig *clientcmdapi.Config) error {
if err := os.MkdirAll(kubeadmapi.GlobalEnvParams.KubernetesDir, 0700); err != nil {
return fmt.Errorf("<util/kubeconfig> failed to create directory %q [%v]", kubeadmapi.GlobalEnvParams.KubernetesDir, err)
}
filename := path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, fmt.Sprintf("%s.conf", name))
// Create and open the file, only if it does not already exist.
f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0600)
if err != nil {
return fmt.Errorf("<util/kubeconfig> failed to create %q, it already exists [%v]", filename, err)
}
f.Close()
if err := clientcmd.WriteToFile(*kubeconfig, filename); err != nil {
return fmt.Errorf("<util/kubeconfig> failed to write to %q [%v]", filename, err)
}
fmt.Printf("<util/kubeconfig> created %q\n", filename)
return nil
}

View file

@ -0,0 +1,199 @@
/*
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 (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
)
const (
configOut1 = `apiVersion: v1
clusters:
- cluster:
server: ""
name: ""
contexts: []
current-context: ""
kind: Config
preferences: {}
users: []
`
configOut2 = `apiVersion: v1
clusters:
- cluster:
server: ""
name: kubernetes
contexts: []
current-context: ""
kind: Config
preferences: {}
users: []
`
)
type configClient struct {
c string
s string
ca []byte
}
type configClientWithCerts struct {
c *clientcmdapi.Config
clusterName string
userName string
clientKey []byte
clientCert []byte
}
type configClientWithToken struct {
c *clientcmdapi.Config
clusterName string
userName string
token string
}
func TestCreateBasicClientConfig(t *testing.T) {
var createBasicTest = []struct {
cc configClient
expected string
}{
{configClient{}, ""},
{configClient{c: "kubernetes"}, ""},
}
for _, rt := range createBasicTest {
c := CreateBasicClientConfig(rt.cc.c, rt.cc.s, rt.cc.ca)
if c.Kind != rt.expected {
t.Errorf(
"failed CreateBasicClientConfig:\n\texpected: %s\n\t actual: %s",
c.Kind,
rt.expected,
)
}
}
}
func TestMakeClientConfigWithCerts(t *testing.T) {
var createBasicTest = []struct {
cc configClient
ccWithCerts configClientWithCerts
expected string
}{
{configClient{}, configClientWithCerts{}, ""},
{configClient{c: "kubernetes"}, configClientWithCerts{}, ""},
}
for _, rt := range createBasicTest {
c := CreateBasicClientConfig(rt.cc.c, rt.cc.s, rt.cc.ca)
rt.ccWithCerts.c = c
cwc := MakeClientConfigWithCerts(
rt.ccWithCerts.c,
rt.ccWithCerts.clusterName,
rt.ccWithCerts.userName,
rt.ccWithCerts.clientKey,
rt.ccWithCerts.clientCert,
)
if cwc.Kind != rt.expected {
t.Errorf(
"failed MakeClientConfigWithCerts:\n\texpected: %s\n\t actual: %s",
c.Kind,
rt.expected,
)
}
}
}
func TestMakeClientConfigWithToken(t *testing.T) {
var createBasicTest = []struct {
cc configClient
ccWithToken configClientWithToken
expected string
}{
{configClient{}, configClientWithToken{}, ""},
{configClient{c: "kubernetes"}, configClientWithToken{}, ""},
}
for _, rt := range createBasicTest {
c := CreateBasicClientConfig(rt.cc.c, rt.cc.s, rt.cc.ca)
rt.ccWithToken.c = c
cwc := MakeClientConfigWithToken(
rt.ccWithToken.c,
rt.ccWithToken.clusterName,
rt.ccWithToken.userName,
rt.ccWithToken.token,
)
if cwc.Kind != rt.expected {
t.Errorf(
"failed MakeClientConfigWithCerts:\n\texpected: %s\n\t actual: %s",
c.Kind,
rt.expected,
)
}
}
}
func TestWriteKubeconfigIfNotExists(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)
kubeadmapi.GlobalEnvParams.HostPKIPath = fmt.Sprintf("%s/etc/kubernetes/pki", tmpdir)
kubeadmapi.GlobalEnvParams.HostEtcdPath = fmt.Sprintf("%s/var/lib/etcd", tmpdir)
kubeadmapi.GlobalEnvParams.DiscoveryImage = fmt.Sprintf("%s/var/lib/etcd", tmpdir)
defer func() { kubeadmapi.GlobalEnvParams = oldEnv }()
var writeConfig = []struct {
name string
cc configClient
expected error
file []byte
}{
{"test1", configClient{}, nil, []byte(configOut1)},
{"test2", configClient{c: "kubernetes"}, nil, []byte(configOut2)},
}
for _, rt := range writeConfig {
c := CreateBasicClientConfig(rt.cc.c, rt.cc.s, rt.cc.ca)
err := WriteKubeconfigIfNotExists(rt.name, c)
if err != rt.expected {
t.Errorf(
"failed WriteKubeconfigIfNotExists with an error:\n\texpected: %s\n\t actual: %s",
err,
rt.expected,
)
}
configPath := filepath.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, fmt.Sprintf("%s.conf", rt.name))
newFile, err := ioutil.ReadFile(configPath)
if !bytes.Equal(newFile, rt.file) {
t.Errorf(
"failed WriteKubeconfigIfNotExists config write:\n\texpected: %s\n\t actual: %s",
newFile,
rt.file,
)
}
}
}

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 util
import (
"crypto/rand"
"encoding/hex"
"fmt"
"strings"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
const (
TokenIDLen = 6
TokenBytes = 8
)
func RandBytes(length int) ([]byte, string, error) {
b := make([]byte, length)
_, err := rand.Read(b)
if err != nil {
return nil, "", err
}
// It's only the tokenID that doesn't care about raw byte slice,
// so we just encoded it in place and ignore bytes slice where we
// do not want it
return b, hex.EncodeToString(b), nil
}
func GenerateToken(s *kubeadmapi.Secrets) error {
_, tokenID, err := RandBytes(TokenIDLen / 2)
if err != nil {
return err
}
tokenBytes, token, err := RandBytes(TokenBytes)
if err != nil {
return err
}
s.TokenID = tokenID
s.BearerToken = token
s.Token = tokenBytes
s.GivenToken = fmt.Sprintf("%s.%s", tokenID, token)
return nil
}
func UseGivenTokenIfValid(s *kubeadmapi.Secrets) (bool, error) {
if s.GivenToken == "" {
return false, nil // not given
}
fmt.Println("<util/tokens> validating provided token")
givenToken := strings.Split(strings.ToLower(s.GivenToken), ".")
// TODO(phase1+) could also print more specific messages in each case
invalidErr := "<util/tokens> provided token does not match expected <6 characters>.<16 characters> format - %s"
if len(givenToken) != 2 {
return false, fmt.Errorf(invalidErr, "not in 2-part dot-separated format")
}
if len(givenToken[0]) != TokenIDLen {
return false, fmt.Errorf(invalidErr, fmt.Sprintf(
"length of first part is incorrect [%d (given) != %d (expected) ]",
len(givenToken[0]), TokenIDLen))
}
tokenBytes := []byte(givenToken[1])
s.TokenID = givenToken[0]
s.BearerToken = givenToken[1]
s.Token = tokenBytes
return true, nil // given and valid
}

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 util
import (
"bytes"
"strings"
"testing"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func TestUsingEmptyTokenFails(t *testing.T) {
// Simulates what happens when you omit --token on the CLI
s := newSecretsWithToken("")
given, err := UseGivenTokenIfValid(s)
if err != nil {
t.Errorf("UseGivenTokenIfValid returned an error when the token was omitted: %v", err)
}
if given {
t.Error("UseGivenTokenIfValid returned given = true when the token was omitted; expected false")
}
}
func TestTokenValidationFailures(t *testing.T) {
var tests = []struct {
t string
expected bool
}{
{
t: "1234567890123456789012",
expected: false,
},
{
t: "12345.1234567890123456",
expected: false,
},
{
t: ".1234567890123456",
expected: false,
},
{
t: "123456.1234567890.123456",
expected: false,
},
}
for _, rt := range tests {
s := newSecretsWithToken(rt.t)
_, err := UseGivenTokenIfValid(s)
if (err == nil) != rt.expected {
t.Errorf(
"failed UseGivenTokenIfValid and did not return an error for this invalid token: [%s]",
rt.t,
)
}
}
}
func TestValidTokenPopulatesSecrets(t *testing.T) {
var tests = []struct {
token string
expectedToken []byte
expectedTokenID string
expectedBearerToken string
}{
{
token: "123456.0123456789AbCdEf",
expectedToken: []byte("0123456789abcdef"),
expectedTokenID: "123456",
expectedBearerToken: "0123456789abcdef",
},
}
for _, rt := range tests {
s := newSecretsWithToken(rt.token)
given, err := UseGivenTokenIfValid(s)
if err != nil {
t.Errorf("UseGivenTokenIfValid gave an error for a valid token: %v", err)
}
if !given {
t.Error("UseGivenTokenIfValid returned given = false when given a valid token")
}
if s.TokenID != rt.expectedTokenID {
t.Errorf("UseGivenTokenIfValid did not populate the TokenID correctly; expected [%s] but got [%s]", rt.expectedTokenID, s.TokenID)
}
if s.BearerToken != rt.expectedBearerToken {
t.Errorf("UseGivenTokenIfValid did not populate the BearerToken correctly; expected [%s] but got [%s]", rt.expectedBearerToken, s.BearerToken)
}
if !bytes.Equal(s.Token, rt.expectedToken) {
t.Errorf("UseGivenTokenIfValid did not populate the Token correctly; expected %v but got %v", rt.expectedToken, s.Token)
}
}
}
func newSecretsWithToken(token string) *kubeadmapi.Secrets {
s := new(kubeadmapi.Secrets)
s.GivenToken = token
return s
}
func TestGenerateToken(t *testing.T) {
var genTest = []struct {
s kubeadmapi.Secrets
l int
n int
}{
{kubeadmapi.Secrets{}, 2, 6},
}
for _, rt := range genTest {
GenerateToken(&rt.s)
givenToken := strings.Split(strings.ToLower(rt.s.GivenToken), ".")
if len(givenToken) != rt.l {
t.Errorf(
"failed GenerateToken num parts:\n\texpected: %d\n\t actual: %d",
rt.l,
len(givenToken),
)
}
if len(givenToken[0]) != rt.n {
t.Errorf(
"failed GenerateToken first part length:\n\texpected: %d\n\t actual: %d",
rt.l,
len(givenToken),
)
}
}
}
func TestUseGivenTokenIfValid(t *testing.T) {
var tokenTest = []struct {
s kubeadmapi.Secrets
expected bool
}{
{kubeadmapi.Secrets{GivenToken: ""}, false}, // GivenToken == ""
{kubeadmapi.Secrets{GivenToken: "noperiod"}, false}, // not 2-part '.' format
{kubeadmapi.Secrets{GivenToken: "abcd.a"}, false}, // len(tokenID) != 6
{kubeadmapi.Secrets{GivenToken: "abcdef.a"}, true},
}
for _, rt := range tokenTest {
actual, _ := UseGivenTokenIfValid(&rt.s)
if actual != rt.expected {
t.Errorf(
"failed UseGivenTokenIfValid:\n\texpected: %t\n\t actual: %t\n\t token:%s",
rt.expected,
actual,
rt.s.GivenToken,
)
}
}
}
func TestRandBytes(t *testing.T) {
var randTest = []struct {
r int
l int
expected error
}{
{0, 0, nil},
{1, 1, nil},
{2, 2, nil},
{3, 3, nil},
{100, 100, nil},
}
for _, rt := range randTest {
actual, _, err := RandBytes(rt.r)
if err != rt.expected {
t.Errorf(
"failed RandBytes:\n\texpected: %s\n\t actual: %s",
rt.expected,
err,
)
}
if len(actual) != rt.l {
t.Errorf(
"failed RandBytes:\n\texpected: %d\n\t actual: %d\n",
rt.l,
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("Error: unable to get URL %q: %s", url, err.Error())
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("Error: unable to fetch release information. URL: %q Status: %v", url, resp.Status)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("Error: 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("Error: 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 succesfully, 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)
}
}
}