Add generated file
This PR adds generated files under pkg/client and vendor folder.
This commit is contained in:
65
vendor/k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane/BUILD
generated
vendored
Normal file
65
vendor/k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane/BUILD
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"manifests_test.go",
|
||||
"volumes_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/app/features:go_default_library",
|
||||
"//cmd/kubeadm/app/phases/certs:go_default_library",
|
||||
"//cmd/kubeadm/test:go_default_library",
|
||||
"//pkg/kubeapiserver/authorizer/modes:go_default_library",
|
||||
"//pkg/util/pointer:go_default_library",
|
||||
"//pkg/util/version:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"manifests.go",
|
||||
"volumes.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane",
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha2:go_default_library",
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/app/features:go_default_library",
|
||||
"//cmd/kubeadm/app/images:go_default_library",
|
||||
"//cmd/kubeadm/app/phases/certs:go_default_library",
|
||||
"//cmd/kubeadm/app/util:go_default_library",
|
||||
"//cmd/kubeadm/app/util/staticpod:go_default_library",
|
||||
"//pkg/kubeapiserver/authorizer/modes:go_default_library",
|
||||
"//pkg/util/version:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
347
vendor/k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane/manifests.go
generated
vendored
Normal file
347
vendor/k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane/manifests.go
generated
vendored
Normal file
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
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 controlplane
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/features"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/images"
|
||||
certphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
staticpodutil "k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod"
|
||||
authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
|
||||
"k8s.io/kubernetes/pkg/util/version"
|
||||
)
|
||||
|
||||
// CreateInitStaticPodManifestFiles will write all static pod manifest files needed to bring up the control plane.
|
||||
func CreateInitStaticPodManifestFiles(manifestDir string, cfg *kubeadmapi.MasterConfiguration) error {
|
||||
glog.V(1).Infoln("[controlplane] creating static pod files")
|
||||
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 {
|
||||
glog.V(1).Infoln("creating APIserver static pod files")
|
||||
return createStaticPodFiles(manifestDir, cfg, kubeadmconstants.KubeAPIServer)
|
||||
}
|
||||
|
||||
// CreateControllerManagerStaticPodManifestFile will write controller manager static pod manifest file.
|
||||
func CreateControllerManagerStaticPodManifestFile(manifestDir string, cfg *kubeadmapi.MasterConfiguration) error {
|
||||
glog.V(1).Infoln("creating controller manager static pod files")
|
||||
return createStaticPodFiles(manifestDir, cfg, kubeadmconstants.KubeControllerManager)
|
||||
}
|
||||
|
||||
// CreateSchedulerStaticPodManifestFile will write scheduler static pod manifest file.
|
||||
func CreateSchedulerStaticPodManifestFile(manifestDir string, cfg *kubeadmapi.MasterConfiguration) error {
|
||||
glog.V(1).Infoln("creating scheduler static pod files")
|
||||
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 manifests.
|
||||
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: staticpodutil.ComponentPod(v1.Container{
|
||||
Name: kubeadmconstants.KubeAPIServer,
|
||||
Image: images.GetCoreImage(kubeadmconstants.KubeAPIServer, cfg.GetControlPlaneImageRepository(), cfg.KubernetesVersion, cfg.UnifiedControlPlaneImage),
|
||||
ImagePullPolicy: v1.PullIfNotPresent,
|
||||
Command: getAPIServerCommand(cfg),
|
||||
VolumeMounts: staticpodutil.VolumeMountMapToSlice(mounts.GetVolumeMounts(kubeadmconstants.KubeAPIServer)),
|
||||
LivenessProbe: staticpodutil.ComponentProbe(cfg, kubeadmconstants.KubeAPIServer, int(cfg.API.BindPort), "/healthz", v1.URISchemeHTTPS),
|
||||
Resources: staticpodutil.ComponentResources("250m"),
|
||||
Env: getProxyEnvVars(),
|
||||
}, mounts.GetVolumes(kubeadmconstants.KubeAPIServer)),
|
||||
kubeadmconstants.KubeControllerManager: staticpodutil.ComponentPod(v1.Container{
|
||||
Name: kubeadmconstants.KubeControllerManager,
|
||||
Image: images.GetCoreImage(kubeadmconstants.KubeControllerManager, cfg.GetControlPlaneImageRepository(), cfg.KubernetesVersion, cfg.UnifiedControlPlaneImage),
|
||||
ImagePullPolicy: v1.PullIfNotPresent,
|
||||
Command: getControllerManagerCommand(cfg, k8sVersion),
|
||||
VolumeMounts: staticpodutil.VolumeMountMapToSlice(mounts.GetVolumeMounts(kubeadmconstants.KubeControllerManager)),
|
||||
LivenessProbe: staticpodutil.ComponentProbe(cfg, kubeadmconstants.KubeControllerManager, 10252, "/healthz", v1.URISchemeHTTP),
|
||||
Resources: staticpodutil.ComponentResources("200m"),
|
||||
Env: getProxyEnvVars(),
|
||||
}, mounts.GetVolumes(kubeadmconstants.KubeControllerManager)),
|
||||
kubeadmconstants.KubeScheduler: staticpodutil.ComponentPod(v1.Container{
|
||||
Name: kubeadmconstants.KubeScheduler,
|
||||
Image: images.GetCoreImage(kubeadmconstants.KubeScheduler, cfg.GetControlPlaneImageRepository(), cfg.KubernetesVersion, cfg.UnifiedControlPlaneImage),
|
||||
ImagePullPolicy: v1.PullIfNotPresent,
|
||||
Command: getSchedulerCommand(cfg),
|
||||
VolumeMounts: staticpodutil.VolumeMountMapToSlice(mounts.GetVolumeMounts(kubeadmconstants.KubeScheduler)),
|
||||
LivenessProbe: staticpodutil.ComponentProbe(cfg, kubeadmconstants.KubeScheduler, 10251, "/healthz", v1.URISchemeHTTP),
|
||||
Resources: staticpodutil.ComponentResources("100m"),
|
||||
Env: getProxyEnvVars(),
|
||||
}, mounts.GetVolumes(kubeadmconstants.KubeScheduler)),
|
||||
}
|
||||
return staticPodSpecs
|
||||
}
|
||||
|
||||
// createStaticPodFiles creates all the requested static pod files.
|
||||
func createStaticPodFiles(manifestDir string, cfg *kubeadmapi.MasterConfiguration, componentNames ...string) error {
|
||||
// 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
|
||||
}
|
||||
|
||||
// gets the StaticPodSpecs, actualized for the current MasterConfiguration
|
||||
glog.V(1).Infoln("[controlplane] getting StaticPodSpecs")
|
||||
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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
fmt.Printf("[controlplane] wrote Static Pod manifest for component %s to %q\n", componentName, kubeadmconstants.GetStaticPodFilepath(componentName, manifestDir))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getAPIServerCommand builds the right API server command from the given config object and version
|
||||
func getAPIServerCommand(cfg *kubeadmapi.MasterConfiguration) []string {
|
||||
defaultArguments := map[string]string{
|
||||
"advertise-address": cfg.API.AdvertiseAddress,
|
||||
"insecure-port": "0",
|
||||
"enable-admission-plugins": "NodeRestriction",
|
||||
// TODO: remove `PersistentVolumeLabel` in kubeadm v1.11, as it's automatically disabled in v1.11.
|
||||
// ref: https://github.com/kubernetes/kubernetes/pull/64326
|
||||
// we can't skip it now as we support v1.10 clusters still.
|
||||
// remove it from the unit tests too.
|
||||
"disable-admission-plugins": "PersistentVolumeLabel",
|
||||
"service-cluster-ip-range": cfg.Networking.ServiceSubnet,
|
||||
"service-account-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPublicKeyName),
|
||||
"client-ca-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.CACertName),
|
||||
"tls-cert-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerCertName),
|
||||
"tls-private-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerKeyName),
|
||||
"kubelet-client-certificate": filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerKubeletClientCertName),
|
||||
"kubelet-client-key": filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerKubeletClientKeyName),
|
||||
"enable-bootstrap-token-auth": "true",
|
||||
"secure-port": fmt.Sprintf("%d", cfg.API.BindPort),
|
||||
"allow-privileged": "true",
|
||||
"kubelet-preferred-address-types": "InternalIP,ExternalIP,Hostname",
|
||||
// add options to configure the front proxy. Without the generated client cert, this will never be useable
|
||||
// so add it unconditionally with recommended values
|
||||
"requestheader-username-headers": "X-Remote-User",
|
||||
"requestheader-group-headers": "X-Remote-Group",
|
||||
"requestheader-extra-headers-prefix": "X-Remote-Extra-",
|
||||
"requestheader-client-ca-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.FrontProxyCACertName),
|
||||
"requestheader-allowed-names": "front-proxy-client",
|
||||
"proxy-client-cert-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.FrontProxyClientCertName),
|
||||
"proxy-client-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.FrontProxyClientKeyName),
|
||||
}
|
||||
|
||||
command := []string{"kube-apiserver"}
|
||||
|
||||
// If the user set endpoints for an external etcd cluster
|
||||
if cfg.Etcd.External != nil {
|
||||
defaultArguments["etcd-servers"] = strings.Join(cfg.Etcd.External.Endpoints, ",")
|
||||
|
||||
// Use any user supplied etcd certificates
|
||||
if cfg.Etcd.External.CAFile != "" {
|
||||
defaultArguments["etcd-cafile"] = cfg.Etcd.External.CAFile
|
||||
}
|
||||
if cfg.Etcd.External.CertFile != "" && cfg.Etcd.External.KeyFile != "" {
|
||||
defaultArguments["etcd-certfile"] = cfg.Etcd.External.CertFile
|
||||
defaultArguments["etcd-keyfile"] = cfg.Etcd.External.KeyFile
|
||||
}
|
||||
} else {
|
||||
// Default to etcd static pod on localhost
|
||||
defaultArguments["etcd-servers"] = "https://127.0.0.1:2379"
|
||||
defaultArguments["etcd-cafile"] = filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdCACertName)
|
||||
defaultArguments["etcd-certfile"] = filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerEtcdClientCertName)
|
||||
defaultArguments["etcd-keyfile"] = filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerEtcdClientKeyName)
|
||||
}
|
||||
|
||||
if features.Enabled(cfg.FeatureGates, features.HighAvailability) {
|
||||
defaultArguments["endpoint-reconciler-type"] = kubeadmconstants.LeaseEndpointReconcilerType
|
||||
}
|
||||
|
||||
if features.Enabled(cfg.FeatureGates, features.DynamicKubeletConfig) {
|
||||
defaultArguments["feature-gates"] = "DynamicKubeletConfig=true"
|
||||
}
|
||||
|
||||
if features.Enabled(cfg.FeatureGates, features.Auditing) {
|
||||
defaultArguments["audit-policy-file"] = kubeadmconstants.GetStaticPodAuditPolicyFile()
|
||||
defaultArguments["audit-log-path"] = filepath.Join(kubeadmconstants.StaticPodAuditPolicyLogDir, kubeadmconstants.AuditPolicyLogFile)
|
||||
if cfg.AuditPolicyConfiguration.LogMaxAge == nil {
|
||||
defaultArguments["audit-log-maxage"] = fmt.Sprintf("%d", kubeadmapiv1alpha2.DefaultAuditPolicyLogMaxAge)
|
||||
} else {
|
||||
defaultArguments["audit-log-maxage"] = fmt.Sprintf("%d", *cfg.AuditPolicyConfiguration.LogMaxAge)
|
||||
}
|
||||
}
|
||||
if cfg.APIServerExtraArgs == nil {
|
||||
cfg.APIServerExtraArgs = map[string]string{}
|
||||
}
|
||||
cfg.APIServerExtraArgs["authorization-mode"] = getAuthzModes(cfg.APIServerExtraArgs["authorization-mode"])
|
||||
command = append(command, kubeadmutil.BuildArgumentListFromMap(defaultArguments, cfg.APIServerExtraArgs)...)
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
// getAuthzModes gets the authorization-related parameters to the api server
|
||||
// Node,RBAC should be fixed in this order at the beginning
|
||||
// AlwaysAllow and AlwaysDeny is ignored as they are only for testing
|
||||
func getAuthzModes(authzModeExtraArgs string) string {
|
||||
modes := []string{
|
||||
authzmodes.ModeNode,
|
||||
authzmodes.ModeRBAC,
|
||||
}
|
||||
if strings.Contains(authzModeExtraArgs, authzmodes.ModeABAC) {
|
||||
modes = append(modes, authzmodes.ModeABAC)
|
||||
}
|
||||
if strings.Contains(authzModeExtraArgs, authzmodes.ModeWebhook) {
|
||||
modes = append(modes, authzmodes.ModeWebhook)
|
||||
}
|
||||
return strings.Join(modes, ",")
|
||||
}
|
||||
|
||||
// calcNodeCidrSize determines the size of the subnets used on each node, based
|
||||
// on the pod subnet provided. For IPv4, we assume that the pod subnet will
|
||||
// be /16 and use /24. If the pod subnet cannot be parsed, the IPv4 value will
|
||||
// be used (/24).
|
||||
//
|
||||
// For IPv6, the algorithm will do two three. First, the node CIDR will be set
|
||||
// to a multiple of 8, using the available bits for easier readability by user.
|
||||
// Second, the number of nodes will be 512 to 64K to attempt to maximize the
|
||||
// number of nodes (see NOTE below). Third, pod networks of /113 and larger will
|
||||
// be rejected, as the amount of bits available is too small.
|
||||
//
|
||||
// A special case is when the pod network size is /112, where /120 will be used,
|
||||
// only allowing 256 nodes and 256 pods.
|
||||
//
|
||||
// If the pod network size is /113 or larger, the node CIDR will be set to the same
|
||||
// size and this will be rejected later in validation.
|
||||
//
|
||||
// NOTE: Currently, the design allows a maximum of 64K nodes. This algorithm splits
|
||||
// the available bits to maximize the number used for nodes, but still have the node
|
||||
// CIDR be a multiple of eight.
|
||||
//
|
||||
func calcNodeCidrSize(podSubnet string) string {
|
||||
maskSize := "24"
|
||||
if ip, podCidr, err := net.ParseCIDR(podSubnet); err == nil {
|
||||
if ip.To4() == nil {
|
||||
var nodeCidrSize int
|
||||
podNetSize, totalBits := podCidr.Mask.Size()
|
||||
switch {
|
||||
case podNetSize == 112:
|
||||
// Special case, allows 256 nodes, 256 pods/node
|
||||
nodeCidrSize = 120
|
||||
case podNetSize < 112:
|
||||
// Use multiple of 8 for node CIDR, with 512 to 64K nodes
|
||||
nodeCidrSize = totalBits - ((totalBits-podNetSize-1)/8-1)*8
|
||||
default:
|
||||
// Not enough bits, will fail later, when validate
|
||||
nodeCidrSize = podNetSize
|
||||
}
|
||||
maskSize = strconv.Itoa(nodeCidrSize)
|
||||
}
|
||||
}
|
||||
return maskSize
|
||||
}
|
||||
|
||||
// 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{
|
||||
"address": "127.0.0.1",
|
||||
"leader-elect": "true",
|
||||
"kubeconfig": filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.ControllerManagerKubeConfigFileName),
|
||||
"root-ca-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.CACertName),
|
||||
"service-account-private-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPrivateKeyName),
|
||||
"cluster-signing-cert-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.CACertName),
|
||||
"cluster-signing-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.CAKeyName),
|
||||
"use-service-account-credentials": "true",
|
||||
"controllers": "*,bootstrapsigner,tokencleaner",
|
||||
}
|
||||
|
||||
// If using external CA, pass empty string to controller manager instead of ca.key/ca.crt path,
|
||||
// so that the csrsigning controller fails to start
|
||||
if res, _ := certphase.UsingExternalCA(cfg); res {
|
||||
defaultArguments["cluster-signing-key-file"] = ""
|
||||
defaultArguments["cluster-signing-cert-file"] = ""
|
||||
}
|
||||
|
||||
// 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 != "" {
|
||||
maskSize := calcNodeCidrSize(cfg.Networking.PodSubnet)
|
||||
defaultArguments["allocate-node-cidrs"] = "true"
|
||||
defaultArguments["cluster-cidr"] = cfg.Networking.PodSubnet
|
||||
defaultArguments["node-cidr-mask-size"] = maskSize
|
||||
}
|
||||
|
||||
command := []string{"kube-controller-manager"}
|
||||
command = append(command, kubeadmutil.BuildArgumentListFromMap(defaultArguments, cfg.ControllerManagerExtraArgs)...)
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
// getSchedulerCommand builds the right scheduler command from the given config object and version
|
||||
func getSchedulerCommand(cfg *kubeadmapi.MasterConfiguration) []string {
|
||||
defaultArguments := map[string]string{
|
||||
"address": "127.0.0.1",
|
||||
"leader-elect": "true",
|
||||
"kubeconfig": filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.SchedulerKubeConfigFileName),
|
||||
}
|
||||
|
||||
command := []string{"kube-scheduler"}
|
||||
command = append(command, kubeadmutil.BuildArgumentListFromMap(defaultArguments, cfg.SchedulerExtraArgs)...)
|
||||
return command
|
||||
}
|
||||
|
||||
// getProxyEnvVars builds a list of environment variables to use in the control plane containers in order to use the right proxy
|
||||
func getProxyEnvVars() []v1.EnvVar {
|
||||
envs := []v1.EnvVar{}
|
||||
for _, env := range os.Environ() {
|
||||
pos := strings.Index(env, "=")
|
||||
if pos == -1 {
|
||||
// malformed environment variable, skip it.
|
||||
continue
|
||||
}
|
||||
name := env[:pos]
|
||||
value := env[pos+1:]
|
||||
if strings.HasSuffix(strings.ToLower(name), "_proxy") && value != "" {
|
||||
envVar := v1.EnvVar{Name: name, Value: value}
|
||||
envs = append(envs, envVar)
|
||||
}
|
||||
}
|
||||
return envs
|
||||
}
|
1015
vendor/k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane/manifests_test.go
generated
vendored
Normal file
1015
vendor/k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane/manifests_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
235
vendor/k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane/volumes.go
generated
vendored
Normal file
235
vendor/k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane/volumes.go
generated
vendored
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
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 controlplane
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/features"
|
||||
staticpodutil "k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod"
|
||||
)
|
||||
|
||||
const (
|
||||
caCertsVolumeName = "ca-certs"
|
||||
caCertsVolumePath = "/etc/ssl/certs"
|
||||
flexvolumeDirVolumeName = "flexvolume-dir"
|
||||
flexvolumeDirVolumePath = "/usr/libexec/kubernetes/kubelet-plugins/volume/exec"
|
||||
)
|
||||
|
||||
// caCertsExtraVolumePaths specifies the paths that can be conditionally mounted into the apiserver and controller-manager containers
|
||||
// as /etc/ssl/certs might be or contain a symlink to them. It's a variable since it may be changed in unit testing. This var MUST
|
||||
// NOT be changed in normal codepaths during runtime.
|
||||
var caCertsExtraVolumePaths = []string{"/etc/pki", "/usr/share/ca-certificates", "/usr/local/share/ca-certificates", "/etc/ca-certificates"}
|
||||
|
||||
// getHostPathVolumesForTheControlPlane gets the required hostPath volumes and mounts for the control plane
|
||||
func getHostPathVolumesForTheControlPlane(cfg *kubeadmapi.MasterConfiguration) controlPlaneHostPathMounts {
|
||||
hostPathDirectoryOrCreate := v1.HostPathDirectoryOrCreate
|
||||
hostPathFileOrCreate := v1.HostPathFileOrCreate
|
||||
hostPathFile := v1.HostPathFile
|
||||
mounts := newControlPlaneHostPathMounts()
|
||||
|
||||
// HostPath volumes for the API Server
|
||||
// Read-only mount for the certificates directory
|
||||
// TODO: Always mount the K8s Certificates directory to a static path inside of the container
|
||||
mounts.NewHostPathMount(kubeadmconstants.KubeAPIServer, kubeadmconstants.KubeCertificatesVolumeName, cfg.CertificatesDir, cfg.CertificatesDir, true, &hostPathDirectoryOrCreate)
|
||||
// Read-only mount for the ca certs (/etc/ssl/certs) directory
|
||||
mounts.NewHostPathMount(kubeadmconstants.KubeAPIServer, caCertsVolumeName, caCertsVolumePath, caCertsVolumePath, true, &hostPathDirectoryOrCreate)
|
||||
if features.Enabled(cfg.FeatureGates, features.Auditing) {
|
||||
// Read-only mount for the audit policy file.
|
||||
mounts.NewHostPathMount(kubeadmconstants.KubeAPIServer, kubeadmconstants.KubeAuditPolicyVolumeName, cfg.AuditPolicyConfiguration.Path, kubeadmconstants.GetStaticPodAuditPolicyFile(), true, &hostPathFile)
|
||||
// Write mount for the audit logs.
|
||||
mounts.NewHostPathMount(kubeadmconstants.KubeAPIServer, kubeadmconstants.KubeAuditPolicyLogVolumeName, cfg.AuditPolicyConfiguration.LogDir, kubeadmconstants.StaticPodAuditPolicyLogDir, false, &hostPathDirectoryOrCreate)
|
||||
}
|
||||
// If external etcd is specified, mount the directories needed for accessing the CA/serving certs and the private key
|
||||
if cfg.Etcd.External != nil {
|
||||
etcdVols, etcdVolMounts := getEtcdCertVolumes(cfg.Etcd.External, cfg.CertificatesDir)
|
||||
mounts.AddHostPathMounts(kubeadmconstants.KubeAPIServer, etcdVols, etcdVolMounts)
|
||||
}
|
||||
|
||||
// HostPath volumes for the controller manager
|
||||
// Read-only mount for the certificates directory
|
||||
// TODO: Always mount the K8s Certificates directory to a static path inside of the container
|
||||
mounts.NewHostPathMount(kubeadmconstants.KubeControllerManager, kubeadmconstants.KubeCertificatesVolumeName, cfg.CertificatesDir, cfg.CertificatesDir, true, &hostPathDirectoryOrCreate)
|
||||
// Read-only mount for the ca certs (/etc/ssl/certs) directory
|
||||
mounts.NewHostPathMount(kubeadmconstants.KubeControllerManager, caCertsVolumeName, caCertsVolumePath, caCertsVolumePath, true, &hostPathDirectoryOrCreate)
|
||||
// Read-only mount for the controller manager kubeconfig file
|
||||
controllerManagerKubeConfigFile := filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.ControllerManagerKubeConfigFileName)
|
||||
mounts.NewHostPathMount(kubeadmconstants.KubeControllerManager, kubeadmconstants.KubeConfigVolumeName, controllerManagerKubeConfigFile, controllerManagerKubeConfigFile, true, &hostPathFileOrCreate)
|
||||
// Mount for the flexvolume directory (/usr/libexec/kubernetes/kubelet-plugins/volume/exec) directory
|
||||
// Flexvolume dir must NOT be readonly as it is used for third-party plugins to integrate with their storage backends via unix domain socket.
|
||||
if stat, err := os.Stat(flexvolumeDirVolumePath); err == nil && stat.IsDir() {
|
||||
mounts.NewHostPathMount(kubeadmconstants.KubeControllerManager, flexvolumeDirVolumeName, flexvolumeDirVolumePath, flexvolumeDirVolumePath, false, &hostPathDirectoryOrCreate)
|
||||
}
|
||||
|
||||
// HostPath volumes for the scheduler
|
||||
// Read-only mount for the scheduler kubeconfig file
|
||||
schedulerKubeConfigFile := filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.SchedulerKubeConfigFileName)
|
||||
mounts.NewHostPathMount(kubeadmconstants.KubeScheduler, kubeadmconstants.KubeConfigVolumeName, schedulerKubeConfigFile, schedulerKubeConfigFile, true, &hostPathFileOrCreate)
|
||||
|
||||
// On some systems were we host-mount /etc/ssl/certs, it is also required to mount additional directories.
|
||||
// This is needed due to symlinks pointing from files in /etc/ssl/certs to these directories.
|
||||
for _, caCertsExtraVolumePath := range caCertsExtraVolumePaths {
|
||||
if isExtraVolumeMountNeeded(caCertsExtraVolumePath) {
|
||||
caCertsExtraVolumeName := strings.Replace(caCertsExtraVolumePath, "/", "-", -1)[1:]
|
||||
mounts.NewHostPathMount(kubeadmconstants.KubeAPIServer, caCertsExtraVolumeName, caCertsExtraVolumePath, caCertsExtraVolumePath, true, &hostPathDirectoryOrCreate)
|
||||
mounts.NewHostPathMount(kubeadmconstants.KubeControllerManager, caCertsExtraVolumeName, caCertsExtraVolumePath, caCertsExtraVolumePath, true, &hostPathDirectoryOrCreate)
|
||||
}
|
||||
}
|
||||
|
||||
// Merge user defined mounts and ensure unique volume and volume mount
|
||||
// names
|
||||
mounts.AddExtraHostPathMounts(kubeadmconstants.KubeAPIServer, cfg.APIServerExtraVolumes)
|
||||
mounts.AddExtraHostPathMounts(kubeadmconstants.KubeControllerManager, cfg.ControllerManagerExtraVolumes)
|
||||
mounts.AddExtraHostPathMounts(kubeadmconstants.KubeScheduler, cfg.SchedulerExtraVolumes)
|
||||
|
||||
return mounts
|
||||
}
|
||||
|
||||
// controlPlaneHostPathMounts is a helper struct for handling all the control plane's hostPath mounts in an easy way
|
||||
type controlPlaneHostPathMounts struct {
|
||||
// volumes is a nested map that forces a unique volumes. The outer map's
|
||||
// keys are a string that should specify the target component to add the
|
||||
// volume to. The values (inner map) of the outer map are maps with string
|
||||
// keys and v1.Volume values. The inner map's key should specify the volume
|
||||
// name.
|
||||
volumes map[string]map[string]v1.Volume
|
||||
// volumeMounts is a nested map that forces a unique volume mounts. The
|
||||
// outer map's keys are a string that should specify the target component
|
||||
// to add the volume mount to. The values (inner map) of the outer map are
|
||||
// maps with string keys and v1.VolumeMount values. The inner map's key
|
||||
// should specify the volume mount name.
|
||||
volumeMounts map[string]map[string]v1.VolumeMount
|
||||
}
|
||||
|
||||
func newControlPlaneHostPathMounts() controlPlaneHostPathMounts {
|
||||
return controlPlaneHostPathMounts{
|
||||
volumes: map[string]map[string]v1.Volume{},
|
||||
volumeMounts: map[string]map[string]v1.VolumeMount{},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *controlPlaneHostPathMounts) NewHostPathMount(component, mountName, hostPath, containerPath string, readOnly bool, hostPathType *v1.HostPathType) {
|
||||
vol := staticpodutil.NewVolume(mountName, hostPath, hostPathType)
|
||||
c.addComponentVolume(component, vol)
|
||||
volMount := staticpodutil.NewVolumeMount(mountName, containerPath, readOnly)
|
||||
c.addComponentVolumeMount(component, volMount)
|
||||
}
|
||||
|
||||
func (c *controlPlaneHostPathMounts) AddHostPathMounts(component string, vols []v1.Volume, volMounts []v1.VolumeMount) {
|
||||
for _, v := range vols {
|
||||
c.addComponentVolume(component, v)
|
||||
}
|
||||
for _, v := range volMounts {
|
||||
c.addComponentVolumeMount(component, v)
|
||||
}
|
||||
}
|
||||
|
||||
// AddExtraHostPathMounts adds host path mounts and overwrites the default
|
||||
// paths in the case that a user specifies the same volume/volume mount name.
|
||||
func (c *controlPlaneHostPathMounts) AddExtraHostPathMounts(component string, extraVols []kubeadmapi.HostPathMount) {
|
||||
for _, extraVol := range extraVols {
|
||||
fmt.Printf("[controlplane] Adding extra host path mount %q to %q\n", extraVol.Name, component)
|
||||
hostPathType := extraVol.PathType
|
||||
c.NewHostPathMount(component, extraVol.Name, extraVol.HostPath, extraVol.MountPath, !extraVol.Writable, &hostPathType)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *controlPlaneHostPathMounts) GetVolumes(component string) map[string]v1.Volume {
|
||||
return c.volumes[component]
|
||||
}
|
||||
|
||||
func (c *controlPlaneHostPathMounts) GetVolumeMounts(component string) map[string]v1.VolumeMount {
|
||||
return c.volumeMounts[component]
|
||||
}
|
||||
|
||||
func (c *controlPlaneHostPathMounts) addComponentVolume(component string, vol v1.Volume) {
|
||||
if _, ok := c.volumes[component]; !ok {
|
||||
c.volumes[component] = map[string]v1.Volume{}
|
||||
}
|
||||
c.volumes[component][vol.Name] = vol
|
||||
}
|
||||
|
||||
func (c *controlPlaneHostPathMounts) addComponentVolumeMount(component string, volMount v1.VolumeMount) {
|
||||
if _, ok := c.volumeMounts[component]; !ok {
|
||||
c.volumeMounts[component] = map[string]v1.VolumeMount{}
|
||||
}
|
||||
c.volumeMounts[component][volMount.Name] = volMount
|
||||
}
|
||||
|
||||
// getEtcdCertVolumes returns the volumes/volumemounts needed for talking to an external etcd cluster
|
||||
func getEtcdCertVolumes(etcdCfg *kubeadmapi.ExternalEtcd, k8sCertificatesDir string) ([]v1.Volume, []v1.VolumeMount) {
|
||||
certPaths := []string{etcdCfg.CAFile, etcdCfg.CertFile, etcdCfg.KeyFile}
|
||||
certDirs := sets.NewString()
|
||||
for _, certPath := range certPaths {
|
||||
certDir := filepath.Dir(certPath)
|
||||
// Ignore ".", which is the result of passing an empty path.
|
||||
// Also ignore the cert directories that already may be mounted; /etc/ssl/certs, /etc/pki or Kubernetes CertificatesDir
|
||||
// If the etcd certs are in there, it's okay, we don't have to do anything
|
||||
extraVolumePath := false
|
||||
for _, caCertsExtraVolumePath := range caCertsExtraVolumePaths {
|
||||
if strings.HasPrefix(certDir, caCertsExtraVolumePath) {
|
||||
extraVolumePath = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if certDir == "." || extraVolumePath || strings.HasPrefix(certDir, caCertsVolumePath) || strings.HasPrefix(certDir, k8sCertificatesDir) {
|
||||
continue
|
||||
}
|
||||
// Filter out any existing hostpath mounts in the list that contains a subset of the path
|
||||
alreadyExists := false
|
||||
for _, existingCertDir := range certDirs.List() {
|
||||
// If the current directory is a parent of an existing one, remove the already existing one
|
||||
if strings.HasPrefix(existingCertDir, certDir) {
|
||||
certDirs.Delete(existingCertDir)
|
||||
} else if strings.HasPrefix(certDir, existingCertDir) {
|
||||
// If an existing directory is a parent of the current one, don't add the current one
|
||||
alreadyExists = true
|
||||
}
|
||||
}
|
||||
if alreadyExists {
|
||||
continue
|
||||
}
|
||||
certDirs.Insert(certDir)
|
||||
}
|
||||
|
||||
volumes := []v1.Volume{}
|
||||
volumeMounts := []v1.VolumeMount{}
|
||||
pathType := v1.HostPathDirectoryOrCreate
|
||||
for i, certDir := range certDirs.List() {
|
||||
name := fmt.Sprintf("etcd-certs-%d", i)
|
||||
volumes = append(volumes, staticpodutil.NewVolume(name, certDir, &pathType))
|
||||
volumeMounts = append(volumeMounts, staticpodutil.NewVolumeMount(name, certDir, true))
|
||||
}
|
||||
return volumes, volumeMounts
|
||||
}
|
||||
|
||||
// isExtraVolumeMountNeeded specifies whether /etc/pki should be host-mounted into the containers
|
||||
// On some systems were we host-mount /etc/ssl/certs, it is also required to mount /etc/pki. This is needed
|
||||
// due to symlinks pointing from files in /etc/ssl/certs into /etc/pki/
|
||||
func isExtraVolumeMountNeeded(caCertsExtraVolumePath string) bool {
|
||||
if _, err := os.Stat(caCertsExtraVolumePath); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
675
vendor/k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane/volumes_test.go
generated
vendored
Normal file
675
vendor/k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane/volumes_test.go
generated
vendored
Normal file
@@ -0,0 +1,675 @@
|
||||
/*
|
||||
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 controlplane
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"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/features"
|
||||
)
|
||||
|
||||
func TestGetEtcdCertVolumes(t *testing.T) {
|
||||
hostPathDirectoryOrCreate := v1.HostPathDirectoryOrCreate
|
||||
k8sCertifcatesDir := "/etc/kubernetes/pki"
|
||||
var tests = []struct {
|
||||
ca, cert, key string
|
||||
vol []v1.Volume
|
||||
volMount []v1.VolumeMount
|
||||
}{
|
||||
{
|
||||
// Should ignore files in /etc/ssl/certs
|
||||
ca: "/etc/ssl/certs/my-etcd-ca.crt",
|
||||
cert: "/etc/ssl/certs/my-etcd.crt",
|
||||
key: "/etc/ssl/certs/my-etcd.key",
|
||||
vol: []v1.Volume{},
|
||||
volMount: []v1.VolumeMount{},
|
||||
},
|
||||
{
|
||||
// Should ignore files in subdirs of /etc/ssl/certs
|
||||
ca: "/etc/ssl/certs/etcd/my-etcd-ca.crt",
|
||||
cert: "/etc/ssl/certs/etcd/my-etcd.crt",
|
||||
key: "/etc/ssl/certs/etcd/my-etcd.key",
|
||||
vol: []v1.Volume{},
|
||||
volMount: []v1.VolumeMount{},
|
||||
},
|
||||
{
|
||||
// Should ignore files in /etc/pki
|
||||
ca: "/etc/pki/my-etcd-ca.crt",
|
||||
cert: "/etc/pki/my-etcd.crt",
|
||||
key: "/etc/pki/my-etcd.key",
|
||||
vol: []v1.Volume{},
|
||||
volMount: []v1.VolumeMount{},
|
||||
},
|
||||
{
|
||||
// Should ignore files in Kubernetes PKI directory (and subdirs)
|
||||
ca: k8sCertifcatesDir + "/ca/my-etcd-ca.crt",
|
||||
cert: k8sCertifcatesDir + "/my-etcd.crt",
|
||||
key: k8sCertifcatesDir + "/my-etcd.key",
|
||||
vol: []v1.Volume{},
|
||||
volMount: []v1.VolumeMount{},
|
||||
},
|
||||
{
|
||||
// All in the same dir
|
||||
ca: "/var/lib/certs/etcd/my-etcd-ca.crt",
|
||||
cert: "/var/lib/certs/etcd/my-etcd.crt",
|
||||
key: "/var/lib/certs/etcd/my-etcd.key",
|
||||
vol: []v1.Volume{
|
||||
{
|
||||
Name: "etcd-certs-0",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/var/lib/certs/etcd",
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
volMount: []v1.VolumeMount{
|
||||
{
|
||||
Name: "etcd-certs-0",
|
||||
MountPath: "/var/lib/certs/etcd",
|
||||
ReadOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// One file + two files in separate dirs
|
||||
ca: "/etc/certs/etcd/my-etcd-ca.crt",
|
||||
cert: "/var/lib/certs/etcd/my-etcd.crt",
|
||||
key: "/var/lib/certs/etcd/my-etcd.key",
|
||||
vol: []v1.Volume{
|
||||
{
|
||||
Name: "etcd-certs-0",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/etc/certs/etcd",
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "etcd-certs-1",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/var/lib/certs/etcd",
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
volMount: []v1.VolumeMount{
|
||||
{
|
||||
Name: "etcd-certs-0",
|
||||
MountPath: "/etc/certs/etcd",
|
||||
ReadOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "etcd-certs-1",
|
||||
MountPath: "/var/lib/certs/etcd",
|
||||
ReadOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// All three files in different directories
|
||||
ca: "/etc/certs/etcd/my-etcd-ca.crt",
|
||||
cert: "/var/lib/certs/etcd/my-etcd.crt",
|
||||
key: "/var/lib/certs/private/my-etcd.key",
|
||||
vol: []v1.Volume{
|
||||
{
|
||||
Name: "etcd-certs-0",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/etc/certs/etcd",
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "etcd-certs-1",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/var/lib/certs/etcd",
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "etcd-certs-2",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/var/lib/certs/private",
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
volMount: []v1.VolumeMount{
|
||||
{
|
||||
Name: "etcd-certs-0",
|
||||
MountPath: "/etc/certs/etcd",
|
||||
ReadOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "etcd-certs-1",
|
||||
MountPath: "/var/lib/certs/etcd",
|
||||
ReadOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "etcd-certs-2",
|
||||
MountPath: "/var/lib/certs/private",
|
||||
ReadOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// The most top-level dir should be used
|
||||
ca: "/etc/certs/etcd/my-etcd-ca.crt",
|
||||
cert: "/etc/certs/etcd/serving/my-etcd.crt",
|
||||
key: "/etc/certs/etcd/serving/my-etcd.key",
|
||||
vol: []v1.Volume{
|
||||
{
|
||||
Name: "etcd-certs-0",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/etc/certs/etcd",
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
volMount: []v1.VolumeMount{
|
||||
{
|
||||
Name: "etcd-certs-0",
|
||||
MountPath: "/etc/certs/etcd",
|
||||
ReadOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// The most top-level dir should be used, regardless of order
|
||||
ca: "/etc/certs/etcd/ca/my-etcd-ca.crt",
|
||||
cert: "/etc/certs/etcd/my-etcd.crt",
|
||||
key: "/etc/certs/etcd/my-etcd.key",
|
||||
vol: []v1.Volume{
|
||||
{
|
||||
Name: "etcd-certs-0",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/etc/certs/etcd",
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
volMount: []v1.VolumeMount{
|
||||
{
|
||||
Name: "etcd-certs-0",
|
||||
MountPath: "/etc/certs/etcd",
|
||||
ReadOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
actualVol, actualVolMount := getEtcdCertVolumes(&kubeadmapi.ExternalEtcd{
|
||||
CAFile: rt.ca,
|
||||
CertFile: rt.cert,
|
||||
KeyFile: rt.key,
|
||||
}, k8sCertifcatesDir)
|
||||
if !reflect.DeepEqual(actualVol, rt.vol) {
|
||||
t.Errorf(
|
||||
"failed getEtcdCertVolumes:\n\texpected: %v\n\t actual: %v",
|
||||
rt.vol,
|
||||
actualVol,
|
||||
)
|
||||
}
|
||||
if !reflect.DeepEqual(actualVolMount, rt.volMount) {
|
||||
t.Errorf(
|
||||
"failed getEtcdCertVolumes:\n\texpected: %v\n\t actual: %v",
|
||||
rt.volMount,
|
||||
actualVolMount,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetHostPathVolumesForTheControlPlane(t *testing.T) {
|
||||
hostPathDirectoryOrCreate := v1.HostPathDirectoryOrCreate
|
||||
hostPathFileOrCreate := v1.HostPathFileOrCreate
|
||||
hostPathFile := v1.HostPathFile
|
||||
volMap := make(map[string]map[string]v1.Volume)
|
||||
volMap[kubeadmconstants.KubeAPIServer] = map[string]v1.Volume{}
|
||||
volMap[kubeadmconstants.KubeAPIServer]["k8s-certs"] = v1.Volume{
|
||||
Name: "k8s-certs",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: testCertsDir,
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
}
|
||||
volMap[kubeadmconstants.KubeAPIServer]["ca-certs"] = v1.Volume{
|
||||
Name: "ca-certs",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/etc/ssl/certs",
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
}
|
||||
volMap[kubeadmconstants.KubeAPIServer]["audit"] = v1.Volume{
|
||||
Name: "audit",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/foo/bar/baz.yaml",
|
||||
Type: &hostPathFile,
|
||||
},
|
||||
},
|
||||
}
|
||||
volMap[kubeadmconstants.KubeAPIServer]["audit-log"] = v1.Volume{
|
||||
Name: "audit-log",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/bar/foo",
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
}
|
||||
volMap[kubeadmconstants.KubeControllerManager] = map[string]v1.Volume{}
|
||||
volMap[kubeadmconstants.KubeControllerManager]["k8s-certs"] = v1.Volume{
|
||||
Name: "k8s-certs",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: testCertsDir,
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
}
|
||||
volMap[kubeadmconstants.KubeControllerManager]["ca-certs"] = v1.Volume{
|
||||
Name: "ca-certs",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/etc/ssl/certs",
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
}
|
||||
volMap[kubeadmconstants.KubeControllerManager]["kubeconfig"] = v1.Volume{
|
||||
Name: "kubeconfig",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/etc/kubernetes/controller-manager.conf",
|
||||
Type: &hostPathFileOrCreate,
|
||||
},
|
||||
},
|
||||
}
|
||||
volMap[kubeadmconstants.KubeScheduler] = map[string]v1.Volume{}
|
||||
volMap[kubeadmconstants.KubeScheduler]["kubeconfig"] = v1.Volume{
|
||||
Name: "kubeconfig",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/etc/kubernetes/scheduler.conf",
|
||||
Type: &hostPathFileOrCreate,
|
||||
},
|
||||
},
|
||||
}
|
||||
volMountMap := make(map[string]map[string]v1.VolumeMount)
|
||||
volMountMap[kubeadmconstants.KubeAPIServer] = map[string]v1.VolumeMount{}
|
||||
volMountMap[kubeadmconstants.KubeAPIServer]["k8s-certs"] = v1.VolumeMount{
|
||||
Name: "k8s-certs",
|
||||
MountPath: testCertsDir,
|
||||
ReadOnly: true,
|
||||
}
|
||||
volMountMap[kubeadmconstants.KubeAPIServer]["ca-certs"] = v1.VolumeMount{
|
||||
Name: "ca-certs",
|
||||
MountPath: "/etc/ssl/certs",
|
||||
ReadOnly: true,
|
||||
}
|
||||
volMountMap[kubeadmconstants.KubeAPIServer]["audit"] = v1.VolumeMount{
|
||||
Name: "audit",
|
||||
MountPath: "/etc/kubernetes/audit/audit.yaml",
|
||||
ReadOnly: true,
|
||||
}
|
||||
volMountMap[kubeadmconstants.KubeAPIServer]["audit-log"] = v1.VolumeMount{
|
||||
Name: "audit-log",
|
||||
MountPath: "/var/log/kubernetes/audit",
|
||||
ReadOnly: false,
|
||||
}
|
||||
volMountMap[kubeadmconstants.KubeControllerManager] = map[string]v1.VolumeMount{}
|
||||
volMountMap[kubeadmconstants.KubeControllerManager]["k8s-certs"] = v1.VolumeMount{
|
||||
Name: "k8s-certs",
|
||||
MountPath: testCertsDir,
|
||||
ReadOnly: true,
|
||||
}
|
||||
volMountMap[kubeadmconstants.KubeControllerManager]["ca-certs"] = v1.VolumeMount{
|
||||
Name: "ca-certs",
|
||||
MountPath: "/etc/ssl/certs",
|
||||
ReadOnly: true,
|
||||
}
|
||||
volMountMap[kubeadmconstants.KubeControllerManager]["kubeconfig"] = v1.VolumeMount{
|
||||
Name: "kubeconfig",
|
||||
MountPath: "/etc/kubernetes/controller-manager.conf",
|
||||
ReadOnly: true,
|
||||
}
|
||||
volMountMap[kubeadmconstants.KubeScheduler] = map[string]v1.VolumeMount{}
|
||||
volMountMap[kubeadmconstants.KubeScheduler]["kubeconfig"] = v1.VolumeMount{
|
||||
Name: "kubeconfig",
|
||||
MountPath: "/etc/kubernetes/scheduler.conf",
|
||||
ReadOnly: true,
|
||||
}
|
||||
|
||||
volMap2 := make(map[string]map[string]v1.Volume)
|
||||
volMap2[kubeadmconstants.KubeAPIServer] = map[string]v1.Volume{}
|
||||
volMap2[kubeadmconstants.KubeAPIServer]["k8s-certs"] = v1.Volume{
|
||||
Name: "k8s-certs",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: testCertsDir,
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
}
|
||||
volMap2[kubeadmconstants.KubeAPIServer]["ca-certs"] = v1.Volume{
|
||||
Name: "ca-certs",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/etc/ssl/certs",
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
}
|
||||
volMap2[kubeadmconstants.KubeAPIServer]["etcd-certs-0"] = v1.Volume{
|
||||
Name: "etcd-certs-0",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/etc/certs/etcd",
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
}
|
||||
volMap2[kubeadmconstants.KubeAPIServer]["etcd-certs-1"] = v1.Volume{
|
||||
Name: "etcd-certs-1",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/var/lib/etcd/certs",
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
}
|
||||
volMap2[kubeadmconstants.KubeControllerManager] = map[string]v1.Volume{}
|
||||
volMap2[kubeadmconstants.KubeControllerManager]["k8s-certs"] = v1.Volume{
|
||||
Name: "k8s-certs",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: testCertsDir,
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
}
|
||||
volMap2[kubeadmconstants.KubeControllerManager]["ca-certs"] = v1.Volume{
|
||||
Name: "ca-certs",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/etc/ssl/certs",
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
}
|
||||
volMap2[kubeadmconstants.KubeControllerManager]["kubeconfig"] = v1.Volume{
|
||||
Name: "kubeconfig",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/etc/kubernetes/controller-manager.conf",
|
||||
Type: &hostPathFileOrCreate,
|
||||
},
|
||||
},
|
||||
}
|
||||
volMap2[kubeadmconstants.KubeScheduler] = map[string]v1.Volume{}
|
||||
volMap2[kubeadmconstants.KubeScheduler]["kubeconfig"] = v1.Volume{
|
||||
Name: "kubeconfig",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/etc/kubernetes/scheduler.conf",
|
||||
Type: &hostPathFileOrCreate,
|
||||
},
|
||||
},
|
||||
}
|
||||
volMountMap2 := make(map[string]map[string]v1.VolumeMount)
|
||||
volMountMap2[kubeadmconstants.KubeAPIServer] = map[string]v1.VolumeMount{}
|
||||
volMountMap2[kubeadmconstants.KubeAPIServer]["k8s-certs"] = v1.VolumeMount{
|
||||
Name: "k8s-certs",
|
||||
MountPath: testCertsDir,
|
||||
ReadOnly: true,
|
||||
}
|
||||
volMountMap2[kubeadmconstants.KubeAPIServer]["ca-certs"] = v1.VolumeMount{
|
||||
Name: "ca-certs",
|
||||
MountPath: "/etc/ssl/certs",
|
||||
ReadOnly: true,
|
||||
}
|
||||
volMountMap2[kubeadmconstants.KubeAPIServer]["etcd-certs-0"] = v1.VolumeMount{
|
||||
Name: "etcd-certs-0",
|
||||
MountPath: "/etc/certs/etcd",
|
||||
ReadOnly: true,
|
||||
}
|
||||
volMountMap2[kubeadmconstants.KubeAPIServer]["etcd-certs-1"] = v1.VolumeMount{
|
||||
Name: "etcd-certs-1",
|
||||
MountPath: "/var/lib/etcd/certs",
|
||||
ReadOnly: true,
|
||||
}
|
||||
volMountMap2[kubeadmconstants.KubeControllerManager] = map[string]v1.VolumeMount{}
|
||||
volMountMap2[kubeadmconstants.KubeControllerManager]["k8s-certs"] = v1.VolumeMount{
|
||||
Name: "k8s-certs",
|
||||
MountPath: testCertsDir,
|
||||
ReadOnly: true,
|
||||
}
|
||||
volMountMap2[kubeadmconstants.KubeControllerManager]["ca-certs"] = v1.VolumeMount{
|
||||
Name: "ca-certs",
|
||||
MountPath: "/etc/ssl/certs",
|
||||
ReadOnly: true,
|
||||
}
|
||||
volMountMap2[kubeadmconstants.KubeControllerManager]["kubeconfig"] = v1.VolumeMount{
|
||||
Name: "kubeconfig",
|
||||
MountPath: "/etc/kubernetes/controller-manager.conf",
|
||||
ReadOnly: true,
|
||||
}
|
||||
volMountMap2[kubeadmconstants.KubeScheduler] = map[string]v1.VolumeMount{}
|
||||
volMountMap2[kubeadmconstants.KubeScheduler]["kubeconfig"] = v1.VolumeMount{
|
||||
Name: "kubeconfig",
|
||||
MountPath: "/etc/kubernetes/scheduler.conf",
|
||||
ReadOnly: true,
|
||||
}
|
||||
var tests = []struct {
|
||||
cfg *kubeadmapi.MasterConfiguration
|
||||
vol map[string]map[string]v1.Volume
|
||||
volMount map[string]map[string]v1.VolumeMount
|
||||
}{
|
||||
{
|
||||
// Should ignore files in /etc/ssl/certs
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
CertificatesDir: testCertsDir,
|
||||
Etcd: kubeadmapi.Etcd{},
|
||||
FeatureGates: map[string]bool{features.Auditing: true},
|
||||
AuditPolicyConfiguration: kubeadmapi.AuditPolicyConfiguration{
|
||||
Path: "/foo/bar/baz.yaml",
|
||||
LogDir: "/bar/foo",
|
||||
},
|
||||
},
|
||||
vol: volMap,
|
||||
volMount: volMountMap,
|
||||
},
|
||||
{
|
||||
// Should ignore files in /etc/ssl/certs and in CertificatesDir
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
CertificatesDir: testCertsDir,
|
||||
Etcd: kubeadmapi.Etcd{
|
||||
External: &kubeadmapi.ExternalEtcd{
|
||||
Endpoints: []string{"foo"},
|
||||
CAFile: "/etc/certs/etcd/my-etcd-ca.crt",
|
||||
CertFile: testCertsDir + "/etcd/my-etcd.crt",
|
||||
KeyFile: "/var/lib/etcd/certs/my-etcd.key",
|
||||
},
|
||||
},
|
||||
},
|
||||
vol: volMap2,
|
||||
volMount: volMountMap2,
|
||||
},
|
||||
}
|
||||
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
// set up tmp caCertsExtraVolumePaths for testing
|
||||
caCertsExtraVolumePaths = []string{fmt.Sprintf("%s/etc/pki", tmpdir), fmt.Sprintf("%s/usr/share/ca-certificates", tmpdir)}
|
||||
defer func() { caCertsExtraVolumePaths = []string{"/etc/pki", "/usr/share/ca-certificates"} }()
|
||||
|
||||
for _, rt := range tests {
|
||||
mounts := getHostPathVolumesForTheControlPlane(rt.cfg)
|
||||
|
||||
// Avoid unit test errors when the flexvolume is mounted
|
||||
if _, ok := mounts.volumes[kubeadmconstants.KubeControllerManager][flexvolumeDirVolumeName]; ok {
|
||||
delete(mounts.volumes[kubeadmconstants.KubeControllerManager], flexvolumeDirVolumeName)
|
||||
}
|
||||
if _, ok := mounts.volumeMounts[kubeadmconstants.KubeControllerManager][flexvolumeDirVolumeName]; ok {
|
||||
delete(mounts.volumeMounts[kubeadmconstants.KubeControllerManager], flexvolumeDirVolumeName)
|
||||
}
|
||||
if !reflect.DeepEqual(mounts.volumes, rt.vol) {
|
||||
t.Errorf(
|
||||
"failed getHostPathVolumesForTheControlPlane:\n\texpected: %v\n\t actual: %v",
|
||||
rt.vol,
|
||||
mounts.volumes,
|
||||
)
|
||||
}
|
||||
if !reflect.DeepEqual(mounts.volumeMounts, rt.volMount) {
|
||||
t.Errorf(
|
||||
"failed getHostPathVolumesForTheControlPlane:\n\texpected: %v\n\t actual: %v",
|
||||
rt.volMount,
|
||||
mounts.volumeMounts,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddExtraHostPathMounts(t *testing.T) {
|
||||
mounts := newControlPlaneHostPathMounts()
|
||||
hostPathDirectoryOrCreate := v1.HostPathDirectoryOrCreate
|
||||
hostPathFileOrCreate := v1.HostPathFileOrCreate
|
||||
vols := []v1.Volume{
|
||||
{
|
||||
Name: "foo",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/tmp/foo",
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "bar",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/tmp/bar",
|
||||
Type: &hostPathFileOrCreate,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
volMounts := []v1.VolumeMount{
|
||||
{
|
||||
Name: "foo",
|
||||
MountPath: "/tmp/foo",
|
||||
ReadOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "bar",
|
||||
MountPath: "/tmp/bar",
|
||||
ReadOnly: true,
|
||||
},
|
||||
}
|
||||
mounts.AddHostPathMounts("component", vols, volMounts)
|
||||
hostPathMounts := []kubeadmapi.HostPathMount{
|
||||
{
|
||||
Name: "foo-0",
|
||||
HostPath: "/tmp/qux-0",
|
||||
MountPath: "/tmp/qux-0",
|
||||
Writable: false,
|
||||
PathType: v1.HostPathFile,
|
||||
},
|
||||
{
|
||||
Name: "bar-0",
|
||||
HostPath: "/tmp/asd-0",
|
||||
MountPath: "/tmp/asd-0",
|
||||
Writable: true,
|
||||
PathType: v1.HostPathDirectory,
|
||||
},
|
||||
{
|
||||
Name: "foo-1",
|
||||
HostPath: "/tmp/qux-1",
|
||||
MountPath: "/tmp/qux-1",
|
||||
Writable: false,
|
||||
PathType: v1.HostPathFileOrCreate,
|
||||
},
|
||||
{
|
||||
Name: "bar-1",
|
||||
HostPath: "/tmp/asd-1",
|
||||
MountPath: "/tmp/asd-1",
|
||||
Writable: true,
|
||||
PathType: v1.HostPathDirectoryOrCreate,
|
||||
},
|
||||
}
|
||||
mounts.AddExtraHostPathMounts("component", hostPathMounts)
|
||||
for _, hostMount := range hostPathMounts {
|
||||
volumeName := hostMount.Name
|
||||
if _, ok := mounts.volumes["component"][volumeName]; !ok {
|
||||
t.Errorf("Expected to find volume %q", volumeName)
|
||||
}
|
||||
vol := mounts.volumes["component"][volumeName]
|
||||
if vol.Name != volumeName {
|
||||
t.Errorf("Expected volume name %q", volumeName)
|
||||
}
|
||||
if vol.HostPath.Path != hostMount.HostPath {
|
||||
t.Errorf("Expected host path %q", hostMount.HostPath)
|
||||
}
|
||||
if _, ok := mounts.volumeMounts["component"][volumeName]; !ok {
|
||||
t.Errorf("Expected to find volume mount %q", volumeName)
|
||||
}
|
||||
if *vol.HostPath.Type != v1.HostPathType(hostMount.PathType) {
|
||||
t.Errorf("Expected to host path type %q", hostMount.PathType)
|
||||
}
|
||||
volMount, _ := mounts.volumeMounts["component"][volumeName]
|
||||
if volMount.Name != volumeName {
|
||||
t.Errorf("Expected volume mount name %q", volumeName)
|
||||
}
|
||||
if volMount.MountPath != hostMount.MountPath {
|
||||
t.Errorf("Expected container path %q", hostMount.MountPath)
|
||||
}
|
||||
if volMount.ReadOnly != !hostMount.Writable {
|
||||
t.Errorf("Expected volume writable setting %t", hostMount.Writable)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user