123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- /*
- 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 (
- "bytes"
- "fmt"
- "io/ioutil"
- "net"
- "net/url"
- "os"
- "sort"
- "strings"
- "github.com/pkg/errors"
- "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/api/resource"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
- kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
- "k8s.io/kubernetes/cmd/kubeadm/app/util"
- )
- const (
- // kubeControllerManagerAddressArg represents the address argument of the kube-controller-manager configuration.
- kubeControllerManagerAddressArg = "address"
- // kubeSchedulerAddressArg represents the address argument of the kube-scheduler configuration.
- kubeSchedulerAddressArg = "address"
- // etcdListenClientURLsArg represents the listen-client-urls argument of the etcd configuration.
- etcdListenClientURLsArg = "listen-client-urls"
- )
- // ComponentPod returns a Pod object from the container and volume specifications
- func ComponentPod(container v1.Container, volumes map[string]v1.Volume) v1.Pod {
- return v1.Pod{
- TypeMeta: metav1.TypeMeta{
- APIVersion: "v1",
- Kind: "Pod",
- },
- ObjectMeta: metav1.ObjectMeta{
- Name: container.Name,
- Namespace: metav1.NamespaceSystem,
- // 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},
- PriorityClassName: "system-cluster-critical",
- HostNetwork: true,
- Volumes: VolumeMapToSlice(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),
- },
- }
- }
- // EtcdProbe is a helper function for building a shell-based, etcdctl v1.Probe object to healthcheck etcd
- func EtcdProbe(cfg *kubeadmapi.Etcd, port int, certsDir string, CACertName string, CertName string, KeyName string) *v1.Probe {
- tlsFlags := fmt.Sprintf("--cacert=%[1]s/%[2]s --cert=%[1]s/%[3]s --key=%[1]s/%[4]s", certsDir, CACertName, CertName, KeyName)
- // etcd pod is alive if a linearizable get succeeds.
- cmd := fmt.Sprintf("ETCDCTL_API=3 etcdctl --endpoints=https://[%s]:%d %s get foo", GetEtcdProbeAddress(cfg), port, tlsFlags)
- return &v1.Probe{
- Handler: v1.Handler{
- Exec: &v1.ExecAction{
- Command: []string{"/bin/sh", "-ec", cmd},
- },
- },
- InitialDelaySeconds: 15,
- TimeoutSeconds: 15,
- FailureThreshold: 8,
- }
- }
- // NewVolume creates a v1.Volume with a hostPath mount to the specified location
- func NewVolume(name, path string, pathType *v1.HostPathType) v1.Volume {
- return v1.Volume{
- Name: name,
- VolumeSource: v1.VolumeSource{
- HostPath: &v1.HostPathVolumeSource{
- Path: path,
- Type: pathType,
- },
- },
- }
- }
- // 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,
- }
- }
- // VolumeMapToSlice returns a slice of volumes from a map's values
- func VolumeMapToSlice(volumes map[string]v1.Volume) []v1.Volume {
- v := make([]v1.Volume, 0, len(volumes))
- for _, vol := range volumes {
- v = append(v, vol)
- }
- sort.Slice(v, func(i, j int) bool {
- return strings.Compare(v[i].Name, v[j].Name) == -1
- })
- return v
- }
- // VolumeMountMapToSlice returns a slice of volumes from a map's values
- func VolumeMountMapToSlice(volumeMounts map[string]v1.VolumeMount) []v1.VolumeMount {
- v := make([]v1.VolumeMount, 0, len(volumeMounts))
- for _, volMount := range volumeMounts {
- v = append(v, volMount)
- }
- sort.Slice(v, func(i, j int) bool {
- return strings.Compare(v[i].Name, v[j].Name) == -1
- })
- return v
- }
- // 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 errors.Wrapf(err, "failed to create directory %q", manifestDir)
- }
- // writes the pod to disk
- serialized, err := util.MarshalToYaml(&pod, v1.SchemeGroupVersion)
- if err != nil {
- return errors.Wrapf(err, "failed to marshal manifest for %q to YAML", componentName)
- }
- filename := kubeadmconstants.GetStaticPodFilepath(componentName, manifestDir)
- if err := ioutil.WriteFile(filename, serialized, 0600); err != nil {
- return errors.Wrapf(err, "failed to write static pod manifest file for %q (%q)", componentName, filename)
- }
- return nil
- }
- // ReadStaticPodFromDisk reads a static pod file from disk
- func ReadStaticPodFromDisk(manifestPath string) (*v1.Pod, error) {
- buf, err := ioutil.ReadFile(manifestPath)
- if err != nil {
- return &v1.Pod{}, errors.Wrapf(err, "failed to read manifest for %q", manifestPath)
- }
- obj, err := util.UnmarshalFromYaml(buf, v1.SchemeGroupVersion)
- if err != nil {
- return &v1.Pod{}, errors.Errorf("failed to unmarshal manifest for %q from YAML: %v", manifestPath, err)
- }
- pod := obj.(*v1.Pod)
- return pod, nil
- }
- // GetAPIServerProbeAddress returns the probe address for the API server
- func GetAPIServerProbeAddress(endpoint *kubeadmapi.APIEndpoint) string {
- // In the case of a self-hosted deployment, the initial host on which kubeadm --init is run,
- // will generate a DaemonSet with a nodeSelector such that all nodes with the label
- // node-role.kubernetes.io/master='' will have the API server deployed to it. Since the init
- // is run only once on an initial host, the API advertise address will be invalid for any
- // future hosts that do not have the same address. Furthermore, since liveness and readiness
- // probes do not support the Downward API we cannot dynamically set the advertise address to
- // the node's IP. The only option then is to use localhost.
- if endpoint != nil && endpoint.AdvertiseAddress != "" {
- return endpoint.AdvertiseAddress
- }
- return "127.0.0.1"
- }
- // GetControllerManagerProbeAddress returns the kubernetes controller manager probe address
- func GetControllerManagerProbeAddress(cfg *kubeadmapi.ClusterConfiguration) string {
- if addr, exists := cfg.ControllerManager.ExtraArgs[kubeControllerManagerAddressArg]; exists {
- return addr
- }
- return "127.0.0.1"
- }
- // GetSchedulerProbeAddress returns the kubernetes scheduler probe address
- func GetSchedulerProbeAddress(cfg *kubeadmapi.ClusterConfiguration) string {
- if addr, exists := cfg.Scheduler.ExtraArgs[kubeSchedulerAddressArg]; exists {
- return addr
- }
- return "127.0.0.1"
- }
- // GetEtcdProbeAddress returns the etcd probe address
- func GetEtcdProbeAddress(cfg *kubeadmapi.Etcd) string {
- if cfg.Local != nil && cfg.Local.ExtraArgs != nil {
- if arg, exists := cfg.Local.ExtraArgs[etcdListenClientURLsArg]; exists {
- // Use the first url in the listen-client-urls if multiple url's are specified.
- if strings.ContainsAny(arg, ",") {
- arg = strings.Split(arg, ",")[0]
- }
- parsedURL, err := url.Parse(arg)
- if err != nil || parsedURL.Hostname() == "" {
- return "127.0.0.1"
- }
- // Return the IP if the URL contains an address instead of a name.
- if ip := net.ParseIP(parsedURL.Hostname()); ip != nil {
- // etcdctl doesn't support auto-converting zero addresses into loopback addresses
- if ip.Equal(net.IPv4zero) {
- return "127.0.0.1"
- }
- if ip.Equal(net.IPv6zero) {
- return net.IPv6loopback.String()
- }
- return ip.String()
- }
- // Use the local resolver to try resolving the name within the URL.
- // If the name can not be resolved, return an IPv4 loopback address.
- // Otherwise, select the first valid IPv4 address.
- // If the name does not resolve to an IPv4 address, select the first valid IPv6 address.
- addrs, err := net.LookupIP(parsedURL.Hostname())
- if err != nil {
- return "127.0.0.1"
- }
- var ip net.IP
- for _, addr := range addrs {
- if addr.To4() != nil {
- ip = addr
- break
- }
- if addr.To16() != nil && ip == nil {
- ip = addr
- }
- }
- return ip.String()
- }
- }
- return "127.0.0.1"
- }
- // ManifestFilesAreEqual compares 2 files. It returns true if their contents are equal, false otherwise
- func ManifestFilesAreEqual(path1, path2 string) (bool, error) {
- content1, err := ioutil.ReadFile(path1)
- if err != nil {
- return false, err
- }
- content2, err := ioutil.ReadFile(path2)
- if err != nil {
- return false, err
- }
- return bytes.Equal(content1, content2), nil
- }
|