From 4db581c8eeda4ac357a885b2f66c43b69c59eca5 Mon Sep 17 00:00:00 2001 From: fabriziopandini Date: Mon, 14 Aug 2017 16:30:31 +0200 Subject: [PATCH 1/4] Move all staticpod utils to separate package --- cmd/kubeadm/app/util/staticpod/utils.go | 137 ++++++++++++ cmd/kubeadm/app/util/staticpod/utils_test.go | 219 +++++++++++++++++++ 2 files changed, 356 insertions(+) create mode 100644 cmd/kubeadm/app/util/staticpod/utils.go create mode 100644 cmd/kubeadm/app/util/staticpod/utils_test.go diff --git a/cmd/kubeadm/app/util/staticpod/utils.go b/cmd/kubeadm/app/util/staticpod/utils.go new file mode 100644 index 0000000000..bdeb09f657 --- /dev/null +++ b/cmd/kubeadm/app/util/staticpod/utils.go @@ -0,0 +1,137 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package staticpod + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/ghodss/yaml" + + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" + kubetypes "k8s.io/kubernetes/pkg/kubelet/types" +) + +// ComponentPod returns a Pod object from the container and volume specifications +func ComponentPod(container v1.Container, volumes []v1.Volume) v1.Pod { + return v1.Pod{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Pod", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: container.Name, + Namespace: metav1.NamespaceSystem, + Annotations: map[string]string{kubetypes.CriticalPodAnnotationKey: ""}, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{container}, + HostNetwork: true, + Volumes: volumes, + }, + } +} + +// ComponentResources returns the v1.ResourceRequirements object needed for allocating a specified amount of the CPU +func ComponentResources(cpu string) v1.ResourceRequirements { + return v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceCPU): resource.MustParse(cpu), + }, + } +} + +// ComponentProbe is a helper function building a ready v1.Probe object from some simple parameters +func ComponentProbe(port int, path string, scheme v1.URIScheme) *v1.Probe { + return &v1.Probe{ + Handler: v1.Handler{ + HTTPGet: &v1.HTTPGetAction{ + // Host has to be set to "127.0.0.1" here due to that our static Pods are on the host's network + Host: "127.0.0.1", + Path: path, + Port: intstr.FromInt(port), + Scheme: scheme, + }, + }, + InitialDelaySeconds: 15, + TimeoutSeconds: 15, + FailureThreshold: 8, + } +} + +// NewVolume creates a v1.Volume with a hostPath mount to the specified location +func NewVolume(name, path string) v1.Volume { + return v1.Volume{ + Name: name, + VolumeSource: v1.VolumeSource{ + HostPath: &v1.HostPathVolumeSource{Path: path}, + }, + } +} + +// NewVolumeMount creates a v1.VolumeMount to the specified location +func NewVolumeMount(name, path string, readOnly bool) v1.VolumeMount { + return v1.VolumeMount{ + Name: name, + MountPath: path, + ReadOnly: readOnly, + } +} + +// GetExtraParameters builds a list of flag arguments two string-string maps, one with default, base commands and one with overrides +func GetExtraParameters(overrides map[string]string, defaults map[string]string) []string { + var command []string + for k, v := range overrides { + if len(v) > 0 { + command = append(command, fmt.Sprintf("--%s=%s", k, v)) + } + } + for k, v := range defaults { + if _, overrideExists := overrides[k]; !overrideExists { + command = append(command, fmt.Sprintf("--%s=%s", k, v)) + } + } + return command +} + +// WriteStaticPodToDisk writes a static pod file to disk +func WriteStaticPodToDisk(componentName, manifestDir string, pod v1.Pod) error { + + // creates target folder if not already exists + if err := os.MkdirAll(manifestDir, 0700); err != nil { + return fmt.Errorf("failed to create directory %q: %v", manifestDir, err) + } + + // writes the pod to disk + serialized, err := yaml.Marshal(pod) + if err != nil { + return fmt.Errorf("failed to marshal manifest for %q to YAML: %v", componentName, err) + } + + filename := kubeadmconstants.GetStaticPodFilepath(componentName, manifestDir) + + if err := ioutil.WriteFile(filename, serialized, 0700); err != nil { + return fmt.Errorf("failed to write static pod manifest file for %q (%q): %v", componentName, filename, err) + } + + return nil +} diff --git a/cmd/kubeadm/app/util/staticpod/utils_test.go b/cmd/kubeadm/app/util/staticpod/utils_test.go new file mode 100644 index 0000000000..e0e844d8a9 --- /dev/null +++ b/cmd/kubeadm/app/util/staticpod/utils_test.go @@ -0,0 +1,219 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package staticpod + +import ( + "reflect" + "sort" + "testing" + + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +func TestComponentResources(t *testing.T) { + a := ComponentResources("250m") + if a.Requests == nil { + t.Errorf( + "failed componentResources, return value was nil", + ) + } +} + +func TestComponentProbe(t *testing.T) { + var tests = []struct { + port int + path string + scheme v1.URIScheme + }{ + { + port: 1, + path: "foo", + scheme: v1.URISchemeHTTP, + }, + { + port: 2, + path: "bar", + scheme: v1.URISchemeHTTPS, + }, + } + for _, rt := range tests { + actual := ComponentProbe(rt.port, rt.path, rt.scheme) + if actual.Handler.HTTPGet.Port != intstr.FromInt(rt.port) { + t.Errorf( + "failed componentProbe:\n\texpected: %v\n\t actual: %v", + rt.port, + actual.Handler.HTTPGet.Port, + ) + } + if actual.Handler.HTTPGet.Path != rt.path { + t.Errorf( + "failed componentProbe:\n\texpected: %s\n\t actual: %s", + rt.path, + actual.Handler.HTTPGet.Path, + ) + } + if actual.Handler.HTTPGet.Scheme != rt.scheme { + t.Errorf( + "failed componentProbe:\n\texpected: %v\n\t actual: %v", + rt.scheme, + actual.Handler.HTTPGet.Scheme, + ) + } + } +} + +func TestComponentPod(t *testing.T) { + var tests = []struct { + n string + }{ + { + n: "foo", + }, + } + + for _, rt := range tests { + c := v1.Container{Name: rt.n} + v := []v1.Volume{} + actual := ComponentPod(c, v) + if actual.ObjectMeta.Name != rt.n { + t.Errorf( + "failed componentPod:\n\texpected: %s\n\t actual: %s", + rt.n, + actual.ObjectMeta.Name, + ) + } + } +} + +func TestNewVolume(t *testing.T) { + var tests = []struct { + name string + path string + expected v1.Volume + }{ + { + name: "foo", + path: "/etc/foo", + expected: v1.Volume{ + Name: "foo", + VolumeSource: v1.VolumeSource{ + HostPath: &v1.HostPathVolumeSource{Path: "/etc/foo"}, + }, + }, + }, + } + + for _, rt := range tests { + actual := NewVolume(rt.name, rt.path) + if !reflect.DeepEqual(actual, rt.expected) { + t.Errorf( + "failed newVolume:\n\texpected: %v\n\t actual: %v", + rt.expected, + actual, + ) + } + } +} + +func TestNewVolumeMount(t *testing.T) { + var tests = []struct { + name string + path string + ro bool + expected v1.VolumeMount + }{ + { + name: "foo", + path: "/etc/foo", + ro: false, + expected: v1.VolumeMount{ + Name: "foo", + MountPath: "/etc/foo", + ReadOnly: false, + }, + }, + { + name: "bar", + path: "/etc/foo/bar", + ro: true, + expected: v1.VolumeMount{ + Name: "bar", + MountPath: "/etc/foo/bar", + ReadOnly: true, + }, + }, + } + + for _, rt := range tests { + actual := NewVolumeMount(rt.name, rt.path, rt.ro) + if !reflect.DeepEqual(actual, rt.expected) { + t.Errorf( + "failed newVolumeMount:\n\texpected: %v\n\t actual: %v", + rt.expected, + actual, + ) + } + } +} + +func TestGetExtraParameters(t *testing.T) { + var tests = []struct { + overrides map[string]string + defaults map[string]string + expected []string + }{ + { + overrides: map[string]string{ + "admission-control": "NamespaceLifecycle,LimitRanger", + }, + defaults: map[string]string{ + "admission-control": "NamespaceLifecycle", + "insecure-bind-address": "127.0.0.1", + "allow-privileged": "true", + }, + expected: []string{ + "--admission-control=NamespaceLifecycle,LimitRanger", + "--insecure-bind-address=127.0.0.1", + "--allow-privileged=true", + }, + }, + { + overrides: map[string]string{ + "admission-control": "NamespaceLifecycle,LimitRanger", + }, + defaults: map[string]string{ + "insecure-bind-address": "127.0.0.1", + "allow-privileged": "true", + }, + expected: []string{ + "--admission-control=NamespaceLifecycle,LimitRanger", + "--insecure-bind-address=127.0.0.1", + "--allow-privileged=true", + }, + }, + } + + for _, rt := range tests { + actual := GetExtraParameters(rt.overrides, rt.defaults) + sort.Strings(actual) + sort.Strings(rt.expected) + if !reflect.DeepEqual(actual, rt.expected) { + t.Errorf("failed getExtraParameters:\nexpected:\n%v\nsaw:\n%v", rt.expected, actual) + } + } +} From 740a78b0f3e5952c1cb2b15ba86f42c1c3c3a8fe Mon Sep 17 00:00:00 2001 From: fabriziopandini Date: Mon, 14 Aug 2017 16:31:09 +0200 Subject: [PATCH 2/4] Main work -- move etcd to separate phase and hook up most things --- .../app/phases/controlplane/manifests.go | 181 +++------- .../app/phases/controlplane/manifests_test.go | 333 ++++-------------- .../app/phases/controlplane/volumes.go | 29 +- .../app/phases/controlplane/volumes_test.go | 71 ---- cmd/kubeadm/app/phases/etcd/local.go | 65 ++++ cmd/kubeadm/app/phases/etcd/local_test.go | 125 +++++++ 6 files changed, 321 insertions(+), 483 deletions(-) create mode 100644 cmd/kubeadm/app/phases/etcd/local.go create mode 100644 cmd/kubeadm/app/phases/etcd/local_test.go diff --git a/cmd/kubeadm/app/phases/controlplane/manifests.go b/cmd/kubeadm/app/phases/controlplane/manifests.go index 5b019a9393..ed073cedd5 100644 --- a/cmd/kubeadm/app/phases/controlplane/manifests.go +++ b/cmd/kubeadm/app/phases/controlplane/manifests.go @@ -17,26 +17,19 @@ limitations under the License. package controlplane import ( - "bytes" "fmt" "os" "path/filepath" "strings" - "github.com/ghodss/yaml" - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/sets" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/images" + staticpodutil "k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod" authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - kubetypes "k8s.io/kubernetes/pkg/kubelet/types" "k8s.io/kubernetes/pkg/util/version" ) @@ -47,125 +40,96 @@ const ( defaultv17AdmissionControl = "Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,ResourceQuota" ) -// 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, k8sVersion *version.Version, manifestsDir string) error { +// CreateInitStaticPodManifestFiles will write all static pod manifest files needed to bring up the control plane. +func CreateInitStaticPodManifestFiles(manifestDir string, cfg *kubeadmapi.MasterConfiguration) error { + return createStaticPodFiles(manifestDir, cfg, kubeadmconstants.KubeAPIServer, kubeadmconstants.KubeControllerManager, kubeadmconstants.KubeScheduler) +} + +// CreateAPIServerStaticPodManifestFile will write APIserver static pod manifest file. +func CreateAPIServerStaticPodManifestFile(manifestDir string, cfg *kubeadmapi.MasterConfiguration) error { + return createStaticPodFiles(manifestDir, cfg, kubeadmconstants.KubeAPIServer) +} + +// CreateControllerManagerStaticPodManifestFile will write controller manager static pod manifest file. +func CreateControllerManagerStaticPodManifestFile(manifestDir string, cfg *kubeadmapi.MasterConfiguration) error { + return createStaticPodFiles(manifestDir, cfg, kubeadmconstants.KubeControllerManager) +} + +// CreateSchedulerStaticPodManifestFile will write scheduler static pod manifest file. +func CreateSchedulerStaticPodManifestFile(manifestDir string, cfg *kubeadmapi.MasterConfiguration) error { + return createStaticPodFiles(manifestDir, cfg, kubeadmconstants.KubeScheduler) +} + +// GetStaticPodSpecs returns all staticPodSpecs actualized to the context of the current MasterConfiguration +// NB. this methods holds the information about how kubeadm creates static pod mainfests. +func GetStaticPodSpecs(cfg *kubeadmapi.MasterConfiguration, k8sVersion *version.Version) map[string]v1.Pod { // Get the required hostpath mounts mounts := getHostPathVolumesForTheControlPlane(cfg) // Prepare static pod specs staticPodSpecs := map[string]v1.Pod{ - kubeadmconstants.KubeAPIServer: componentPod(v1.Container{ + kubeadmconstants.KubeAPIServer: staticpodutil.ComponentPod(v1.Container{ Name: kubeadmconstants.KubeAPIServer, Image: images.GetCoreImage(kubeadmconstants.KubeAPIServer, cfg.ImageRepository, cfg.KubernetesVersion, cfg.UnifiedControlPlaneImage), Command: getAPIServerCommand(cfg, k8sVersion), VolumeMounts: mounts.GetVolumeMounts(kubeadmconstants.KubeAPIServer), - LivenessProbe: componentProbe(int(cfg.API.BindPort), "/healthz", v1.URISchemeHTTPS), - Resources: componentResources("250m"), + LivenessProbe: staticpodutil.ComponentProbe(int(cfg.API.BindPort), "/healthz", v1.URISchemeHTTPS), + Resources: staticpodutil.ComponentResources("250m"), Env: getProxyEnvVars(), }, mounts.GetVolumes(kubeadmconstants.KubeAPIServer)), - kubeadmconstants.KubeControllerManager: componentPod(v1.Container{ + kubeadmconstants.KubeControllerManager: staticpodutil.ComponentPod(v1.Container{ Name: kubeadmconstants.KubeControllerManager, Image: images.GetCoreImage(kubeadmconstants.KubeControllerManager, cfg.ImageRepository, cfg.KubernetesVersion, cfg.UnifiedControlPlaneImage), Command: getControllerManagerCommand(cfg, k8sVersion), VolumeMounts: mounts.GetVolumeMounts(kubeadmconstants.KubeControllerManager), - LivenessProbe: componentProbe(10252, "/healthz", v1.URISchemeHTTP), - Resources: componentResources("200m"), + LivenessProbe: staticpodutil.ComponentProbe(10252, "/healthz", v1.URISchemeHTTP), + Resources: staticpodutil.ComponentResources("200m"), Env: getProxyEnvVars(), }, mounts.GetVolumes(kubeadmconstants.KubeControllerManager)), - kubeadmconstants.KubeScheduler: componentPod(v1.Container{ + kubeadmconstants.KubeScheduler: staticpodutil.ComponentPod(v1.Container{ Name: kubeadmconstants.KubeScheduler, Image: images.GetCoreImage(kubeadmconstants.KubeScheduler, cfg.ImageRepository, cfg.KubernetesVersion, cfg.UnifiedControlPlaneImage), Command: getSchedulerCommand(cfg), VolumeMounts: mounts.GetVolumeMounts(kubeadmconstants.KubeScheduler), - LivenessProbe: componentProbe(10251, "/healthz", v1.URISchemeHTTP), - Resources: componentResources("100m"), + LivenessProbe: staticpodutil.ComponentProbe(10251, "/healthz", v1.URISchemeHTTP), + Resources: staticpodutil.ComponentResources("100m"), Env: getProxyEnvVars(), }, mounts.GetVolumes(kubeadmconstants.KubeScheduler)), } - // Add etcd static pod spec only if external etcd is not configured - if len(cfg.Etcd.Endpoints) == 0 { + return staticPodSpecs +} - etcdPod := componentPod(v1.Container{ - Name: kubeadmconstants.Etcd, - Command: getEtcdCommand(cfg), - Image: images.GetCoreImage(kubeadmconstants.Etcd, cfg.ImageRepository, "", cfg.Etcd.Image), - // Mount the etcd datadir path read-write so etcd can store data in a more persistent manner - VolumeMounts: []v1.VolumeMount{newVolumeMount(etcdVolumeName, cfg.Etcd.DataDir, false)}, - LivenessProbe: componentProbe(2379, "/health", v1.URISchemeHTTP), - }, []v1.Volume{newVolume(etcdVolumeName, cfg.Etcd.DataDir)}) +// createStaticPodFiles creates all the requested static pod files. +func createStaticPodFiles(manifestDir string, cfg *kubeadmapi.MasterConfiguration, componentNames ...string) error { - staticPodSpecs[kubeadmconstants.Etcd] = etcdPod + // TODO: Move the "pkg/util/version".Version object into the internal API instead of always parsing the string + k8sVersion, err := version.ParseSemantic(cfg.KubernetesVersion) + if err != nil { + return err } - if err := os.MkdirAll(manifestsDir, 0700); err != nil { - return fmt.Errorf("failed to create directory %q [%v]", manifestsDir, err) - } - for name, spec := range staticPodSpecs { - filename := kubeadmconstants.GetStaticPodFilepath(name, manifestsDir) - serialized, err := yaml.Marshal(spec) - if err != nil { - return fmt.Errorf("failed to marshal manifest for %q to YAML [%v]", name, err) + // gets the StaticPodSpecs, actualized for the current MasterConfiguration + specs := GetStaticPodSpecs(cfg, k8sVersion) + + // creates required static pod specs + for _, componentName := range componentNames { + // retrives the StaticPodSpec for given component + spec, exists := specs[componentName] + if !exists { + return fmt.Errorf("couldn't retrive StaticPodSpec for %s", componentName) } - if err := cmdutil.DumpReaderToFile(bytes.NewReader(serialized), filename); err != nil { - return fmt.Errorf("failed to create static pod manifest file for %q (%q) [%v]", name, filename, err) + + // writes the StaticPodSpec to disk + if err := staticpodutil.WriteStaticPodToDisk(componentName, manifestDir, spec); err != nil { + return fmt.Errorf("failed to create static pod manifest file for %q: %v", componentName, err) } } + return nil } -// componentResources returns the v1.ResourceRequirements object needed for allocating a specified amount of the CPU -func componentResources(cpu string) v1.ResourceRequirements { - return v1.ResourceRequirements{ - Requests: v1.ResourceList{ - v1.ResourceName(v1.ResourceCPU): resource.MustParse(cpu), - }, - } -} - -// componentProbe is a helper function building a ready v1.Probe object from some simple parameters -func componentProbe(port int, path string, scheme v1.URIScheme) *v1.Probe { - return &v1.Probe{ - Handler: v1.Handler{ - HTTPGet: &v1.HTTPGetAction{ - // Host has to be set to "127.0.0.1" here due to that our static Pods are on the host's network - Host: "127.0.0.1", - Path: path, - Port: intstr.FromInt(port), - Scheme: scheme, - }, - }, - InitialDelaySeconds: 15, - TimeoutSeconds: 15, - FailureThreshold: 8, - } -} - -// componentPod returns a Pod object from the container and volume specifications -func componentPod(container v1.Container, volumes []v1.Volume) v1.Pod { - return v1.Pod{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "Pod", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: container.Name, - Namespace: metav1.NamespaceSystem, - Annotations: map[string]string{kubetypes.CriticalPodAnnotationKey: ""}, - // The component and tier labels are useful for quickly identifying the control plane Pods when doing a .List() - // against Pods in the kube-system namespace. Can for example be used together with the WaitForPodsWithLabel function - Labels: map[string]string{"component": container.Name, "tier": "control-plane"}, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{container}, - HostNetwork: true, - Volumes: volumes, - }, - } -} - // getAPIServerCommand builds the right API server command from the given config object and version func getAPIServerCommand(cfg *kubeadmapi.MasterConfiguration, k8sVersion *version.Version) []string { defaultArguments := map[string]string{ @@ -195,7 +159,7 @@ func getAPIServerCommand(cfg *kubeadmapi.MasterConfiguration, k8sVersion *versio } command := []string{"kube-apiserver"} - command = append(command, getExtraParameters(cfg.APIServerExtraArgs, defaultArguments)...) + command = append(command, staticpodutil.GetExtraParameters(cfg.APIServerExtraArgs, defaultArguments)...) command = append(command, getAuthzParameters(cfg.AuthorizationModes)...) // Check if the user decided to use an external etcd cluster @@ -227,19 +191,6 @@ func getAPIServerCommand(cfg *kubeadmapi.MasterConfiguration, k8sVersion *versio return command } -// getEtcdCommand builds the right etcd command from the given config object -func getEtcdCommand(cfg *kubeadmapi.MasterConfiguration) []string { - defaultArguments := map[string]string{ - "listen-client-urls": "http://127.0.0.1:2379", - "advertise-client-urls": "http://127.0.0.1:2379", - "data-dir": cfg.Etcd.DataDir, - } - - command := []string{"etcd"} - command = append(command, getExtraParameters(cfg.Etcd.ExtraArgs, defaultArguments)...) - return command -} - // getControllerManagerCommand builds the right controller manager command from the given config object and version func getControllerManagerCommand(cfg *kubeadmapi.MasterConfiguration, k8sVersion *version.Version) []string { defaultArguments := map[string]string{ @@ -255,7 +206,7 @@ func getControllerManagerCommand(cfg *kubeadmapi.MasterConfiguration, k8sVersion } command := []string{"kube-controller-manager"} - command = append(command, getExtraParameters(cfg.ControllerManagerExtraArgs, defaultArguments)...) + command = append(command, staticpodutil.GetExtraParameters(cfg.ControllerManagerExtraArgs, defaultArguments)...) if cfg.CloudProvider != "" { command = append(command, "--cloud-provider="+cfg.CloudProvider) @@ -283,7 +234,7 @@ func getSchedulerCommand(cfg *kubeadmapi.MasterConfiguration) []string { } command := []string{"kube-scheduler"} - command = append(command, getExtraParameters(cfg.SchedulerExtraArgs, defaultArguments)...) + command = append(command, staticpodutil.GetExtraParameters(cfg.SchedulerExtraArgs, defaultArguments)...) return command } @@ -327,19 +278,3 @@ func getAuthzParameters(modes []string) []string { command = append(command, "--authorization-mode="+strings.Join(modes, ",")) return command } - -// getExtraParameters builds a list of flag arguments two string-string maps, one with default, base commands and one with overrides -func getExtraParameters(overrides map[string]string, defaults map[string]string) []string { - var command []string - for k, v := range overrides { - if len(v) > 0 { - command = append(command, fmt.Sprintf("--%s=%s", k, v)) - } - } - for k, v := range defaults { - if _, overrideExists := overrides[k]; !overrideExists { - command = append(command, fmt.Sprintf("--%s=%s", k, v)) - } - } - return command -} diff --git a/cmd/kubeadm/app/phases/controlplane/manifests_test.go b/cmd/kubeadm/app/phases/controlplane/manifests_test.go index b84bf5ac02..23dcc03bf8 100644 --- a/cmd/kubeadm/app/phases/controlplane/manifests_test.go +++ b/cmd/kubeadm/app/phases/controlplane/manifests_test.go @@ -18,20 +18,17 @@ package controlplane import ( "fmt" - "io/ioutil" "os" "path/filepath" "reflect" "sort" "testing" - "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/util/yaml" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/pkg/util/version" + + testutil "k8s.io/kubernetes/cmd/kubeadm/test" ) const ( @@ -39,189 +36,98 @@ const ( etcdDataDir = "/var/lib/etcd" ) -func TestWriteStaticPodManifests(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "") - if err != nil { - t.Fatalf("Couldn't create tmpdir") +func TestGetStaticPodSpecs(t *testing.T) { + + // Creates a Master Configuration + cfg := &kubeadmapi.MasterConfiguration{ + KubernetesVersion: "v1.7.0", } - defer os.RemoveAll(tmpdir) - // set up tmp KubernetesDir for testing - kubeadmconstants.KubernetesDir = fmt.Sprintf("%s/etc/kubernetes", tmpdir) - defer func() { kubeadmconstants.KubernetesDir = "/etc/kubernetes" }() + // Executes GetStaticPodSpecs - var tests = []struct { - cfg *kubeadmapi.MasterConfiguration - expectErr bool - expectedAPIProbePort int32 + // TODO: Move the "pkg/util/version".Version object into the internal API instead of always parsing the string + k8sVersion, _ := version.ParseSemantic(cfg.KubernetesVersion) + + specs := GetStaticPodSpecs(cfg, k8sVersion) + + var assertions = []struct { + staticPodName string }{ { - cfg: &kubeadmapi.MasterConfiguration{ - KubernetesVersion: "v1.7.0", - }, - expectErr: false, + staticPodName: kubeadmconstants.KubeAPIServer, }, { - cfg: &kubeadmapi.MasterConfiguration{ - API: kubeadmapi.API{ - BindPort: 443, - }, - KubernetesVersion: "v1.7.0", - }, - expectErr: false, - expectedAPIProbePort: 443, + staticPodName: kubeadmconstants.KubeControllerManager, + }, + { + staticPodName: kubeadmconstants.KubeScheduler, }, } - for _, rt := range tests { - actual := WriteStaticPodManifests(rt.cfg, version.MustParseSemantic(rt.cfg.KubernetesVersion), fmt.Sprintf("%s/etc/kubernetes/manifests", tmpdir)) - if (actual == nil) && rt.expectErr { - t.Error("expected an error from WriteStaticPodManifests but got none") - continue - } - if (actual != nil) && !rt.expectErr { - t.Errorf("didn't expect an error from WriteStaticPodManifests but got: %v", err) - continue - } - if rt.expectErr { - continue - } + for _, assertion := range assertions { - // Below is dead code. - if rt.expectedAPIProbePort != 0 { - manifest, err := os.Open(filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.ManifestsSubDirName, "kube-apiserver.yaml")) - if err != nil { - t.Errorf("WriteStaticPodManifests: %v", err) - continue - } - defer manifest.Close() + // assert the spec for the staticPodName exists + if spec, ok := specs[assertion.staticPodName]; ok { - var pod v1.Pod - d := yaml.NewYAMLOrJSONDecoder(manifest, 4096) - if err := d.Decode(&pod); err != nil { - t.Error("WriteStaticPodManifests: error decoding manifests/kube-apiserver.yaml into Pod") - continue + // Assert each specs refers to the right pod + if spec.Spec.Containers[0].Name != assertion.staticPodName { + t.Errorf("getKubeConfigSpecs spec for %s contains pod %s, expectes %s", assertion.staticPodName, spec.Spec.Containers[0].Name, assertion.staticPodName) } - // Lots of individual checks as we traverse pointers so we don't panic dereferencing a nil on failure - containers := pod.Spec.Containers - if containers == nil || len(containers) == 0 { - t.Error("WriteStaticPodManifests: wrote an apiserver manifest without any containers") - continue - } - - probe := containers[0].LivenessProbe - if probe == nil { - t.Error("WriteStaticPodManifests: wrote an apiserver manifest without a liveness probe") - continue - } - - httpGET := probe.Handler.HTTPGet - if httpGET == nil { - t.Error("WriteStaticPodManifests: wrote an apiserver manifest without an HTTP liveness probe") - continue - } - - port := httpGET.Port.IntVal - if rt.expectedAPIProbePort != port { - t.Errorf("WriteStaticPodManifests: apiserver pod liveness probe port was: %v, wanted %v", port, rt.expectedAPIProbePort) - } + } else { + t.Errorf("getStaticPodSpecs didn't create spec for %s ", assertion.staticPodName) } } } -func TestComponentResources(t *testing.T) { - a := componentResources("250m") - if a.Requests == nil { - t.Errorf( - "failed componentResources, return value was nil", - ) - } -} +func TestCreateStaticPodFilesAndWrappers(t *testing.T) { -func TestComponentProbe(t *testing.T) { var tests = []struct { - port int - path string - scheme v1.URIScheme + createStaticPodFunction func(outDir string, cfg *kubeadmapi.MasterConfiguration) error + expectedFiles []string }{ - { - port: 1, - path: "foo", - scheme: v1.URISchemeHTTP, + { // CreateInitStaticPodManifestFiles + createStaticPodFunction: CreateInitStaticPodManifestFiles, + expectedFiles: []string{kubeadmconstants.KubeAPIServer, kubeadmconstants.KubeControllerManager, kubeadmconstants.KubeScheduler}, }, - { - port: 2, - path: "bar", - scheme: v1.URISchemeHTTPS, + { // CreateAPIServerStaticPodManifestFile + createStaticPodFunction: CreateAPIServerStaticPodManifestFile, + expectedFiles: []string{kubeadmconstants.KubeAPIServer}, }, - } - for _, rt := range tests { - actual := componentProbe(rt.port, rt.path, rt.scheme) - if actual.Handler.HTTPGet.Port != intstr.FromInt(rt.port) { - t.Errorf( - "failed componentProbe:\n\texpected: %v\n\t actual: %v", - rt.port, - actual.Handler.HTTPGet.Port, - ) - } - if actual.Handler.HTTPGet.Path != rt.path { - t.Errorf( - "failed componentProbe:\n\texpected: %s\n\t actual: %s", - rt.path, - actual.Handler.HTTPGet.Path, - ) - } - if actual.Handler.HTTPGet.Scheme != rt.scheme { - t.Errorf( - "failed componentProbe:\n\texpected: %v\n\t actual: %v", - rt.scheme, - actual.Handler.HTTPGet.Scheme, - ) - } - } -} - -func TestComponentPod(t *testing.T) { - var tests = []struct { - name string - expected v1.Pod - }{ - { - name: "foo", - expected: v1.Pod{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "Pod", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: "kube-system", - Annotations: map[string]string{"scheduler.alpha.kubernetes.io/critical-pod": ""}, - Labels: map[string]string{"component": "foo", "tier": "control-plane"}, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Name: "foo", - }, - }, - HostNetwork: true, - Volumes: []v1.Volume{}, - }, - }, + { // CreateControllerManagerStaticPodManifestFile + createStaticPodFunction: CreateControllerManagerStaticPodManifestFile, + expectedFiles: []string{kubeadmconstants.KubeControllerManager}, + }, + { // CreateSchedulerStaticPodManifestFile + createStaticPodFunction: CreateSchedulerStaticPodManifestFile, + expectedFiles: []string{kubeadmconstants.KubeScheduler}, }, } - for _, rt := range tests { - c := v1.Container{Name: rt.name} - actual := componentPod(c, []v1.Volume{}) - if !reflect.DeepEqual(rt.expected, actual) { - t.Errorf( - "failed componentPod:\n\texpected: %v\n\t actual: %v", - rt.expected, - actual, - ) + for _, test := range tests { + + // Create temp folder for the test case + tmpdir := testutil.SetupTempDir(t) + defer os.RemoveAll(tmpdir) + + // Creates a Master Configuration + cfg := &kubeadmapi.MasterConfiguration{ + KubernetesVersion: "v1.7.0", + } + + // Execute createStaticPodFunction + manifestPath := filepath.Join(tmpdir, kubeadmconstants.ManifestsSubDirName) + err := test.createStaticPodFunction(manifestPath, cfg) + if err != nil { + t.Errorf("Error executing createStaticPodFunction: %v", err) + continue + } + + // Assert expected files are there + testutil.AssertFilesCount(t, manifestPath, len(test.expectedFiles)) + + for _, fileName := range test.expectedFiles { + testutil.AssertFileExists(t, manifestPath, fileName+".yaml") } } } @@ -498,62 +404,6 @@ func TestGetControllerManagerCommand(t *testing.T) { } } -func TestGetEtcdCommand(t *testing.T) { - var tests = []struct { - cfg *kubeadmapi.MasterConfiguration - expected []string - }{ - { - cfg: &kubeadmapi.MasterConfiguration{ - Etcd: kubeadmapi.Etcd{DataDir: "/var/lib/etcd"}, - }, - expected: []string{ - "etcd", - "--listen-client-urls=http://127.0.0.1:2379", - "--advertise-client-urls=http://127.0.0.1:2379", - "--data-dir=/var/lib/etcd", - }, - }, - { - cfg: &kubeadmapi.MasterConfiguration{ - Etcd: kubeadmapi.Etcd{ - DataDir: "/var/lib/etcd", - ExtraArgs: map[string]string{ - "listen-client-urls": "http://10.0.1.10:2379", - "advertise-client-urls": "http://10.0.1.10:2379", - }, - }, - }, - expected: []string{ - "etcd", - "--listen-client-urls=http://10.0.1.10:2379", - "--advertise-client-urls=http://10.0.1.10:2379", - "--data-dir=/var/lib/etcd", - }, - }, - { - cfg: &kubeadmapi.MasterConfiguration{ - Etcd: kubeadmapi.Etcd{DataDir: "/etc/foo"}, - }, - expected: []string{ - "etcd", - "--listen-client-urls=http://127.0.0.1:2379", - "--advertise-client-urls=http://127.0.0.1:2379", - "--data-dir=/etc/foo", - }, - }, - } - - for _, rt := range tests { - actual := getEtcdCommand(rt.cfg) - sort.Strings(actual) - sort.Strings(rt.expected) - if !reflect.DeepEqual(actual, rt.expected) { - t.Errorf("failed getEtcdCommand:\nexpected:\n%v\nsaw:\n%v", rt.expected, actual) - } - } -} - func TestGetSchedulerCommand(t *testing.T) { var tests = []struct { cfg *kubeadmapi.MasterConfiguration @@ -651,50 +501,3 @@ func TestGetAuthzParameters(t *testing.T) { } } } - -func TestGetExtraParameters(t *testing.T) { - var tests = []struct { - overrides map[string]string - defaults map[string]string - expected []string - }{ - { - overrides: map[string]string{ - "admission-control": "NamespaceLifecycle,LimitRanger", - }, - defaults: map[string]string{ - "admission-control": "NamespaceLifecycle", - "insecure-bind-address": "127.0.0.1", - "allow-privileged": "true", - }, - expected: []string{ - "--admission-control=NamespaceLifecycle,LimitRanger", - "--insecure-bind-address=127.0.0.1", - "--allow-privileged=true", - }, - }, - { - overrides: map[string]string{ - "admission-control": "NamespaceLifecycle,LimitRanger", - }, - defaults: map[string]string{ - "insecure-bind-address": "127.0.0.1", - "allow-privileged": "true", - }, - expected: []string{ - "--admission-control=NamespaceLifecycle,LimitRanger", - "--insecure-bind-address=127.0.0.1", - "--allow-privileged=true", - }, - }, - } - - for _, rt := range tests { - actual := getExtraParameters(rt.overrides, rt.defaults) - sort.Strings(actual) - sort.Strings(rt.expected) - if !reflect.DeepEqual(actual, rt.expected) { - t.Errorf("failed getExtraParameters:\nexpected:\n%v\nsaw:\n%v", rt.expected, actual) - } - } -} diff --git a/cmd/kubeadm/app/phases/controlplane/volumes.go b/cmd/kubeadm/app/phases/controlplane/volumes.go index 14cf4c0bcc..db60823788 100644 --- a/cmd/kubeadm/app/phases/controlplane/volumes.go +++ b/cmd/kubeadm/app/phases/controlplane/volumes.go @@ -26,11 +26,11 @@ import ( "k8s.io/apimachinery/pkg/util/sets" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" + staticpodutil "k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod" ) const ( k8sCertsVolumeName = "k8s-certs" - etcdVolumeName = "etcd" caCertsVolumeName = "ca-certs" caCertsVolumePath = "/etc/ssl/certs" caCertsPkiVolumeName = "ca-certs-etc-pki" @@ -98,8 +98,8 @@ func newControlPlaneHostPathMounts() controlPlaneHostPathMounts { } func (c *controlPlaneHostPathMounts) NewHostPathMount(component, mountName, hostPath, containerPath string, readOnly bool) { - c.volumes[component] = append(c.volumes[component], newVolume(mountName, hostPath)) - c.volumeMounts[component] = append(c.volumeMounts[component], newVolumeMount(mountName, containerPath, readOnly)) + c.volumes[component] = append(c.volumes[component], staticpodutil.NewVolume(mountName, hostPath)) + c.volumeMounts[component] = append(c.volumeMounts[component], staticpodutil.NewVolumeMount(mountName, containerPath, readOnly)) } func (c *controlPlaneHostPathMounts) AddHostPathMounts(component string, vols []v1.Volume, volMounts []v1.VolumeMount) { @@ -115,25 +115,6 @@ func (c *controlPlaneHostPathMounts) GetVolumeMounts(component string) []v1.Volu return c.volumeMounts[component] } -// newVolume creates a v1.Volume with a hostPath mount to the specified location -func newVolume(name, path string) v1.Volume { - return v1.Volume{ - Name: name, - VolumeSource: v1.VolumeSource{ - HostPath: &v1.HostPathVolumeSource{Path: path}, - }, - } -} - -// newVolumeMount creates a v1.VolumeMount to the specified location -func newVolumeMount(name, path string, readOnly bool) v1.VolumeMount { - return v1.VolumeMount{ - Name: name, - MountPath: path, - ReadOnly: readOnly, - } -} - // getEtcdCertVolumes returns the volumes/volumemounts needed for talking to an external etcd cluster func getEtcdCertVolumes(etcdCfg kubeadmapi.Etcd) ([]v1.Volume, []v1.VolumeMount) { certPaths := []string{etcdCfg.CAFile, etcdCfg.CertFile, etcdCfg.KeyFile} @@ -166,8 +147,8 @@ func getEtcdCertVolumes(etcdCfg kubeadmapi.Etcd) ([]v1.Volume, []v1.VolumeMount) volumeMounts := []v1.VolumeMount{} for i, certDir := range certDirs.List() { name := fmt.Sprintf("etcd-certs-%d", i) - volumes = append(volumes, newVolume(name, certDir)) - volumeMounts = append(volumeMounts, newVolumeMount(name, certDir, true)) + volumes = append(volumes, staticpodutil.NewVolume(name, certDir)) + volumeMounts = append(volumeMounts, staticpodutil.NewVolumeMount(name, certDir, true)) } return volumes, volumeMounts } diff --git a/cmd/kubeadm/app/phases/controlplane/volumes_test.go b/cmd/kubeadm/app/phases/controlplane/volumes_test.go index 81f58a28d1..d711e03bad 100644 --- a/cmd/kubeadm/app/phases/controlplane/volumes_test.go +++ b/cmd/kubeadm/app/phases/controlplane/volumes_test.go @@ -28,77 +28,6 @@ import ( kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" ) -func TestNewVolume(t *testing.T) { - var tests = []struct { - name string - path string - expected v1.Volume - }{ - { - name: "foo", - path: "/etc/foo", - expected: v1.Volume{ - Name: "foo", - VolumeSource: v1.VolumeSource{ - HostPath: &v1.HostPathVolumeSource{Path: "/etc/foo"}, - }, - }, - }, - } - - for _, rt := range tests { - actual := newVolume(rt.name, rt.path) - if !reflect.DeepEqual(actual, rt.expected) { - t.Errorf( - "failed newVolume:\n\texpected: %v\n\t actual: %v", - rt.expected, - actual, - ) - } - } -} - -func TestNewVolumeMount(t *testing.T) { - var tests = []struct { - name string - path string - ro bool - expected v1.VolumeMount - }{ - { - name: "foo", - path: "/etc/foo", - ro: false, - expected: v1.VolumeMount{ - Name: "foo", - MountPath: "/etc/foo", - ReadOnly: false, - }, - }, - { - name: "bar", - path: "/etc/foo/bar", - ro: true, - expected: v1.VolumeMount{ - Name: "bar", - MountPath: "/etc/foo/bar", - ReadOnly: true, - }, - }, - } - - for _, rt := range tests { - actual := newVolumeMount(rt.name, rt.path, rt.ro) - if !reflect.DeepEqual(actual, rt.expected) { - t.Errorf( - "failed newVolumeMount:\n\texpected: %v\n\t actual: %v", - rt.expected, - actual, - ) - } - } -} - func TestGetEtcdCertVolumes(t *testing.T) { var tests = []struct { ca, cert, key string diff --git a/cmd/kubeadm/app/phases/etcd/local.go b/cmd/kubeadm/app/phases/etcd/local.go new file mode 100644 index 0000000000..17f2af8381 --- /dev/null +++ b/cmd/kubeadm/app/phases/etcd/local.go @@ -0,0 +1,65 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package etcd + +import ( + "k8s.io/api/core/v1" + kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" + kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" + "k8s.io/kubernetes/cmd/kubeadm/app/images" + staticpodutil "k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod" +) + +const ( + etcdVolumeName = "etcd" +) + +// CreateLocalEtcdStaticPodManifestFile will write local etcd static pod manifest file. +func CreateLocalEtcdStaticPodManifestFile(manifestDir string, cfg *kubeadmapi.MasterConfiguration) error { + + // gets etcd StaticPodSpec, actualized for the current MasterConfiguration + spec := GetEtcdPodSpec(cfg) + + // writes etcd StaticPod to disk + return staticpodutil.WriteStaticPodToDisk(kubeadmconstants.Etcd, manifestDir, spec) +} + +// GetEtcdPodSpec returns the etcd static Pod actualized to the context of the current MasterConfiguration +// NB. GetEtcdPodSpec methods holds the information about how kubeadm creates etcd static pod mainfests. +func GetEtcdPodSpec(cfg *kubeadmapi.MasterConfiguration) v1.Pod { + return staticpodutil.ComponentPod(v1.Container{ + Name: kubeadmconstants.Etcd, + Command: getEtcdCommand(cfg), + Image: images.GetCoreImage(kubeadmconstants.Etcd, cfg.ImageRepository, "", cfg.Etcd.Image), + // Mount the etcd datadir path read-write so etcd can store data in a more persistent manner + VolumeMounts: []v1.VolumeMount{staticpodutil.NewVolumeMount(etcdVolumeName, cfg.Etcd.DataDir, false)}, + LivenessProbe: staticpodutil.ComponentProbe(2379, "/health", v1.URISchemeHTTP), + }, []v1.Volume{staticpodutil.NewVolume(etcdVolumeName, cfg.Etcd.DataDir)}) +} + +// getEtcdCommand builds the right etcd command from the given config object +func getEtcdCommand(cfg *kubeadmapi.MasterConfiguration) []string { + defaultArguments := map[string]string{ + "listen-client-urls": "http://127.0.0.1:2379", + "advertise-client-urls": "http://127.0.0.1:2379", + "data-dir": cfg.Etcd.DataDir, + } + + command := []string{"etcd"} + command = append(command, staticpodutil.GetExtraParameters(cfg.Etcd.ExtraArgs, defaultArguments)...) + return command +} diff --git a/cmd/kubeadm/app/phases/etcd/local_test.go b/cmd/kubeadm/app/phases/etcd/local_test.go new file mode 100644 index 0000000000..0d076ca047 --- /dev/null +++ b/cmd/kubeadm/app/phases/etcd/local_test.go @@ -0,0 +1,125 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package etcd + +import ( + "os" + "path/filepath" + "reflect" + "sort" + "testing" + + kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" + kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" + + testutil "k8s.io/kubernetes/cmd/kubeadm/test" +) + +func TestGetEtcdPodSpec(t *testing.T) { + + // Creates a Master Configuration + cfg := &kubeadmapi.MasterConfiguration{ + KubernetesVersion: "v1.7.0", + } + + // Executes GetEtcdPodSpec + spec := GetEtcdPodSpec(cfg) + + // Assert each specs refers to the right pod + if spec.Spec.Containers[0].Name != kubeadmconstants.Etcd { + t.Errorf("getKubeConfigSpecs spec for etcd contains pod %s, expectes %s", spec.Spec.Containers[0].Name, kubeadmconstants.Etcd) + } +} + +func TestCreateLocalEtcdStaticPodManifestFile(t *testing.T) { + + // Create temp folder for the test case + tmpdir := testutil.SetupTempDir(t) + defer os.RemoveAll(tmpdir) + + // Creates a Master Configuration + cfg := &kubeadmapi.MasterConfiguration{ + KubernetesVersion: "v1.7.0", + } + + // Execute createStaticPodFunction + manifestPath := filepath.Join(tmpdir, kubeadmconstants.ManifestsSubDirName) + err := CreateLocalEtcdStaticPodManifestFile(manifestPath, cfg) + if err != nil { + t.Errorf("Error executing CreateEtcdStaticPodManifestFile: %v", err) + } + + // Assert expected files are there + testutil.AssertFilesCount(t, manifestPath, 1) + testutil.AssertFileExists(t, manifestPath, kubeadmconstants.Etcd+".yaml") +} + +func TestGetEtcdCommand(t *testing.T) { + var tests = []struct { + cfg *kubeadmapi.MasterConfiguration + expected []string + }{ + { + cfg: &kubeadmapi.MasterConfiguration{ + Etcd: kubeadmapi.Etcd{DataDir: "/var/lib/etcd"}, + }, + expected: []string{ + "etcd", + "--listen-client-urls=http://127.0.0.1:2379", + "--advertise-client-urls=http://127.0.0.1:2379", + "--data-dir=/var/lib/etcd", + }, + }, + { + cfg: &kubeadmapi.MasterConfiguration{ + Etcd: kubeadmapi.Etcd{ + DataDir: "/var/lib/etcd", + ExtraArgs: map[string]string{ + "listen-client-urls": "http://10.0.1.10:2379", + "advertise-client-urls": "http://10.0.1.10:2379", + }, + }, + }, + expected: []string{ + "etcd", + "--listen-client-urls=http://10.0.1.10:2379", + "--advertise-client-urls=http://10.0.1.10:2379", + "--data-dir=/var/lib/etcd", + }, + }, + { + cfg: &kubeadmapi.MasterConfiguration{ + Etcd: kubeadmapi.Etcd{DataDir: "/etc/foo"}, + }, + expected: []string{ + "etcd", + "--listen-client-urls=http://127.0.0.1:2379", + "--advertise-client-urls=http://127.0.0.1:2379", + "--data-dir=/etc/foo", + }, + }, + } + + for _, rt := range tests { + actual := getEtcdCommand(rt.cfg) + sort.Strings(actual) + sort.Strings(rt.expected) + if !reflect.DeepEqual(actual, rt.expected) { + t.Errorf("failed getEtcdCommand:\nexpected:\n%v\nsaw:\n%v", rt.expected, actual) + } + } +} From 11e5274e2b7a639f578ed736b7d3df254007ebbc Mon Sep 17 00:00:00 2001 From: fabriziopandini Date: Mon, 14 Aug 2017 16:31:32 +0200 Subject: [PATCH 3/4] Add CLI commands --- cmd/kubeadm/app/cmd/init.go | 13 +- cmd/kubeadm/app/cmd/phases/controlplane.go | 107 +++++++++++++ .../app/cmd/phases/controlplane_test.go | 150 ++++++++++++++++++ cmd/kubeadm/app/cmd/phases/etcd.go | 76 +++++++++ cmd/kubeadm/app/cmd/phases/etcd_test.go | 86 ++++++++++ cmd/kubeadm/app/cmd/phases/phase.go | 8 +- cmd/kubeadm/app/cmd/phases/util.go | 50 ++++++ 7 files changed, 484 insertions(+), 6 deletions(-) create mode 100644 cmd/kubeadm/app/cmd/phases/controlplane.go create mode 100644 cmd/kubeadm/app/cmd/phases/controlplane_test.go create mode 100644 cmd/kubeadm/app/cmd/phases/etcd.go create mode 100644 cmd/kubeadm/app/cmd/phases/etcd_test.go create mode 100644 cmd/kubeadm/app/cmd/phases/util.go diff --git a/cmd/kubeadm/app/cmd/init.go b/cmd/kubeadm/app/cmd/init.go index 9933ffd064..af20f58c99 100644 --- a/cmd/kubeadm/app/cmd/init.go +++ b/cmd/kubeadm/app/cmd/init.go @@ -20,7 +20,6 @@ import ( "fmt" "io" "io/ioutil" - "path/filepath" "strconv" "text/template" @@ -40,6 +39,7 @@ import ( nodebootstraptokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node" "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/pkiutil" controlplanephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane" + etcdphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/etcd" kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig" markmasterphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/markmaster" selfhostingphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/selfhosting" @@ -241,9 +241,16 @@ func (i *Init) Run(out io.Writer) error { } // PHASE 3: Bootstrap the control plane - if err := controlplanephase.WriteStaticPodManifests(i.cfg, k8sVersion, kubeadmconstants.GetStaticPodDirectory()); err != nil { + manifestPath := kubeadmconstants.GetStaticPodDirectory() + if err := controlplanephase.CreateInitStaticPodManifestFiles(manifestPath, i.cfg); err != nil { return err } + // Add etcd static pod spec only if external etcd is not configured + if len(i.cfg.Etcd.Endpoints) == 0 { + if err := etcdphase.CreateLocalEtcdStaticPodManifestFile(manifestPath, i.cfg); err != nil { + return err + } + } client, err := kubeadmutil.CreateClientAndWaitForAPI(kubeadmconstants.GetAdminKubeConfigPath()) if err != nil { @@ -319,7 +326,7 @@ func (i *Init) Run(out io.Writer) error { } ctx := map[string]string{ - "KubeConfigPath": filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.AdminKubeConfigFileName), + "KubeConfigPath": kubeadmconstants.GetAdminKubeConfigPath(), "KubeConfigName": kubeadmconstants.AdminKubeConfigFileName, "Token": i.cfg.Token, "CAPubKeyPin": pubkeypin.Hash(caCert), diff --git a/cmd/kubeadm/app/cmd/phases/controlplane.go b/cmd/kubeadm/app/cmd/phases/controlplane.go new file mode 100644 index 0000000000..0614dffef6 --- /dev/null +++ b/cmd/kubeadm/app/cmd/phases/controlplane.go @@ -0,0 +1,107 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package phases + +import ( + "github.com/spf13/cobra" + + kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" + kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" + kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" + controlplanephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane" + "k8s.io/kubernetes/pkg/api" +) + +// NewCmdControlplane return main command for Controlplane phase +func NewCmdControlplane() *cobra.Command { + cmd := &cobra.Command{ + Use: "controlplane", + Short: "Generate all static pod manifest files necessary to establish the control plane.", + RunE: subCmdRunE("controlplane"), + } + + manifestPath := kubeadmconstants.GetStaticPodDirectory() + cmd.AddCommand(getControlPlaneSubCommands(manifestPath)...) + return cmd +} + +// getControlPlaneSubCommands returns sub commands for Controlplane phase +func getControlPlaneSubCommands(outDir string) []*cobra.Command { + + cfg := &kubeadmapiext.MasterConfiguration{} + // Default values for the cobra help text + api.Scheme.Default(cfg) + + var cfgPath string + var subCmds []*cobra.Command + + subCmdProperties := []struct { + use string + short string + cmdFunc func(outDir string, cfg *kubeadmapi.MasterConfiguration) error + }{ + { + use: "all", + short: "Generate all static pod manifest files necessary to establish the control plane.", + cmdFunc: controlplanephase.CreateInitStaticPodManifestFiles, + }, + { + use: "apiserver", + short: "Generate apiserver static pod manifest.", + cmdFunc: controlplanephase.CreateAPIServerStaticPodManifestFile, + }, + { + use: "controller-manager", + short: "Generate controller-manager static pod manifest.", + cmdFunc: controlplanephase.CreateControllerManagerStaticPodManifestFile, + }, + { + use: "scheduler", + short: "Generate scheduler static pod manifest.", + cmdFunc: controlplanephase.CreateSchedulerStaticPodManifestFile, + }, + } + + for _, properties := range subCmdProperties { + // Creates the UX Command + cmd := &cobra.Command{ + Use: properties.use, + Short: properties.short, + Run: runCmdPhase(properties.cmdFunc, &outDir, &cfgPath, cfg), + } + + // Add flags to the command + cmd.Flags().StringVar(&cfg.CertificatesDir, "cert-dir", cfg.CertificatesDir, `The path where certificates are stored`) + cmd.Flags().StringVar(&cfg.KubernetesVersion, "kubernetes-version", cfg.KubernetesVersion, `Choose a specific Kubernetes version for the control plane`) + + if properties.use == "all" || properties.use == "apiserver" { + cmd.Flags().StringVar(&cfg.API.AdvertiseAddress, "apiserver-advertise-address", cfg.API.AdvertiseAddress, "The IP address the API Server will advertise it's listening on. 0.0.0.0 means the default network interface's address.") + cmd.Flags().Int32Var(&cfg.API.BindPort, "apiserver-bind-port", cfg.API.BindPort, "Port for the API Server to bind to") + cmd.Flags().StringVar(&cfg.Networking.ServiceSubnet, "service-cidr", cfg.Networking.ServiceSubnet, "Use alternative range of IP address for service VIPs") + } + + if properties.use == "all" || properties.use == "controller-manager" { + cmd.Flags().StringVar(&cfg.Networking.PodSubnet, "pod-network-cidr", cfg.Networking.PodSubnet, "Specify range of IP addresses for the pod network; if set, the control plane will automatically allocate CIDRs for every node") + } + + cmd.Flags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)") + + subCmds = append(subCmds, cmd) + } + + return subCmds +} diff --git a/cmd/kubeadm/app/cmd/phases/controlplane_test.go b/cmd/kubeadm/app/cmd/phases/controlplane_test.go new file mode 100644 index 0000000000..671794bbc8 --- /dev/null +++ b/cmd/kubeadm/app/cmd/phases/controlplane_test.go @@ -0,0 +1,150 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package phases + +import ( + "fmt" + "os" + "testing" + + // required for triggering api machinery startup when running unit tests + _ "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/install" + + testutil "k8s.io/kubernetes/cmd/kubeadm/test" + cmdtestutil "k8s.io/kubernetes/cmd/kubeadm/test/cmd" +) + +func TestControlPlaneSubCommandsHasFlags(t *testing.T) { + + subCmds := getControlPlaneSubCommands("") + + commonFlags := []string{ + "cert-dir", + "config", + } + + var tests = []struct { + command string + additionalFlags []string + }{ + { + command: "all", + additionalFlags: []string{ + "kubernetes-version", + "apiserver-advertise-address", + "apiserver-bind-port", + "service-cidr", + "pod-network-cidr", + }, + }, + { + command: "apiserver", + additionalFlags: []string{ + "kubernetes-version", + "apiserver-advertise-address", + "apiserver-bind-port", + "service-cidr", + }, + }, + { + command: "controller-manager", + additionalFlags: []string{ + "kubernetes-version", + "pod-network-cidr", + }, + }, + { + command: "scheduler", + additionalFlags: []string{ + "kubernetes-version", + }, + }, + } + + for _, test := range tests { + expectedFlags := append(commonFlags, test.additionalFlags...) + cmdtestutil.AssertSubCommandHasFlags(t, subCmds, test.command, expectedFlags...) + } +} + +func TestControlPlaneCreateFilesWithFlags(t *testing.T) { + + var tests = []struct { + command string + additionalFlags []string + expectedFiles []string + }{ + { + command: "all", + additionalFlags: []string{ + "--kubernetes-version=v1.7.0", + "--apiserver-advertise-address=1.2.3.4", + "--apiserver-bind-port=6443", + "--service-cidr=1.2.3.4/16", + "--pod-network-cidr=1.2.3.4/16", + }, + expectedFiles: []string{ + "kube-apiserver.yaml", + "kube-controller-manager.yaml", + "kube-scheduler.yaml", + }, + }, + { + command: "apiserver", + additionalFlags: []string{ + "--kubernetes-version=v1.7.0", + "--apiserver-advertise-address=1.2.3.4", + "--apiserver-bind-port=6443", + "--service-cidr=1.2.3.4/16", + }, + expectedFiles: []string{"kube-apiserver.yaml"}, + }, + { + command: "controller-manager", + additionalFlags: []string{ + "--kubernetes-version=v1.7.0", + "--pod-network-cidr=1.2.3.4/16", + }, + expectedFiles: []string{"kube-controller-manager.yaml"}, + }, + { + command: "scheduler", + additionalFlags: []string{ + "--kubernetes-version=v1.7.0", + }, + expectedFiles: []string{"kube-scheduler.yaml"}, + }, + } + + for _, test := range tests { + + // Create temp folder for the test case + tmpdir := testutil.SetupTempDir(t) + defer os.RemoveAll(tmpdir) + + // Get subcommands working in the temporary directory + subCmds := getControlPlaneSubCommands(tmpdir) + + // Execute the subcommand + certDirFlag := fmt.Sprintf("--cert-dir=%s", tmpdir) + allFlags := append(test.additionalFlags, certDirFlag) + cmdtestutil.RunSubCommand(t, subCmds, test.command, allFlags...) + + // Checks that requested files are there + testutil.AssertFileExists(t, tmpdir, test.expectedFiles...) + } +} diff --git a/cmd/kubeadm/app/cmd/phases/etcd.go b/cmd/kubeadm/app/cmd/phases/etcd.go new file mode 100644 index 0000000000..7d205160fe --- /dev/null +++ b/cmd/kubeadm/app/cmd/phases/etcd.go @@ -0,0 +1,76 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package phases + +import ( + "github.com/spf13/cobra" + + kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" + kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" + kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" + etcdphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/etcd" + "k8s.io/kubernetes/pkg/api" +) + +// NewCmdEtcd return main command for Etcd phase +func NewCmdEtcd() *cobra.Command { + cmd := &cobra.Command{ + Use: "etcd", + Short: "Generate static pod manifest file for etcd.", + RunE: subCmdRunE("etcd"), + } + + manifestPath := kubeadmconstants.GetStaticPodDirectory() + cmd.AddCommand(getEtcdSubCommands(manifestPath)...) + return cmd +} + +// getEtcdSubCommands returns sub commands for etcd phase +func getEtcdSubCommands(outDir string) []*cobra.Command { + + cfg := &kubeadmapiext.MasterConfiguration{} + // Default values for the cobra help text + api.Scheme.Default(cfg) + + var cfgPath string + var subCmds []*cobra.Command + + properties := struct { + use string + short string + cmdFunc func(outDir string, cfg *kubeadmapi.MasterConfiguration) error + }{ + use: "local", + short: "Generate static pod manifest file for a local, single-node etcd instance.", + cmdFunc: etcdphase.CreateLocalEtcdStaticPodManifestFile, + } + + // Creates the UX Command + cmd := &cobra.Command{ + Use: properties.use, + Short: properties.short, + Run: runCmdPhase(properties.cmdFunc, &outDir, &cfgPath, cfg), + } + + // Add flags to the command + cmd.Flags().StringVar(&cfg.CertificatesDir, "cert-dir", cfg.CertificatesDir, `The path where certificates are stored`) + cmd.Flags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)") + + subCmds = append(subCmds, cmd) + + return subCmds +} diff --git a/cmd/kubeadm/app/cmd/phases/etcd_test.go b/cmd/kubeadm/app/cmd/phases/etcd_test.go new file mode 100644 index 0000000000..0f6a128430 --- /dev/null +++ b/cmd/kubeadm/app/cmd/phases/etcd_test.go @@ -0,0 +1,86 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package phases + +import ( + "fmt" + "os" + "testing" + + // required for triggering api machinery startup when running unit tests + _ "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/install" + + testutil "k8s.io/kubernetes/cmd/kubeadm/test" + cmdtestutil "k8s.io/kubernetes/cmd/kubeadm/test/cmd" +) + +func TestEtcdSubCommandsHasFlags(t *testing.T) { + + subCmds := getEtcdSubCommands("") + + commonFlags := []string{ + "cert-dir", + "config", + } + + var tests = []struct { + command string + additionalFlags []string + }{ + { + command: "local", + }, + } + + for _, test := range tests { + expectedFlags := append(commonFlags, test.additionalFlags...) + cmdtestutil.AssertSubCommandHasFlags(t, subCmds, test.command, expectedFlags...) + } +} + +func TestEtcdCreateFilesWithFlags(t *testing.T) { + + var tests = []struct { + command string + additionalFlags []string + expectedFiles []string + }{ + { + command: "local", + expectedFiles: []string{"etcd.yaml"}, + additionalFlags: []string{}, + }, + } + + for _, test := range tests { + + // Create temp folder for the test case + tmpdir := testutil.SetupTempDir(t) + defer os.RemoveAll(tmpdir) + + // Get subcommands working in the temporary directory + subCmds := getEtcdSubCommands(tmpdir) + + // Execute the subcommand + certDirFlag := fmt.Sprintf("--cert-dir=%s", tmpdir) + allFlags := append(test.additionalFlags, certDirFlag) + cmdtestutil.RunSubCommand(t, subCmds, test.command, allFlags...) + + // Checks that requested files are there + testutil.AssertFileExists(t, tmpdir, test.expectedFiles...) + } +} diff --git a/cmd/kubeadm/app/cmd/phases/phase.go b/cmd/kubeadm/app/cmd/phases/phase.go index e2b7a95437..fd6edbfed0 100644 --- a/cmd/kubeadm/app/cmd/phases/phase.go +++ b/cmd/kubeadm/app/cmd/phases/phase.go @@ -31,13 +31,15 @@ func NewCmdPhase(out io.Writer) *cobra.Command { RunE: subCmdRunE("phase"), } - cmd.AddCommand(NewCmdKubeConfig(out)) + cmd.AddCommand(NewCmdBootstrapToken()) cmd.AddCommand(NewCmdCerts()) + cmd.AddCommand(NewCmdControlplane()) + cmd.AddCommand(NewCmdEtcd()) + cmd.AddCommand(NewCmdKubeConfig(out)) + cmd.AddCommand(NewCmdMarkMaster()) cmd.AddCommand(NewCmdPreFlight()) cmd.AddCommand(NewCmdSelfhosting()) - cmd.AddCommand(NewCmdMarkMaster()) cmd.AddCommand(NewCmdUploadConfig()) - cmd.AddCommand(NewCmdBootstrapToken()) return cmd } diff --git a/cmd/kubeadm/app/cmd/phases/util.go b/cmd/kubeadm/app/cmd/phases/util.go new file mode 100644 index 0000000000..32b7f3fb1d --- /dev/null +++ b/cmd/kubeadm/app/cmd/phases/util.go @@ -0,0 +1,50 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package phases + +import ( + "github.com/spf13/cobra" + + kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" + kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" + "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation" + kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" + configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" +) + +// runCmdPhase creates a cobra.Command Run function, by composing the call to the given cmdFunc with necessary additional steps (e.g preparation of input parameters) +func runCmdPhase(cmdFunc func(outDir string, cfg *kubeadmapi.MasterConfiguration) error, outDir, cfgPath *string, cfg *kubeadmapiext.MasterConfiguration) func(cmd *cobra.Command, args []string) { + + // the following statement build a clousure that wraps a call to a cmdFunc, binding + // the function itself with the specific parameters of each sub command. + // Please note that specific parameter should be passed as value, while other parameters - passed as reference - + // are shared between sub commands and gets access to current value e.g. flags value. + + return func(cmd *cobra.Command, args []string) { + if err := validation.ValidateMixedArguments(cmd.Flags()); err != nil { + kubeadmutil.CheckErr(err) + } + + // This call returns the ready-to-use configuration based on the configuration file that might or might not exist and the default cfg populated by flags + internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(*cfgPath, cfg) + kubeadmutil.CheckErr(err) + + // Execute the cmdFunc + err = cmdFunc(*outDir, internalcfg) + kubeadmutil.CheckErr(err) + } +} From 8ab27c1fbe5890fda42c715b972df35fcfa90162 Mon Sep 17 00:00:00 2001 From: fabriziopandini Date: Mon, 14 Aug 2017 16:31:53 +0200 Subject: [PATCH 4/4] Autogenerated bazel etc. --- build/visible_to/BUILD | 1 - cmd/kubeadm/app/BUILD | 1 + cmd/kubeadm/app/cmd/BUILD | 1 + cmd/kubeadm/app/cmd/phases/BUILD | 8 ++++ cmd/kubeadm/app/phases/controlplane/BUILD | 11 +----- cmd/kubeadm/app/phases/etcd/BUILD | 47 ++++++++++++++++++++++ cmd/kubeadm/app/util/BUILD | 1 + cmd/kubeadm/app/util/staticpod/BUILD | 48 +++++++++++++++++++++++ 8 files changed, 108 insertions(+), 10 deletions(-) create mode 100644 cmd/kubeadm/app/phases/etcd/BUILD create mode 100644 cmd/kubeadm/app/util/staticpod/BUILD diff --git a/build/visible_to/BUILD b/build/visible_to/BUILD index df805b5a3e..137ba26670 100644 --- a/build/visible_to/BUILD +++ b/build/visible_to/BUILD @@ -74,7 +74,6 @@ package_group( packages = [ "//cmd/kubeadm/app", "//cmd/kubeadm/app/cmd", - "//cmd/kubeadm/app/phases/controlplane", ], ) diff --git a/cmd/kubeadm/app/BUILD b/cmd/kubeadm/app/BUILD index fb884978fa..a6bbc13b21 100644 --- a/cmd/kubeadm/app/BUILD +++ b/cmd/kubeadm/app/BUILD @@ -39,6 +39,7 @@ filegroup( "//cmd/kubeadm/app/phases/bootstraptoken/node:all-srcs", "//cmd/kubeadm/app/phases/certs:all-srcs", "//cmd/kubeadm/app/phases/controlplane:all-srcs", + "//cmd/kubeadm/app/phases/etcd:all-srcs", "//cmd/kubeadm/app/phases/kubeconfig:all-srcs", "//cmd/kubeadm/app/phases/markmaster:all-srcs", "//cmd/kubeadm/app/phases/selfhosting:all-srcs", diff --git a/cmd/kubeadm/app/cmd/BUILD b/cmd/kubeadm/app/cmd/BUILD index c28fe2504e..e3a58fe8a5 100644 --- a/cmd/kubeadm/app/cmd/BUILD +++ b/cmd/kubeadm/app/cmd/BUILD @@ -31,6 +31,7 @@ go_library( "//cmd/kubeadm/app/phases/bootstraptoken/node:go_default_library", "//cmd/kubeadm/app/phases/certs/pkiutil:go_default_library", "//cmd/kubeadm/app/phases/controlplane:go_default_library", + "//cmd/kubeadm/app/phases/etcd:go_default_library", "//cmd/kubeadm/app/phases/kubeconfig:go_default_library", "//cmd/kubeadm/app/phases/markmaster:go_default_library", "//cmd/kubeadm/app/phases/selfhosting:go_default_library", diff --git a/cmd/kubeadm/app/cmd/phases/BUILD b/cmd/kubeadm/app/cmd/phases/BUILD index dba0606765..8f683403e7 100644 --- a/cmd/kubeadm/app/cmd/phases/BUILD +++ b/cmd/kubeadm/app/cmd/phases/BUILD @@ -11,21 +11,27 @@ go_library( srcs = [ "bootstraptoken.go", "certs.go", + "controlplane.go", + "etcd.go", "kubeconfig.go", "markmaster.go", "phase.go", "preflight.go", "selfhosting.go", "uploadconfig.go", + "util.go", ], deps = [ "//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library", + "//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library", "//cmd/kubeadm/app/constants:go_default_library", "//cmd/kubeadm/app/phases/bootstraptoken/clusterinfo:go_default_library", "//cmd/kubeadm/app/phases/bootstraptoken/node:go_default_library", "//cmd/kubeadm/app/phases/certs:go_default_library", "//cmd/kubeadm/app/phases/certs/pkiutil:go_default_library", + "//cmd/kubeadm/app/phases/controlplane:go_default_library", + "//cmd/kubeadm/app/phases/etcd:go_default_library", "//cmd/kubeadm/app/phases/kubeconfig:go_default_library", "//cmd/kubeadm/app/phases/markmaster:go_default_library", "//cmd/kubeadm/app/phases/selfhosting:go_default_library", @@ -45,6 +51,8 @@ go_test( name = "go_default_test", srcs = [ "certs_test.go", + "controlplane_test.go", + "etcd_test.go", "kubeconfig_test.go", "phase_test.go", ], diff --git a/cmd/kubeadm/app/phases/controlplane/BUILD b/cmd/kubeadm/app/phases/controlplane/BUILD index 72d10123b5..71b71eebcc 100644 --- a/cmd/kubeadm/app/phases/controlplane/BUILD +++ b/cmd/kubeadm/app/phases/controlplane/BUILD @@ -16,11 +16,9 @@ go_test( deps = [ "//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/constants:go_default_library", + "//cmd/kubeadm/test:go_default_library", "//pkg/util/version:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library", ], ) @@ -35,15 +33,10 @@ go_library( "//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library", "//cmd/kubeadm/app/constants:go_default_library", "//cmd/kubeadm/app/images:go_default_library", + "//cmd/kubeadm/app/util/staticpod:go_default_library", "//pkg/kubeapiserver/authorizer/modes:go_default_library", - "//pkg/kubectl/cmd/util:go_default_library", - "//pkg/kubelet/types:go_default_library", "//pkg/util/version:go_default_library", - "//vendor/github.com/ghodss/yaml:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", ], ) diff --git a/cmd/kubeadm/app/phases/etcd/BUILD b/cmd/kubeadm/app/phases/etcd/BUILD new file mode 100644 index 0000000000..91d159ac60 --- /dev/null +++ b/cmd/kubeadm/app/phases/etcd/BUILD @@ -0,0 +1,47 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_test( + name = "go_default_test", + srcs = ["local_test.go"], + library = ":go_default_library", + tags = ["automanaged"], + deps = [ + "//cmd/kubeadm/app/apis/kubeadm:go_default_library", + "//cmd/kubeadm/app/constants:go_default_library", + "//cmd/kubeadm/test:go_default_library", + ], +) + +go_library( + name = "go_default_library", + srcs = ["local.go"], + tags = ["automanaged"], + deps = [ + "//cmd/kubeadm/app/apis/kubeadm:go_default_library", + "//cmd/kubeadm/app/constants:go_default_library", + "//cmd/kubeadm/app/images:go_default_library", + "//cmd/kubeadm/app/util/staticpod:go_default_library", + "//vendor/k8s.io/api/core/v1:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], +) diff --git a/cmd/kubeadm/app/util/BUILD b/cmd/kubeadm/app/util/BUILD index 8c6cb6b799..ba1dd61d25 100644 --- a/cmd/kubeadm/app/util/BUILD +++ b/cmd/kubeadm/app/util/BUILD @@ -52,6 +52,7 @@ filegroup( "//cmd/kubeadm/app/util/config:all-srcs", "//cmd/kubeadm/app/util/kubeconfig:all-srcs", "//cmd/kubeadm/app/util/pubkeypin:all-srcs", + "//cmd/kubeadm/app/util/staticpod:all-srcs", "//cmd/kubeadm/app/util/token:all-srcs", ], tags = ["automanaged"], diff --git a/cmd/kubeadm/app/util/staticpod/BUILD b/cmd/kubeadm/app/util/staticpod/BUILD new file mode 100644 index 0000000000..51c976f2d9 --- /dev/null +++ b/cmd/kubeadm/app/util/staticpod/BUILD @@ -0,0 +1,48 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_test( + name = "go_default_test", + srcs = ["utils_test.go"], + library = ":go_default_library", + tags = ["automanaged"], + deps = [ + "//vendor/k8s.io/api/core/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", + ], +) + +go_library( + name = "go_default_library", + srcs = ["utils.go"], + tags = ["automanaged"], + deps = [ + "//cmd/kubeadm/app/constants:go_default_library", + "//pkg/kubelet/types:go_default_library", + "//vendor/github.com/ghodss/yaml:go_default_library", + "//vendor/k8s.io/api/core/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], +)