Unverified Commit ed89907b authored by Lars Lehtonen's avatar Lars Lehtonen

Merge branch 'fix-aws-ebs-swallowed-errors' into fix-storageos-swallowed-err

parents 76a21a41 09f4ecff
...@@ -25,23 +25,23 @@ filegroup( ...@@ -25,23 +25,23 @@ filegroup(
# in build/common.sh. # in build/common.sh.
DOCKERIZED_BINARIES = { DOCKERIZED_BINARIES = {
"cloud-controller-manager": { "cloud-controller-manager": {
"base": "@official_busybox//image:image.tar", "base": "@official_busybox//image",
"target": "//cmd/cloud-controller-manager:cloud-controller-manager", "target": "//cmd/cloud-controller-manager:cloud-controller-manager",
}, },
"kube-apiserver": { "kube-apiserver": {
"base": "@official_busybox//image:image.tar", "base": "@official_busybox//image",
"target": "//cmd/kube-apiserver:kube-apiserver", "target": "//cmd/kube-apiserver:kube-apiserver",
}, },
"kube-controller-manager": { "kube-controller-manager": {
"base": "@official_busybox//image:image.tar", "base": "@official_busybox//image",
"target": "//cmd/kube-controller-manager:kube-controller-manager", "target": "//cmd/kube-controller-manager:kube-controller-manager",
}, },
"kube-scheduler": { "kube-scheduler": {
"base": "@official_busybox//image:image.tar", "base": "@official_busybox//image",
"target": "//plugin/cmd/kube-scheduler:kube-scheduler", "target": "//plugin/cmd/kube-scheduler:kube-scheduler",
}, },
"kube-proxy": { "kube-proxy": {
"base": "@debian-iptables-amd64//image:image.tar", "base": "@debian-iptables-amd64//image",
"target": "//cmd/kube-proxy:kube-proxy", "target": "//cmd/kube-proxy:kube-proxy",
}, },
} }
......
...@@ -65,7 +65,7 @@ func main() { ...@@ -65,7 +65,7 @@ func main() {
if s.AllowUntaggedCloud == true { if s.AllowUntaggedCloud == true {
glog.Warning("detected a cluster without a ClusterID. A ClusterID will be required in the future. Please tag your cluster to avoid any future issues") glog.Warning("detected a cluster without a ClusterID. A ClusterID will be required in the future. Please tag your cluster to avoid any future issues")
} else { } else {
glog.Fatalf("no ClusterID Found. A ClusterID is required for the cloud provider to function properly. This check can be bypassed by setting the allow-untagged-cloud option") glog.Fatalf("no ClusterID found. A ClusterID is required for the cloud provider to function properly. This check can be bypassed by setting the allow-untagged-cloud option")
} }
} }
......
...@@ -175,7 +175,8 @@ func startAttachDetachController(ctx ControllerContext) (bool, error) { ...@@ -175,7 +175,8 @@ func startAttachDetachController(ctx ControllerContext) (bool, error) {
ctx.InformerFactory.Core().V1().PersistentVolumeClaims(), ctx.InformerFactory.Core().V1().PersistentVolumeClaims(),
ctx.InformerFactory.Core().V1().PersistentVolumes(), ctx.InformerFactory.Core().V1().PersistentVolumes(),
ctx.Cloud, ctx.Cloud,
ProbeAttachableVolumePlugins(ctx.Options.VolumeConfiguration), ProbeAttachableVolumePlugins(),
GetDynamicPluginProber(ctx.Options.VolumeConfiguration),
ctx.Options.DisableAttachDetachReconcilerSync, ctx.Options.DisableAttachDetachReconcilerSync,
ctx.Options.ReconcilerSyncLoopPeriod.Duration, ctx.Options.ReconcilerSyncLoopPeriod.Duration,
attachdetach.DefaultTimerConfig, attachdetach.DefaultTimerConfig,
......
...@@ -60,18 +60,15 @@ import ( ...@@ -60,18 +60,15 @@ import (
) )
// ProbeAttachableVolumePlugins collects all volume plugins for the attach/ // ProbeAttachableVolumePlugins collects all volume plugins for the attach/
// detach controller. VolumeConfiguration is used ot get FlexVolumePluginDir // detach controller.
// which specifies the directory to search for additional third party volume
// plugins.
// The list of plugins is manually compiled. This code and the plugin // The list of plugins is manually compiled. This code and the plugin
// initialization code for kubelet really, really need a through refactor. // initialization code for kubelet really, really need a through refactor.
func ProbeAttachableVolumePlugins(config componentconfig.VolumeConfiguration) []volume.VolumePlugin { func ProbeAttachableVolumePlugins() []volume.VolumePlugin {
allPlugins := []volume.VolumePlugin{} allPlugins := []volume.VolumePlugin{}
allPlugins = append(allPlugins, aws_ebs.ProbeVolumePlugins()...) allPlugins = append(allPlugins, aws_ebs.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, gce_pd.ProbeVolumePlugins()...) allPlugins = append(allPlugins, gce_pd.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, cinder.ProbeVolumePlugins()...) allPlugins = append(allPlugins, cinder.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, flexvolume.ProbeVolumePlugins(config.FlexVolumePluginDir)...)
allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...) allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...) allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...) allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...)
...@@ -82,6 +79,13 @@ func ProbeAttachableVolumePlugins(config componentconfig.VolumeConfiguration) [] ...@@ -82,6 +79,13 @@ func ProbeAttachableVolumePlugins(config componentconfig.VolumeConfiguration) []
return allPlugins return allPlugins
} }
// GetDynamicPluginProber gets the probers of dynamically discoverable plugins
// for the attach/detach controller.
// Currently only Flexvolume plugins are dynamically discoverable.
func GetDynamicPluginProber(config componentconfig.VolumeConfiguration) volume.DynamicPluginProber {
return flexvolume.GetDynamicPluginProber(config.FlexVolumePluginDir)
}
// ProbeControllerVolumePlugins collects all persistent volume plugins into an // ProbeControllerVolumePlugins collects all persistent volume plugins into an
// easy to use list. Only volume plugins that implement any of // easy to use list. Only volume plugins that implement any of
// provisioner/recycler/deleter interface should be returned. // provisioner/recycler/deleter interface should be returned.
......
...@@ -327,6 +327,13 @@ func (i *Init) Run(out io.Writer) error { ...@@ -327,6 +327,13 @@ func (i *Init) Run(out io.Writer) error {
return err return err
} }
// Upload currently used configuration to the cluster
// Note: This is done right in the beginning of cluster initialization; as we might want to make other phases
// depend on centralized information from this source in the future
if err := uploadconfigphase.UploadConfiguration(i.cfg, client); err != nil {
return err
}
// PHASE 4: Mark the master with the right label/taint // PHASE 4: Mark the master with the right label/taint
if err := markmasterphase.MarkMaster(client, i.cfg.NodeName); err != nil { if err := markmasterphase.MarkMaster(client, i.cfg.NodeName); err != nil {
return err return err
...@@ -361,11 +368,6 @@ func (i *Init) Run(out io.Writer) error { ...@@ -361,11 +368,6 @@ func (i *Init) Run(out io.Writer) error {
// PHASE 6: Install and deploy all addons, and configure things as necessary // PHASE 6: Install and deploy all addons, and configure things as necessary
// Upload currently used configuration to the cluster
if err := uploadconfigphase.UploadConfiguration(i.cfg, client); err != nil {
return err
}
if err := apiconfigphase.CreateRBACRules(client, k8sVersion); err != nil { if err := apiconfigphase.CreateRBACRules(client, k8sVersion); err != nil {
return err return err
} }
......
...@@ -30,9 +30,11 @@ import ( ...@@ -30,9 +30,11 @@ import (
) )
const ( const (
caCertsVolumeName = "ca-certs" caCertsVolumeName = "ca-certs"
caCertsVolumePath = "/etc/ssl/certs" caCertsVolumePath = "/etc/ssl/certs"
caCertsPkiVolumeName = "ca-certs-etc-pki" caCertsPkiVolumeName = "ca-certs-etc-pki"
flexvolumeDirVolumeName = "flexvolume-dir"
flexvolumeDirVolumePath = "/usr/libexec/kubernetes/kubelet-plugins/volume/exec"
) )
// caCertsPkiVolumePath specifies the path that can be conditionally mounted into the apiserver and controller-manager containers // caCertsPkiVolumePath specifies the path that can be conditionally mounted into the apiserver and controller-manager containers
...@@ -68,6 +70,9 @@ func getHostPathVolumesForTheControlPlane(cfg *kubeadmapi.MasterConfiguration) c ...@@ -68,6 +70,9 @@ func getHostPathVolumesForTheControlPlane(cfg *kubeadmapi.MasterConfiguration) c
// Read-only mount for the controller manager kubeconfig file // Read-only mount for the controller manager kubeconfig file
controllerManagerKubeConfigFile := filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.ControllerManagerKubeConfigFileName) controllerManagerKubeConfigFile := filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.ControllerManagerKubeConfigFileName)
mounts.NewHostPathMount(kubeadmconstants.KubeControllerManager, kubeadmconstants.KubeConfigVolumeName, controllerManagerKubeConfigFile, controllerManagerKubeConfigFile, true, &hostPathFileOrCreate) 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.
mounts.NewHostPathMount(kubeadmconstants.KubeControllerManager, flexvolumeDirVolumeName, flexvolumeDirVolumePath, flexvolumeDirVolumePath, false, &hostPathDirectoryOrCreate)
// HostPath volumes for the scheduler // HostPath volumes for the scheduler
// Read-only mount for the scheduler kubeconfig file // Read-only mount for the scheduler kubeconfig file
......
...@@ -309,6 +309,15 @@ func TestGetHostPathVolumesForTheControlPlane(t *testing.T) { ...@@ -309,6 +309,15 @@ func TestGetHostPathVolumesForTheControlPlane(t *testing.T) {
}, },
}, },
}, },
{
Name: "flexvolume-dir",
VolumeSource: v1.VolumeSource{
HostPath: &v1.HostPathVolumeSource{
Path: "/usr/libexec/kubernetes/kubelet-plugins/volume/exec",
Type: &hostPathDirectoryOrCreate,
},
},
},
}, },
kubeadmconstants.KubeScheduler: { kubeadmconstants.KubeScheduler: {
{ {
...@@ -351,6 +360,11 @@ func TestGetHostPathVolumesForTheControlPlane(t *testing.T) { ...@@ -351,6 +360,11 @@ func TestGetHostPathVolumesForTheControlPlane(t *testing.T) {
MountPath: "/etc/kubernetes/controller-manager.conf", MountPath: "/etc/kubernetes/controller-manager.conf",
ReadOnly: true, ReadOnly: true,
}, },
{
Name: "flexvolume-dir",
MountPath: "/usr/libexec/kubernetes/kubelet-plugins/volume/exec",
ReadOnly: false,
},
}, },
kubeadmconstants.KubeScheduler: { kubeadmconstants.KubeScheduler: {
{ {
...@@ -439,6 +453,15 @@ func TestGetHostPathVolumesForTheControlPlane(t *testing.T) { ...@@ -439,6 +453,15 @@ func TestGetHostPathVolumesForTheControlPlane(t *testing.T) {
}, },
}, },
}, },
{
Name: "flexvolume-dir",
VolumeSource: v1.VolumeSource{
HostPath: &v1.HostPathVolumeSource{
Path: "/usr/libexec/kubernetes/kubelet-plugins/volume/exec",
Type: &hostPathDirectoryOrCreate,
},
},
},
}, },
kubeadmconstants.KubeScheduler: { kubeadmconstants.KubeScheduler: {
{ {
...@@ -491,6 +514,11 @@ func TestGetHostPathVolumesForTheControlPlane(t *testing.T) { ...@@ -491,6 +514,11 @@ func TestGetHostPathVolumesForTheControlPlane(t *testing.T) {
MountPath: "/etc/kubernetes/controller-manager.conf", MountPath: "/etc/kubernetes/controller-manager.conf",
ReadOnly: true, ReadOnly: true,
}, },
{
Name: "flexvolume-dir",
MountPath: "/usr/libexec/kubernetes/kubelet-plugins/volume/exec",
ReadOnly: false,
},
}, },
kubeadmconstants.KubeScheduler: { kubeadmconstants.KubeScheduler: {
{ {
......
...@@ -61,9 +61,7 @@ import ( ...@@ -61,9 +61,7 @@ import (
) )
// ProbeVolumePlugins collects all volume plugins into an easy to use list. // ProbeVolumePlugins collects all volume plugins into an easy to use list.
// PluginDir specifies the directory to search for additional third party func ProbeVolumePlugins() []volume.VolumePlugin {
// volume plugins.
func ProbeVolumePlugins(pluginDir string) []volume.VolumePlugin {
allPlugins := []volume.VolumePlugin{} allPlugins := []volume.VolumePlugin{}
// The list of plugins to probe is decided by the kubelet binary, not // The list of plugins to probe is decided by the kubelet binary, not
...@@ -88,7 +86,6 @@ func ProbeVolumePlugins(pluginDir string) []volume.VolumePlugin { ...@@ -88,7 +86,6 @@ func ProbeVolumePlugins(pluginDir string) []volume.VolumePlugin {
allPlugins = append(allPlugins, downwardapi.ProbeVolumePlugins()...) allPlugins = append(allPlugins, downwardapi.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, fc.ProbeVolumePlugins()...) allPlugins = append(allPlugins, fc.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, flocker.ProbeVolumePlugins()...) allPlugins = append(allPlugins, flocker.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, flexvolume.ProbeVolumePlugins(pluginDir)...)
allPlugins = append(allPlugins, azure_file.ProbeVolumePlugins()...) allPlugins = append(allPlugins, azure_file.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, configmap.ProbeVolumePlugins()...) allPlugins = append(allPlugins, configmap.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...) allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
...@@ -102,6 +99,13 @@ func ProbeVolumePlugins(pluginDir string) []volume.VolumePlugin { ...@@ -102,6 +99,13 @@ func ProbeVolumePlugins(pluginDir string) []volume.VolumePlugin {
return allPlugins return allPlugins
} }
// GetDynamicPluginProber gets the probers of dynamically discoverable plugins
// for kubelet.
// Currently only Flexvolume plugins are dynamically discoverable.
func GetDynamicPluginProber(pluginDir string) volume.DynamicPluginProber {
return flexvolume.GetDynamicPluginProber(pluginDir)
}
// ProbeNetworkPlugins collects all compiled-in plugins // ProbeNetworkPlugins collects all compiled-in plugins
func ProbeNetworkPlugins(pluginDir, cniConfDir, cniBinDir string) []network.NetworkPlugin { func ProbeNetworkPlugins(pluginDir, cniConfDir, cniBinDir string) []network.NetworkPlugin {
allPlugins := []network.NetworkPlugin{} allPlugins := []network.NetworkPlugin{}
......
...@@ -151,22 +151,22 @@ func UnsecuredDependencies(s *options.KubeletServer) (*kubelet.Dependencies, err ...@@ -151,22 +151,22 @@ func UnsecuredDependencies(s *options.KubeletServer) (*kubelet.Dependencies, err
} }
return &kubelet.Dependencies{ return &kubelet.Dependencies{
Auth: nil, // default does not enforce auth[nz] Auth: nil, // default does not enforce auth[nz]
CAdvisorInterface: nil, // cadvisor.New launches background processes (bg http.ListenAndServe, and some bg cleaners), not set here CAdvisorInterface: nil, // cadvisor.New launches background processes (bg http.ListenAndServe, and some bg cleaners), not set here
Cloud: nil, // cloud provider might start background processes Cloud: nil, // cloud provider might start background processes
ContainerManager: nil, ContainerManager: nil,
DockerClient: dockerClient, DockerClient: dockerClient,
KubeClient: nil, KubeClient: nil,
ExternalKubeClient: nil, ExternalKubeClient: nil,
EventClient: nil, EventClient: nil,
Mounter: mounter, Mounter: mounter,
NetworkPlugins: ProbeNetworkPlugins(s.NetworkPluginDir, s.CNIConfDir, s.CNIBinDir), NetworkPlugins: ProbeNetworkPlugins(s.NetworkPluginDir, s.CNIConfDir, s.CNIBinDir),
OOMAdjuster: oom.NewOOMAdjuster(), OOMAdjuster: oom.NewOOMAdjuster(),
OSInterface: kubecontainer.RealOS{}, OSInterface: kubecontainer.RealOS{},
Writer: writer, Writer: writer,
VolumePlugins: ProbeVolumePlugins(s.VolumePluginDir), VolumePlugins: ProbeVolumePlugins(),
TLSOptions: tlsOptions, DynamicPluginProber: GetDynamicPluginProber(s.VolumePluginDir),
}, nil TLSOptions: tlsOptions}, nil
} }
// Run runs the specified KubeletServer with the given Dependencies. This should never exit. // Run runs the specified KubeletServer with the given Dependencies. This should never exit.
...@@ -745,7 +745,7 @@ func parseResourceList(m kubeletconfiginternal.ConfigurationMap) (v1.ResourceLis ...@@ -745,7 +745,7 @@ func parseResourceList(m kubeletconfiginternal.ConfigurationMap) (v1.ResourceLis
for k, v := range m { for k, v := range m {
switch v1.ResourceName(k) { switch v1.ResourceName(k) {
// CPU, memory and local storage resources are supported. // CPU, memory and local storage resources are supported.
case v1.ResourceCPU, v1.ResourceMemory, v1.ResourceStorage: case v1.ResourceCPU, v1.ResourceMemory, v1.ResourceEphemeralStorage:
q, err := resource.ParseQuantity(v) q, err := resource.ParseQuantity(v)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -753,12 +753,7 @@ func parseResourceList(m kubeletconfiginternal.ConfigurationMap) (v1.ResourceLis ...@@ -753,12 +753,7 @@ func parseResourceList(m kubeletconfiginternal.ConfigurationMap) (v1.ResourceLis
if q.Sign() == -1 { if q.Sign() == -1 {
return nil, fmt.Errorf("resource quantity for %q cannot be negative: %v", k, v) return nil, fmt.Errorf("resource quantity for %q cannot be negative: %v", k, v)
} }
// storage specified in configuration map is mapped to ResourceStorageScratch API rl[v1.ResourceName(k)] = q
if v1.ResourceName(k) == v1.ResourceStorage {
rl[v1.ResourceStorageScratch] = q
} else {
rl[v1.ResourceName(k)] = q
}
default: default:
return nil, fmt.Errorf("cannot reserve %q resource", k) return nil, fmt.Errorf("cannot reserve %q resource", k)
} }
......
...@@ -31,6 +31,8 @@ KUBELET_AUTHORIZATION_WEBHOOK=${KUBELET_AUTHORIZATION_WEBHOOK:-""} ...@@ -31,6 +31,8 @@ KUBELET_AUTHORIZATION_WEBHOOK=${KUBELET_AUTHORIZATION_WEBHOOK:-""}
KUBELET_AUTHENTICATION_WEBHOOK=${KUBELET_AUTHENTICATION_WEBHOOK:-""} KUBELET_AUTHENTICATION_WEBHOOK=${KUBELET_AUTHENTICATION_WEBHOOK:-""}
POD_MANIFEST_PATH=${POD_MANIFEST_PATH:-"/var/run/kubernetes/static-pods"} POD_MANIFEST_PATH=${POD_MANIFEST_PATH:-"/var/run/kubernetes/static-pods"}
KUBELET_FLAGS=${KUBELET_FLAGS:-""} KUBELET_FLAGS=${KUBELET_FLAGS:-""}
# many dev environments run with swap on, so we don't fail in this env
FAIL_SWAP_ON=${FAIL_SWAP_ON:-"false"}
# Name of the network plugin, eg: "kubenet" # Name of the network plugin, eg: "kubenet"
NET_PLUGIN=${NET_PLUGIN:-""} NET_PLUGIN=${NET_PLUGIN:-""}
# Place the config files and binaries required by NET_PLUGIN in these directory, # Place the config files and binaries required by NET_PLUGIN in these directory,
...@@ -112,6 +114,11 @@ if [ "${CLOUD_PROVIDER}" == "openstack" ]; then ...@@ -112,6 +114,11 @@ if [ "${CLOUD_PROVIDER}" == "openstack" ]; then
fi fi
fi fi
# warn if users are running with swap allowed
if [ "${FAIL_SWAP_ON}" == "false" ]; then
echo "WARNING : The kubelet is configured to not fail if swap is enabled; production deployments should disable swap."
fi
if [ "$(id -u)" != "0" ]; then if [ "$(id -u)" != "0" ]; then
echo "WARNING : This script MAY be run as root for docker socket / iptables functionality; if failures occur, retry as root." 2>&1 echo "WARNING : This script MAY be run as root for docker socket / iptables functionality; if failures occur, retry as root." 2>&1
fi fi
...@@ -665,6 +672,7 @@ function start_kubelet { ...@@ -665,6 +672,7 @@ function start_kubelet {
--eviction-soft=${EVICTION_SOFT} \ --eviction-soft=${EVICTION_SOFT} \
--eviction-pressure-transition-period=${EVICTION_PRESSURE_TRANSITION_PERIOD} \ --eviction-pressure-transition-period=${EVICTION_PRESSURE_TRANSITION_PERIOD} \
--pod-manifest-path="${POD_MANIFEST_PATH}" \ --pod-manifest-path="${POD_MANIFEST_PATH}" \
--fail-swap-on="${FAIL_SWAP_ON}" \
${auth_args} \ ${auth_args} \
${dns_args} \ ${dns_args} \
${cni_conf_dir_args} \ ${cni_conf_dir_args} \
......
...@@ -107,6 +107,7 @@ func IsResourceQuotaScopeValidForResource(scope api.ResourceQuotaScope, resource ...@@ -107,6 +107,7 @@ func IsResourceQuotaScopeValidForResource(scope api.ResourceQuotaScope, resource
var standardContainerResources = sets.NewString( var standardContainerResources = sets.NewString(
string(api.ResourceCPU), string(api.ResourceCPU),
string(api.ResourceMemory), string(api.ResourceMemory),
string(api.ResourceEphemeralStorage),
) )
// IsStandardContainerResourceName returns true if the container can make a resource request // IsStandardContainerResourceName returns true if the container can make a resource request
...@@ -194,10 +195,13 @@ func IsStandardQuotaResourceName(str string) bool { ...@@ -194,10 +195,13 @@ func IsStandardQuotaResourceName(str string) bool {
var standardResources = sets.NewString( var standardResources = sets.NewString(
string(api.ResourceCPU), string(api.ResourceCPU),
string(api.ResourceMemory), string(api.ResourceMemory),
string(api.ResourceEphemeralStorage),
string(api.ResourceRequestsCPU), string(api.ResourceRequestsCPU),
string(api.ResourceRequestsMemory), string(api.ResourceRequestsMemory),
string(api.ResourceRequestsEphemeralStorage),
string(api.ResourceLimitsCPU), string(api.ResourceLimitsCPU),
string(api.ResourceLimitsMemory), string(api.ResourceLimitsMemory),
string(api.ResourceLimitsEphemeralStorage),
string(api.ResourcePods), string(api.ResourcePods),
string(api.ResourceQuotas), string(api.ResourceQuotas),
string(api.ResourceServices), string(api.ResourceServices),
......
...@@ -166,6 +166,7 @@ func addConversionFuncs(scheme *runtime.Scheme) error { ...@@ -166,6 +166,7 @@ func addConversionFuncs(scheme *runtime.Scheme) error {
"spec.nodeName", "spec.nodeName",
"spec.restartPolicy", "spec.restartPolicy",
"spec.serviceAccountName", "spec.serviceAccountName",
"spec.schedulerName",
"status.phase", "status.phase",
"status.hostIP", "status.hostIP",
"status.podIP": "status.podIP":
......
...@@ -3817,8 +3817,8 @@ func ValidateResourceRequirements(requirements *api.ResourceRequirements, fldPat ...@@ -3817,8 +3817,8 @@ func ValidateResourceRequirements(requirements *api.ResourceRequirements, fldPat
// Validate resource quantity. // Validate resource quantity.
allErrs = append(allErrs, ValidateResourceQuantityValue(string(resourceName), quantity, fldPath)...) allErrs = append(allErrs, ValidateResourceQuantityValue(string(resourceName), quantity, fldPath)...)
if resourceName == api.ResourceStorageOverlay && !utilfeature.DefaultFeatureGate.Enabled(features.LocalStorageCapacityIsolation) { if resourceName == api.ResourceEphemeralStorage && !utilfeature.DefaultFeatureGate.Enabled(features.LocalStorageCapacityIsolation) {
allErrs = append(allErrs, field.Forbidden(limPath, "ResourceStorageOverlay field disabled by feature-gate for ResourceRequirements")) allErrs = append(allErrs, field.Forbidden(limPath, "ResourceEphemeralStorage field disabled by feature-gate for ResourceRequirements"))
} }
} }
for resourceName, quantity := range requirements.Requests { for resourceName, quantity := range requirements.Requests {
......
...@@ -2683,7 +2683,7 @@ func TestAlphaLocalStorageCapacityIsolation(t *testing.T) { ...@@ -2683,7 +2683,7 @@ func TestAlphaLocalStorageCapacityIsolation(t *testing.T) {
containerLimitCase := api.ResourceRequirements{ containerLimitCase := api.ResourceRequirements{
Limits: api.ResourceList{ Limits: api.ResourceList{
api.ResourceStorageOverlay: *resource.NewMilliQuantity( api.ResourceEphemeralStorage: *resource.NewMilliQuantity(
int64(40000), int64(40000),
resource.BinarySI), resource.BinarySI),
}, },
......
...@@ -124,7 +124,7 @@ type Instances interface { ...@@ -124,7 +124,7 @@ type Instances interface {
// ProviderID is a unique identifier of the node. This will not be called // ProviderID is a unique identifier of the node. This will not be called
// from the node whose nodeaddresses are being queried. i.e. local metadata // from the node whose nodeaddresses are being queried. i.e. local metadata
// services cannot be used in this method to obtain nodeaddresses // services cannot be used in this method to obtain nodeaddresses
NodeAddressesByProviderID(providerId string) ([]v1.NodeAddress, error) NodeAddressesByProviderID(providerID string) ([]v1.NodeAddress, error)
// ExternalID returns the cloud provider ID of the node with the specified NodeName. // ExternalID returns the cloud provider ID of the node with the specified NodeName.
// Note that if the instance does not exist or is no longer running, we must return ("", cloudprovider.InstanceNotFound) // Note that if the instance does not exist or is no longer running, we must return ("", cloudprovider.InstanceNotFound)
ExternalID(nodeName types.NodeName) (string, error) ExternalID(nodeName types.NodeName) (string, error)
...@@ -140,6 +140,9 @@ type Instances interface { ...@@ -140,6 +140,9 @@ type Instances interface {
// CurrentNodeName returns the name of the node we are currently running on // CurrentNodeName returns the name of the node we are currently running on
// On most clouds (e.g. GCE) this is the hostname, so we provide the hostname // On most clouds (e.g. GCE) this is the hostname, so we provide the hostname
CurrentNodeName(hostname string) (types.NodeName, error) CurrentNodeName(hostname string) (types.NodeName, error)
// InstanceExistsByProviderID returns true if the instance for the given provider id still is running.
// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
InstanceExistsByProviderID(providerID string) (bool, error)
} }
// Route is a representation of an advanced routing rule. // Route is a representation of an advanced routing rule.
......
...@@ -1101,6 +1101,12 @@ func (c *Cloud) ExternalID(nodeName types.NodeName) (string, error) { ...@@ -1101,6 +1101,12 @@ func (c *Cloud) ExternalID(nodeName types.NodeName) (string, error) {
return orEmpty(instance.InstanceId), nil return orEmpty(instance.InstanceId), nil
} }
// InstanceExistsByProviderID returns true if the instance with the given provider id still exists and is running.
// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
func (c *Cloud) InstanceExistsByProviderID(providerID string) (bool, error) {
return false, errors.New("unimplemented")
}
// InstanceID returns the cloud provider ID of the node with the specified nodeName. // InstanceID returns the cloud provider ID of the node with the specified nodeName.
func (c *Cloud) InstanceID(nodeName types.NodeName) (string, error) { func (c *Cloud) InstanceID(nodeName types.NodeName) (string, error) {
// In the future it is possible to also return an endpoint as: // In the future it is possible to also return an endpoint as:
......
...@@ -17,6 +17,7 @@ limitations under the License. ...@@ -17,6 +17,7 @@ limitations under the License.
package azure package azure
import ( import (
"errors"
"fmt" "fmt"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
...@@ -86,6 +87,12 @@ func (az *Cloud) ExternalID(name types.NodeName) (string, error) { ...@@ -86,6 +87,12 @@ func (az *Cloud) ExternalID(name types.NodeName) (string, error) {
return az.InstanceID(name) return az.InstanceID(name)
} }
// InstanceExistsByProviderID returns true if the instance with the given provider id still exists and is running.
// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
func (az *Cloud) InstanceExistsByProviderID(providerID string) (bool, error) {
return false, errors.New("unimplemented")
}
func (az *Cloud) isCurrentInstance(name types.NodeName) (bool, error) { func (az *Cloud) isCurrentInstance(name types.NodeName) (bool, error) {
nodeName := mapNodeNameToVMName(name) nodeName := mapNodeNameToVMName(name)
metadataName, err := az.metadata.Text("instance/compute/name") metadataName, err := az.metadata.Text("instance/compute/name")
......
...@@ -47,8 +47,12 @@ type FakeUpdateBalancerCall struct { ...@@ -47,8 +47,12 @@ type FakeUpdateBalancerCall struct {
// FakeCloud is a test-double implementation of Interface, LoadBalancer, Instances, and Routes. It is useful for testing. // FakeCloud is a test-double implementation of Interface, LoadBalancer, Instances, and Routes. It is useful for testing.
type FakeCloud struct { type FakeCloud struct {
Exists bool Exists bool
Err error Err error
ExistsByProviderID bool
ErrByProviderID error
Calls []string Calls []string
Addresses []v1.NodeAddress Addresses []v1.NodeAddress
ExtID map[types.NodeName]string ExtID map[types.NodeName]string
...@@ -234,6 +238,13 @@ func (f *FakeCloud) InstanceTypeByProviderID(providerID string) (string, error) ...@@ -234,6 +238,13 @@ func (f *FakeCloud) InstanceTypeByProviderID(providerID string) (string, error)
return f.InstanceTypes[types.NodeName(providerID)], nil return f.InstanceTypes[types.NodeName(providerID)], nil
} }
// InstanceExistsByProviderID returns true if the instance with the given provider id still exists and is running.
// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
func (f *FakeCloud) InstanceExistsByProviderID(providerID string) (bool, error) {
f.addCall("instance-exists-by-provider-id")
return f.ExistsByProviderID, f.ErrByProviderID
}
// List is a test-spy implementation of Instances.List. // List is a test-spy implementation of Instances.List.
// It adds an entry "list" into the internal method call record. // It adds an entry "list" into the internal method call record.
func (f *FakeCloud) List(filter string) ([]types.NodeName, error) { func (f *FakeCloud) List(filter string) ([]types.NodeName, error) {
......
...@@ -144,51 +144,42 @@ type GCEServiceManager struct { ...@@ -144,51 +144,42 @@ type GCEServiceManager struct {
gce *GCECloud gce *GCECloud
} }
type ConfigGlobal struct {
TokenURL string `gcfg:"token-url"`
TokenBody string `gcfg:"token-body"`
// ProjectID and NetworkProjectID can either be the numeric or string-based
// unique identifier that starts with [a-z].
ProjectID string `gcfg:"project-id"`
// NetworkProjectID refers to the project which owns the network being used.
NetworkProjectID string `gcfg:"network-project-id"`
NetworkName string `gcfg:"network-name"`
SubnetworkName string `gcfg:"subnetwork-name"`
// SecondaryRangeName is the name of the secondary range to allocate IP
// aliases. The secondary range must be present on the subnetwork the
// cluster is attached to.
SecondaryRangeName string `gcfg:"secondary-range-name"`
NodeTags []string `gcfg:"node-tags"`
NodeInstancePrefix string `gcfg:"node-instance-prefix"`
Multizone bool `gcfg:"multizone"`
// ApiEndpoint is the GCE compute API endpoint to use. If this is blank,
// then the default endpoint is used.
ApiEndpoint string `gcfg:"api-endpoint"`
// LocalZone specifies the GCE zone that gce cloud client instance is
// located in (i.e. where the controller will be running). If this is
// blank, then the local zone will be discovered via the metadata server.
LocalZone string `gcfg:"local-zone"`
// Possible values: List of api names separated by comma. Default to none.
// For example: MyFeatureFlag
AlphaFeatures []string `gcfg:"alpha-features"`
}
// ConfigFile is the struct used to parse the /etc/gce.conf configuration file. // ConfigFile is the struct used to parse the /etc/gce.conf configuration file.
type ConfigFile struct { type ConfigFile struct {
Global ConfigGlobal `gcfg:"global"` Global struct {
TokenURL string `gcfg:"token-url"`
TokenBody string `gcfg:"token-body"`
ProjectID string `gcfg:"project-id"`
NetworkName string `gcfg:"network-name"`
SubnetworkName string `gcfg:"subnetwork-name"`
// SecondaryRangeName is the name of the secondary range to allocate IP
// aliases. The secondary range must be present on the subnetwork the
// cluster is attached to.
SecondaryRangeName string `gcfg:"secondary-range-name"`
NodeTags []string `gcfg:"node-tags"`
NodeInstancePrefix string `gcfg:"node-instance-prefix"`
Multizone bool `gcfg:"multizone"`
// ApiEndpoint is the GCE compute API endpoint to use. If this is blank,
// then the default endpoint is used.
ApiEndpoint string `gcfg:"api-endpoint"`
// LocalZone specifies the GCE zone that gce cloud client instance is
// located in (i.e. where the controller will be running). If this is
// blank, then the local zone will be discovered via the metadata server.
LocalZone string `gcfg:"local-zone"`
// AlphaFeatures is a list of API flags to be enabled. Defaults to none.
// Example API name format: "MyFeatureFlag"
AlphaFeatures []string `gcfg:"alpha-features"`
}
} }
// CloudConfig includes all the necessary configuration for creating GCECloud // CloudConfig includes all the necessary configuration for creating GCECloud
type CloudConfig struct { type CloudConfig struct {
ApiEndpoint string ApiEndpoint string
ProjectID string ProjectID string
NetworkProjectID string
Region string Region string
Zone string Zone string
ManagedZones []string ManagedZones []string
NetworkName string
NetworkURL string NetworkURL string
SubnetworkName string
SubnetworkURL string SubnetworkURL string
SecondaryRangeName string SecondaryRangeName string
NodeTags []string NodeTags []string
...@@ -216,6 +207,11 @@ func (g *GCECloud) GetKMSService() *cloudkms.Service { ...@@ -216,6 +207,11 @@ func (g *GCECloud) GetKMSService() *cloudkms.Service {
return g.cloudkmsService return g.cloudkmsService
} }
// Returns the ProjectID corresponding to the project this cloud is in.
func (g *GCECloud) GetProjectID() string {
return g.projectID
}
// newGCECloud creates a new instance of GCECloud. // newGCECloud creates a new instance of GCECloud.
func newGCECloud(config io.Reader) (gceCloud *GCECloud, err error) { func newGCECloud(config io.Reader) (gceCloud *GCECloud, err error) {
var cloudConfig *CloudConfig var cloudConfig *CloudConfig
...@@ -284,7 +280,6 @@ func generateCloudConfig(configFile *ConfigFile) (cloudConfig *CloudConfig, err ...@@ -284,7 +280,6 @@ func generateCloudConfig(configFile *ConfigFile) (cloudConfig *CloudConfig, err
return nil, err return nil, err
} }
} }
if configFile != nil { if configFile != nil {
if configFile.Global.ProjectID != "" { if configFile.Global.ProjectID != "" {
cloudConfig.ProjectID = configFile.Global.ProjectID cloudConfig.ProjectID = configFile.Global.ProjectID
...@@ -292,9 +287,6 @@ func generateCloudConfig(configFile *ConfigFile) (cloudConfig *CloudConfig, err ...@@ -292,9 +287,6 @@ func generateCloudConfig(configFile *ConfigFile) (cloudConfig *CloudConfig, err
if configFile.Global.LocalZone != "" { if configFile.Global.LocalZone != "" {
cloudConfig.Zone = configFile.Global.LocalZone cloudConfig.Zone = configFile.Global.LocalZone
} }
if configFile.Global.NetworkProjectID != "" {
cloudConfig.NetworkProjectID = configFile.Global.NetworkProjectID
}
} }
// retrieve region // retrieve region
...@@ -309,27 +301,27 @@ func generateCloudConfig(configFile *ConfigFile) (cloudConfig *CloudConfig, err ...@@ -309,27 +301,27 @@ func generateCloudConfig(configFile *ConfigFile) (cloudConfig *CloudConfig, err
cloudConfig.ManagedZones = nil // Use all zones in region cloudConfig.ManagedZones = nil // Use all zones in region
} }
// Determine if network parameter is URL or Name // generate networkURL
if configFile != nil && configFile.Global.NetworkName != "" { if configFile != nil && configFile.Global.NetworkName != "" {
if strings.Contains(configFile.Global.NetworkName, "/") { if strings.Contains(configFile.Global.NetworkName, "/") {
cloudConfig.NetworkURL = configFile.Global.NetworkName cloudConfig.NetworkURL = configFile.Global.NetworkName
} else { } else {
cloudConfig.NetworkName = configFile.Global.NetworkName cloudConfig.NetworkURL = gceNetworkURL(cloudConfig.ApiEndpoint, cloudConfig.ProjectID, configFile.Global.NetworkName)
} }
} else { } else {
cloudConfig.NetworkName, err = getNetworkNameViaMetadata() networkName, err := getNetworkNameViaMetadata()
if err != nil { if err != nil {
return nil, err return nil, err
} }
cloudConfig.NetworkURL = gceNetworkURL("", cloudConfig.ProjectID, networkName)
} }
// Determine if subnetwork parameter is URL or Name // generate subnetworkURL
// If cluster is on a GCP network of mode=custom, then `SubnetName` must be specified in config file.
if configFile != nil && configFile.Global.SubnetworkName != "" { if configFile != nil && configFile.Global.SubnetworkName != "" {
if strings.Contains(configFile.Global.SubnetworkName, "/") { if strings.Contains(configFile.Global.SubnetworkName, "/") {
cloudConfig.SubnetworkURL = configFile.Global.SubnetworkName cloudConfig.SubnetworkURL = configFile.Global.SubnetworkName
} else { } else {
cloudConfig.SubnetworkName = configFile.Global.SubnetworkName cloudConfig.SubnetworkURL = gceSubnetworkURL(cloudConfig.ApiEndpoint, cloudConfig.ProjectID, cloudConfig.Region, configFile.Global.SubnetworkName)
} }
} }
...@@ -340,15 +332,11 @@ func generateCloudConfig(configFile *ConfigFile) (cloudConfig *CloudConfig, err ...@@ -340,15 +332,11 @@ func generateCloudConfig(configFile *ConfigFile) (cloudConfig *CloudConfig, err
return cloudConfig, err return cloudConfig, err
} }
// CreateGCECloud creates a GCECloud object using the specified parameters. // Creates a GCECloud object using the specified parameters.
// If no networkUrl is specified, loads networkName via rest call. // If no networkUrl is specified, loads networkName via rest call.
// If no tokenSource is specified, uses oauth2.DefaultTokenSource. // If no tokenSource is specified, uses oauth2.DefaultTokenSource.
// If managedZones is nil / empty all zones in the region will be managed. // If managedZones is nil / empty all zones in the region will be managed.
func CreateGCECloud(config *CloudConfig) (*GCECloud, error) { func CreateGCECloud(config *CloudConfig) (*GCECloud, error) {
// Use ProjectID for NetworkProjectID, if it wasn't explicitly set.
if config.NetworkProjectID == "" {
config.NetworkProjectID = config.ProjectID
}
client, err := newOauthClient(config.TokenSource) client, err := newOauthClient(config.TokenSource)
if err != nil { if err != nil {
...@@ -397,49 +385,19 @@ func CreateGCECloud(config *CloudConfig) (*GCECloud, error) { ...@@ -397,49 +385,19 @@ func CreateGCECloud(config *CloudConfig) (*GCECloud, error) {
return nil, err return nil, err
} }
// config.ProjectID may be the id or project number if config.NetworkURL == "" {
// In gce_routes.go, the generated networkURL is compared with a URL within a route networkName, err := getNetworkNameViaAPICall(service, config.ProjectID)
// therefore, we need to make sure the URL is constructed with the string ID.
projID, err := getProjectID(service, config.ProjectID)
if err != nil {
return nil, fmt.Errorf("failed to get project %v, err: %v", config.ProjectID, err)
}
// config.NetworkProjectID may be the id or project number. In order to compare project ID
// to network project ID to determine XPN status, we need to verify both are actual IDs.
netProjID := projID
if config.NetworkProjectID != config.ProjectID {
netProjID, err = getProjectID(service, config.NetworkProjectID)
if err != nil {
return nil, fmt.Errorf("failed to get network project %v, err: %v", config.NetworkProjectID, err)
}
}
onXPN := projID != netProjID
var networkURL string
var subnetURL string
if config.NetworkName == "" && config.NetworkURL == "" {
// TODO: Stop using this call and return an error.
// This function returns the first network in a list of networks for a project. The project
// should be set via configuration instead of randomly taking the first.
networkName, err := getNetworkNameViaAPICall(service, config.NetworkProjectID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
networkURL = gceNetworkURL(config.ApiEndpoint, netProjID, networkName) config.NetworkURL = gceNetworkURL(config.ApiEndpoint, config.ProjectID, networkName)
} else if config.NetworkURL != "" {
networkURL = config.NetworkURL
} else {
networkURL = gceNetworkURL(config.ApiEndpoint, netProjID, config.NetworkName)
} }
if config.SubnetworkURL != "" { networkProjectID, err := getProjectIDInURL(config.NetworkURL)
subnetURL = config.SubnetworkURL if err != nil {
} else if config.SubnetworkName != "" { return nil, err
subnetURL = gceSubnetworkURL(config.ApiEndpoint, netProjID, config.Region, config.SubnetworkName)
} }
onXPN := networkProjectID != config.ProjectID
if len(config.ManagedZones) == 0 { if len(config.ManagedZones) == 0 {
config.ManagedZones, err = getZonesForRegion(service, config.ProjectID, config.Region) config.ManagedZones, err = getZonesForRegion(service, config.ProjectID, config.Region)
...@@ -459,14 +417,14 @@ func CreateGCECloud(config *CloudConfig) (*GCECloud, error) { ...@@ -459,14 +417,14 @@ func CreateGCECloud(config *CloudConfig) (*GCECloud, error) {
serviceBeta: serviceBeta, serviceBeta: serviceBeta,
containerService: containerService, containerService: containerService,
cloudkmsService: cloudkmsService, cloudkmsService: cloudkmsService,
projectID: projID, projectID: config.ProjectID,
networkProjectID: netProjID, networkProjectID: networkProjectID,
onXPN: onXPN, onXPN: onXPN,
region: config.Region, region: config.Region,
localZone: config.Zone, localZone: config.Zone,
managedZones: config.ManagedZones, managedZones: config.ManagedZones,
networkURL: networkURL, networkURL: config.NetworkURL,
subnetworkURL: subnetURL, subnetworkURL: config.SubnetworkURL,
secondaryRangeName: config.SecondaryRangeName, secondaryRangeName: config.SecondaryRangeName,
nodeTags: config.NodeTags, nodeTags: config.NodeTags,
nodeInstancePrefix: config.NodeInstancePrefix, nodeInstancePrefix: config.NodeInstancePrefix,
...@@ -515,16 +473,6 @@ func (gce *GCECloud) ProviderName() string { ...@@ -515,16 +473,6 @@ func (gce *GCECloud) ProviderName() string {
return ProviderName return ProviderName
} }
// ProjectID returns the ProjectID corresponding to the project this cloud is in.
func (g *GCECloud) ProjectID() string {
return g.projectID
}
// NetworkProjectID returns the ProjectID corresponding to the project this cluster's network is in.
func (g *GCECloud) NetworkProjectID() string {
return g.networkProjectID
}
// Region returns the region // Region returns the region
func (gce *GCECloud) Region() string { func (gce *GCECloud) Region() string {
return gce.region return gce.region
...@@ -621,16 +569,6 @@ func getNetworkNameViaAPICall(svc *compute.Service, projectID string) (string, e ...@@ -621,16 +569,6 @@ func getNetworkNameViaAPICall(svc *compute.Service, projectID string) (string, e
return networkList.Items[0].Name, nil return networkList.Items[0].Name, nil
} }
// getProjectID returns the project's string ID given a project number or string
func getProjectID(svc *compute.Service, projectNumberOrID string) (string, error) {
proj, err := svc.Projects.Get(projectNumberOrID).Do()
if err != nil {
return "", err
}
return proj.Name, nil
}
func getZonesForRegion(svc *compute.Service, projectID, region string) ([]string, error) { func getZonesForRegion(svc *compute.Service, projectID, region string) ([]string, error) {
// TODO: use PageToken to list all not just the first 500 // TODO: use PageToken to list all not just the first 500
listCall := svc.Zones.List(projectID) listCall := svc.Zones.List(projectID)
......
...@@ -27,38 +27,38 @@ func newFirewallMetricContext(request string) *metricContext { ...@@ -27,38 +27,38 @@ func newFirewallMetricContext(request string) *metricContext {
// GetFirewall returns the Firewall by name. // GetFirewall returns the Firewall by name.
func (gce *GCECloud) GetFirewall(name string) (*compute.Firewall, error) { func (gce *GCECloud) GetFirewall(name string) (*compute.Firewall, error) {
mc := newFirewallMetricContext("get") mc := newFirewallMetricContext("get")
v, err := gce.service.Firewalls.Get(gce.NetworkProjectID(), name).Do() v, err := gce.service.Firewalls.Get(gce.projectID, name).Do()
return v, mc.Observe(err) return v, mc.Observe(err)
} }
// CreateFirewall creates the passed firewall // CreateFirewall creates the passed firewall
func (gce *GCECloud) CreateFirewall(f *compute.Firewall) error { func (gce *GCECloud) CreateFirewall(f *compute.Firewall) error {
mc := newFirewallMetricContext("create") mc := newFirewallMetricContext("create")
op, err := gce.service.Firewalls.Insert(gce.NetworkProjectID(), f).Do() op, err := gce.service.Firewalls.Insert(gce.projectID, f).Do()
if err != nil { if err != nil {
return mc.Observe(err) return mc.Observe(err)
} }
return gce.waitForGlobalOpInProject(op, gce.NetworkProjectID(), mc) return gce.waitForGlobalOp(op, mc)
} }
// DeleteFirewall deletes the given firewall rule. // DeleteFirewall deletes the given firewall rule.
func (gce *GCECloud) DeleteFirewall(name string) error { func (gce *GCECloud) DeleteFirewall(name string) error {
mc := newFirewallMetricContext("delete") mc := newFirewallMetricContext("delete")
op, err := gce.service.Firewalls.Delete(gce.NetworkProjectID(), name).Do() op, err := gce.service.Firewalls.Delete(gce.projectID, name).Do()
if err != nil { if err != nil {
return mc.Observe(err) return mc.Observe(err)
} }
return gce.waitForGlobalOpInProject(op, gce.NetworkProjectID(), mc) return gce.waitForGlobalOp(op, mc)
} }
// UpdateFirewall applies the given firewall as an update to an existing service. // UpdateFirewall applies the given firewall as an update to an existing service.
func (gce *GCECloud) UpdateFirewall(f *compute.Firewall) error { func (gce *GCECloud) UpdateFirewall(f *compute.Firewall) error {
mc := newFirewallMetricContext("update") mc := newFirewallMetricContext("update")
op, err := gce.service.Firewalls.Update(gce.NetworkProjectID(), f.Name, f).Do() op, err := gce.service.Firewalls.Update(gce.projectID, f.Name, f).Do()
if err != nil { if err != nil {
return mc.Observe(err) return mc.Observe(err)
} }
return gce.waitForGlobalOpInProject(op, gce.NetworkProjectID(), mc) return gce.waitForGlobalOp(op, mc)
} }
...@@ -17,6 +17,7 @@ limitations under the License. ...@@ -17,6 +17,7 @@ limitations under the License.
package gce package gce
import ( import (
"errors"
"fmt" "fmt"
"net" "net"
"net/http" "net/http"
...@@ -153,6 +154,12 @@ func (gce *GCECloud) ExternalID(nodeName types.NodeName) (string, error) { ...@@ -153,6 +154,12 @@ func (gce *GCECloud) ExternalID(nodeName types.NodeName) (string, error) {
return strconv.FormatUint(inst.ID, 10), nil return strconv.FormatUint(inst.ID, 10), nil
} }
// InstanceExistsByProviderID returns true if the instance with the given provider id still exists and is running.
// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
func (gce *GCECloud) InstanceExistsByProviderID(providerID string) (bool, error) {
return false, errors.New("unimplemented")
}
// InstanceID returns the cloud provider ID of the node with the specified NodeName. // InstanceID returns the cloud provider ID of the node with the specified NodeName.
func (gce *GCECloud) InstanceID(nodeName types.NodeName) (string, error) { func (gce *GCECloud) InstanceID(nodeName types.NodeName) (string, error) {
instanceName := mapNodeNameToInstanceName(nodeName) instanceName := mapNodeNameToInstanceName(nodeName)
......
...@@ -729,7 +729,7 @@ func (gce *GCECloud) firewallNeedsUpdate(name, serviceName, region, ipAddress st ...@@ -729,7 +729,7 @@ func (gce *GCECloud) firewallNeedsUpdate(name, serviceName, region, ipAddress st
return false, false, nil return false, false, nil
} }
fw, err := gce.service.Firewalls.Get(gce.NetworkProjectID(), makeFirewallName(name)).Do() fw, err := gce.service.Firewalls.Get(gce.projectID, makeFirewallName(name)).Do()
if err != nil { if err != nil {
if isHTTPErrorCode(err, http.StatusNotFound) { if isHTTPErrorCode(err, http.StatusNotFound) {
return false, true, nil return false, true, nil
...@@ -776,7 +776,7 @@ func (gce *GCECloud) ensureHttpHealthCheckFirewall(serviceName, ipAddress, regio ...@@ -776,7 +776,7 @@ func (gce *GCECloud) ensureHttpHealthCheckFirewall(serviceName, ipAddress, regio
ports := []v1.ServicePort{{Protocol: "tcp", Port: hcPort}} ports := []v1.ServicePort{{Protocol: "tcp", Port: hcPort}}
fwName := MakeHealthCheckFirewallName(clusterID, hcName, isNodesHealthCheck) fwName := MakeHealthCheckFirewallName(clusterID, hcName, isNodesHealthCheck)
fw, err := gce.service.Firewalls.Get(gce.NetworkProjectID(), fwName).Do() fw, err := gce.service.Firewalls.Get(gce.projectID, fwName).Do()
if err != nil { if err != nil {
if !isHTTPErrorCode(err, http.StatusNotFound) { if !isHTTPErrorCode(err, http.StatusNotFound) {
return fmt.Errorf("error getting firewall for health checks: %v", err) return fmt.Errorf("error getting firewall for health checks: %v", err)
......
...@@ -93,74 +93,62 @@ func getErrorFromOp(op *computev1.Operation) error { ...@@ -93,74 +93,62 @@ func getErrorFromOp(op *computev1.Operation) error {
} }
func (gce *GCECloud) waitForGlobalOp(op gceObject, mc *metricContext) error { func (gce *GCECloud) waitForGlobalOp(op gceObject, mc *metricContext) error {
return gce.waitForGlobalOpInProject(op, gce.ProjectID(), mc)
}
func (gce *GCECloud) waitForRegionOp(op gceObject, region string, mc *metricContext) error {
return gce.waitForRegionOpInProject(op, gce.ProjectID(), region, mc)
}
func (gce *GCECloud) waitForZoneOp(op gceObject, zone string, mc *metricContext) error {
return gce.waitForZoneOpInProject(op, gce.ProjectID(), zone, mc)
}
func (gce *GCECloud) waitForGlobalOpInProject(op gceObject, projectID string, mc *metricContext) error {
switch v := op.(type) { switch v := op.(type) {
case *computealpha.Operation: case *computealpha.Operation:
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) { return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
op, err := gce.serviceAlpha.GlobalOperations.Get(projectID, operationName).Do() op, err := gce.serviceAlpha.GlobalOperations.Get(gce.projectID, operationName).Do()
return convertToV1Operation(op), err return convertToV1Operation(op), err
}, mc) }, mc)
case *computebeta.Operation: case *computebeta.Operation:
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) { return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
op, err := gce.serviceBeta.GlobalOperations.Get(projectID, operationName).Do() op, err := gce.serviceBeta.GlobalOperations.Get(gce.projectID, operationName).Do()
return convertToV1Operation(op), err return convertToV1Operation(op), err
}, mc) }, mc)
case *computev1.Operation: case *computev1.Operation:
return gce.waitForOp(op.(*computev1.Operation), func(operationName string) (*computev1.Operation, error) { return gce.waitForOp(op.(*computev1.Operation), func(operationName string) (*computev1.Operation, error) {
return gce.service.GlobalOperations.Get(projectID, operationName).Do() return gce.service.GlobalOperations.Get(gce.projectID, operationName).Do()
}, mc) }, mc)
default: default:
return fmt.Errorf("unexpected type: %T", v) return fmt.Errorf("unexpected type: %T", v)
} }
} }
func (gce *GCECloud) waitForRegionOpInProject(op gceObject, projectID, region string, mc *metricContext) error { func (gce *GCECloud) waitForRegionOp(op gceObject, region string, mc *metricContext) error {
switch v := op.(type) { switch v := op.(type) {
case *computealpha.Operation: case *computealpha.Operation:
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) { return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
op, err := gce.serviceAlpha.RegionOperations.Get(projectID, region, operationName).Do() op, err := gce.serviceAlpha.RegionOperations.Get(gce.projectID, region, operationName).Do()
return convertToV1Operation(op), err return convertToV1Operation(op), err
}, mc) }, mc)
case *computebeta.Operation: case *computebeta.Operation:
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) { return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
op, err := gce.serviceBeta.RegionOperations.Get(projectID, region, operationName).Do() op, err := gce.serviceBeta.RegionOperations.Get(gce.projectID, region, operationName).Do()
return convertToV1Operation(op), err return convertToV1Operation(op), err
}, mc) }, mc)
case *computev1.Operation: case *computev1.Operation:
return gce.waitForOp(op.(*computev1.Operation), func(operationName string) (*computev1.Operation, error) { return gce.waitForOp(op.(*computev1.Operation), func(operationName string) (*computev1.Operation, error) {
return gce.service.RegionOperations.Get(projectID, region, operationName).Do() return gce.service.RegionOperations.Get(gce.projectID, region, operationName).Do()
}, mc) }, mc)
default: default:
return fmt.Errorf("unexpected type: %T", v) return fmt.Errorf("unexpected type: %T", v)
} }
} }
func (gce *GCECloud) waitForZoneOpInProject(op gceObject, projectID, zone string, mc *metricContext) error { func (gce *GCECloud) waitForZoneOp(op gceObject, zone string, mc *metricContext) error {
switch v := op.(type) { switch v := op.(type) {
case *computealpha.Operation: case *computealpha.Operation:
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) { return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
op, err := gce.serviceAlpha.ZoneOperations.Get(projectID, zone, operationName).Do() op, err := gce.serviceAlpha.ZoneOperations.Get(gce.projectID, zone, operationName).Do()
return convertToV1Operation(op), err return convertToV1Operation(op), err
}, mc) }, mc)
case *computebeta.Operation: case *computebeta.Operation:
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) { return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
op, err := gce.serviceBeta.ZoneOperations.Get(projectID, zone, operationName).Do() op, err := gce.serviceBeta.ZoneOperations.Get(gce.projectID, zone, operationName).Do()
return convertToV1Operation(op), err return convertToV1Operation(op), err
}, mc) }, mc)
case *computev1.Operation: case *computev1.Operation:
return gce.waitForOp(op.(*computev1.Operation), func(operationName string) (*computev1.Operation, error) { return gce.waitForOp(op.(*computev1.Operation), func(operationName string) (*computev1.Operation, error) {
return gce.service.ZoneOperations.Get(projectID, zone, operationName).Do() return gce.service.ZoneOperations.Get(gce.projectID, zone, operationName).Do()
}, mc) }, mc)
default: default:
return fmt.Errorf("unexpected type: %T", v) return fmt.Errorf("unexpected type: %T", v)
......
...@@ -38,13 +38,13 @@ func (gce *GCECloud) ListRoutes(clusterName string) ([]*cloudprovider.Route, err ...@@ -38,13 +38,13 @@ func (gce *GCECloud) ListRoutes(clusterName string) ([]*cloudprovider.Route, err
page := 0 page := 0
for ; page == 0 || (pageToken != "" && page < maxPages); page++ { for ; page == 0 || (pageToken != "" && page < maxPages); page++ {
mc := newRoutesMetricContext("list_page") mc := newRoutesMetricContext("list_page")
listCall := gce.service.Routes.List(gce.NetworkProjectID()) listCall := gce.service.Routes.List(gce.projectID)
prefix := truncateClusterName(clusterName) prefix := truncateClusterName(clusterName)
// Filter for routes starting with clustername AND belonging to the // Filter for routes starting with clustername AND belonging to the
// relevant gcp network AND having description = "k8s-node-route". // relevant gcp network AND having description = "k8s-node-route".
filter := "(name eq " + prefix + "-.*) " filter := "(name eq " + prefix + "-.*) "
filter = filter + "(network eq " + gce.NetworkURL() + ") " filter = filter + "(network eq " + gce.networkURL + ") "
filter = filter + "(description eq " + k8sNodeRouteTag + ")" filter = filter + "(description eq " + k8sNodeRouteTag + ")"
listCall = listCall.Filter(filter) listCall = listCall.Filter(filter)
if pageToken != "" { if pageToken != "" {
...@@ -80,11 +80,11 @@ func (gce *GCECloud) CreateRoute(clusterName string, nameHint string, route *clo ...@@ -80,11 +80,11 @@ func (gce *GCECloud) CreateRoute(clusterName string, nameHint string, route *clo
} }
mc := newRoutesMetricContext("create") mc := newRoutesMetricContext("create")
insertOp, err := gce.service.Routes.Insert(gce.NetworkProjectID(), &compute.Route{ insertOp, err := gce.service.Routes.Insert(gce.projectID, &compute.Route{
Name: routeName, Name: routeName,
DestRange: route.DestinationCIDR, DestRange: route.DestinationCIDR,
NextHopInstance: fmt.Sprintf("zones/%s/instances/%s", targetInstance.Zone, targetInstance.Name), NextHopInstance: fmt.Sprintf("zones/%s/instances/%s", targetInstance.Zone, targetInstance.Name),
Network: gce.NetworkURL(), Network: gce.networkURL,
Priority: 1000, Priority: 1000,
Description: k8sNodeRouteTag, Description: k8sNodeRouteTag,
}).Do() }).Do()
...@@ -96,16 +96,16 @@ func (gce *GCECloud) CreateRoute(clusterName string, nameHint string, route *clo ...@@ -96,16 +96,16 @@ func (gce *GCECloud) CreateRoute(clusterName string, nameHint string, route *clo
return mc.Observe(err) return mc.Observe(err)
} }
} }
return gce.waitForGlobalOpInProject(insertOp, gce.NetworkProjectID(), mc) return gce.waitForGlobalOp(insertOp, mc)
} }
func (gce *GCECloud) DeleteRoute(clusterName string, route *cloudprovider.Route) error { func (gce *GCECloud) DeleteRoute(clusterName string, route *cloudprovider.Route) error {
mc := newRoutesMetricContext("delete") mc := newRoutesMetricContext("delete")
deleteOp, err := gce.service.Routes.Delete(gce.NetworkProjectID(), route.Name).Do() deleteOp, err := gce.service.Routes.Delete(gce.projectID, route.Name).Do()
if err != nil { if err != nil {
return mc.Observe(err) return mc.Observe(err)
} }
return gce.waitForGlobalOpInProject(deleteOp, gce.NetworkProjectID(), mc) return gce.waitForGlobalOp(deleteOp, mc)
} }
func truncateClusterName(clusterName string) string { func truncateClusterName(clusterName string) string {
......
...@@ -29,43 +29,6 @@ import ( ...@@ -29,43 +29,6 @@ import (
computev1 "google.golang.org/api/compute/v1" computev1 "google.golang.org/api/compute/v1"
) )
func TestReadConfigFile(t *testing.T) {
const s = `[Global]
token-url = my-token-url
token-body = my-token-body
project-id = my-project
network-project-id = my-network-project
network-name = my-network
subnetwork-name = my-subnetwork
secondary-range-name = my-secondary-range
node-tags = my-node-tag1
node-instance-prefix = my-prefix
multizone = true
`
reader := strings.NewReader(s)
config, err := readConfig(reader)
if err != nil {
t.Fatalf("Unexpected config parsing error %v", err)
}
expected := &ConfigFile{Global: ConfigGlobal{
TokenURL: "my-token-url",
TokenBody: "my-token-body",
ProjectID: "my-project",
NetworkProjectID: "my-network-project",
NetworkName: "my-network",
SubnetworkName: "my-subnetwork",
SecondaryRangeName: "my-secondary-range",
NodeTags: []string{"my-node-tag1"},
NodeInstancePrefix: "my-prefix",
Multizone: true,
}}
if !reflect.DeepEqual(expected, config) {
t.Fatalf("Expected config file values to be read into ConfigFile struct. \nExpected:\n%+v\nActual:\n%+v", expected, config)
}
}
func TestExtraKeyInConfig(t *testing.T) { func TestExtraKeyInConfig(t *testing.T) {
const s = `[Global] const s = `[Global]
project-id = my-project project-id = my-project
...@@ -300,8 +263,23 @@ func TestSplitProviderID(t *testing.T) { ...@@ -300,8 +263,23 @@ func TestSplitProviderID(t *testing.T) {
} }
} }
func TestGenerateCloudConfigs(t *testing.T) { type generateConfigParams struct {
configBoilerplate := ConfigGlobal{ TokenURL string
TokenBody string
ProjectID string
NetworkName string
SubnetworkName string
SecondaryRangeName string
NodeTags []string
NodeInstancePrefix string
Multizone bool
ApiEndpoint string
LocalZone string
AlphaFeatures []string
}
func newGenerateConfigDefaults() *generateConfigParams {
return &generateConfigParams{
TokenURL: "", TokenURL: "",
TokenBody: "", TokenBody: "",
ProjectID: "project-id", ProjectID: "project-id",
...@@ -315,147 +293,197 @@ func TestGenerateCloudConfigs(t *testing.T) { ...@@ -315,147 +293,197 @@ func TestGenerateCloudConfigs(t *testing.T) {
LocalZone: "us-central1-a", LocalZone: "us-central1-a",
AlphaFeatures: []string{}, AlphaFeatures: []string{},
} }
}
cloudBoilerplate := CloudConfig{ func TestGenerateCloudConfigs(t *testing.T) {
ApiEndpoint: "",
ProjectID: "project-id",
NetworkProjectID: "",
Region: "us-central1",
Zone: "us-central1-a",
ManagedZones: []string{"us-central1-a"},
NetworkName: "network-name",
SubnetworkName: "",
NetworkURL: "",
SubnetworkURL: "",
SecondaryRangeName: "",
NodeTags: []string{"node-tag"},
TokenSource: google.ComputeTokenSource(""),
NodeInstancePrefix: "node-prefix",
UseMetadataServer: true,
AlphaFeatureGate: &AlphaFeatureGate{map[string]bool{}},
}
testCases := []struct { testCases := []struct {
name string TokenURL string
config func() ConfigGlobal TokenBody string
cloud func() CloudConfig ProjectID string
NetworkName string
SubnetworkName string
NodeTags []string
NodeInstancePrefix string
Multizone bool
ApiEndpoint string
LocalZone string
cloudConfig *CloudConfig
AlphaFeatures []string
}{ }{
// default config
{ {
name: "Empty Config", cloudConfig: &CloudConfig{
config: func() ConfigGlobal { return configBoilerplate }, ApiEndpoint: "",
cloud: func() CloudConfig { return cloudBoilerplate }, ProjectID: "project-id",
}, Region: "us-central1",
{ Zone: "us-central1-a",
name: "Nil token URL", ManagedZones: []string{"us-central1-a"},
config: func() ConfigGlobal { NetworkURL: "https://www.googleapis.com/compute/v1/projects/project-id/global/networks/network-name",
v := configBoilerplate SubnetworkURL: "",
v.TokenURL = "nil" NodeTags: []string{"node-tag"},
return v NodeInstancePrefix: "node-prefix",
}, TokenSource: google.ComputeTokenSource(""),
cloud: func() CloudConfig { UseMetadataServer: true,
v := cloudBoilerplate AlphaFeatureGate: &AlphaFeatureGate{map[string]bool{}},
v.TokenSource = nil
return v
}, },
}, },
// nil token source
{ {
name: "Network Project ID", TokenURL: "nil",
config: func() ConfigGlobal { cloudConfig: &CloudConfig{
v := configBoilerplate ApiEndpoint: "",
v.NetworkProjectID = "my-awesome-project" ProjectID: "project-id",
return v Region: "us-central1",
}, Zone: "us-central1-a",
cloud: func() CloudConfig { ManagedZones: []string{"us-central1-a"},
v := cloudBoilerplate NetworkURL: "https://www.googleapis.com/compute/v1/projects/project-id/global/networks/network-name",
v.NetworkProjectID = "my-awesome-project" SubnetworkURL: "",
return v NodeTags: []string{"node-tag"},
NodeInstancePrefix: "node-prefix",
TokenSource: nil,
UseMetadataServer: true,
AlphaFeatureGate: &AlphaFeatureGate{map[string]bool{}},
}, },
}, },
// specified api endpoint
{ {
name: "Specified API Endpint", ApiEndpoint: "https://www.googleapis.com/compute/staging_v1/",
config: func() ConfigGlobal { cloudConfig: &CloudConfig{
v := configBoilerplate ApiEndpoint: "https://www.googleapis.com/compute/staging_v1/",
v.ApiEndpoint = "https://www.googleapis.com/compute/staging_v1/" ProjectID: "project-id",
return v Region: "us-central1",
}, Zone: "us-central1-a",
cloud: func() CloudConfig { ManagedZones: []string{"us-central1-a"},
v := cloudBoilerplate NetworkURL: "https://www.googleapis.com/compute/staging_v1/projects/project-id/global/networks/network-name",
v.ApiEndpoint = "https://www.googleapis.com/compute/staging_v1/" SubnetworkURL: "",
return v NodeTags: []string{"node-tag"},
NodeInstancePrefix: "node-prefix",
TokenSource: google.ComputeTokenSource(""),
UseMetadataServer: true,
AlphaFeatureGate: &AlphaFeatureGate{map[string]bool{}},
}, },
}, },
// fqdn subnetname
{ {
name: "Network & Subnetwork names", SubnetworkName: "https://www.googleapis.com/compute/v1/projects/project-id/regions/us-central1/subnetworks/subnetwork-name",
config: func() ConfigGlobal { cloudConfig: &CloudConfig{
v := configBoilerplate ApiEndpoint: "",
v.NetworkName = "my-network" ProjectID: "project-id",
v.SubnetworkName = "my-subnetwork" Region: "us-central1",
return v Zone: "us-central1-a",
}, ManagedZones: []string{"us-central1-a"},
cloud: func() CloudConfig { NetworkURL: "https://www.googleapis.com/compute/v1/projects/project-id/global/networks/network-name",
v := cloudBoilerplate SubnetworkURL: "https://www.googleapis.com/compute/v1/projects/project-id/regions/us-central1/subnetworks/subnetwork-name",
v.NetworkName = "my-network" NodeTags: []string{"node-tag"},
v.SubnetworkName = "my-subnetwork" NodeInstancePrefix: "node-prefix",
return v TokenSource: google.ComputeTokenSource(""),
UseMetadataServer: true,
AlphaFeatureGate: &AlphaFeatureGate{map[string]bool{}},
}, },
}, },
// subnetname
{ {
name: "Network & Subnetwork URLs", SubnetworkName: "subnetwork-name",
config: func() ConfigGlobal { cloudConfig: &CloudConfig{
v := configBoilerplate ApiEndpoint: "",
v.NetworkName = "https://www.googleapis.com/compute/v1/projects/project-id/global/networks/my-network" ProjectID: "project-id",
v.SubnetworkName = "https://www.googleapis.com/compute/v1/projects/project-id/regions/us-central1/subnetworks/my-subnetwork" Region: "us-central1",
return v Zone: "us-central1-a",
}, ManagedZones: []string{"us-central1-a"},
cloud: func() CloudConfig { NetworkURL: "https://www.googleapis.com/compute/v1/projects/project-id/global/networks/network-name",
v := cloudBoilerplate SubnetworkURL: "https://www.googleapis.com/compute/v1/projects/project-id/regions/us-central1/subnetworks/subnetwork-name",
v.NetworkName = "" NodeTags: []string{"node-tag"},
v.SubnetworkName = "" NodeInstancePrefix: "node-prefix",
v.NetworkURL = "https://www.googleapis.com/compute/v1/projects/project-id/global/networks/my-network" TokenSource: google.ComputeTokenSource(""),
v.SubnetworkURL = "https://www.googleapis.com/compute/v1/projects/project-id/regions/us-central1/subnetworks/my-subnetwork" UseMetadataServer: true,
return v AlphaFeatureGate: &AlphaFeatureGate{map[string]bool{}},
},
},
{
name: "Multizone",
config: func() ConfigGlobal {
v := configBoilerplate
v.Multizone = true
return v
},
cloud: func() CloudConfig {
v := cloudBoilerplate
v.ManagedZones = nil
return v
}, },
}, },
// multi zone
{ {
name: "Secondary Range Name", Multizone: true,
config: func() ConfigGlobal { cloudConfig: &CloudConfig{
v := configBoilerplate ApiEndpoint: "",
v.SecondaryRangeName = "my-secondary" ProjectID: "project-id",
return v Region: "us-central1",
}, Zone: "us-central1-a",
cloud: func() CloudConfig { ManagedZones: nil,
v := cloudBoilerplate NetworkURL: "https://www.googleapis.com/compute/v1/projects/project-id/global/networks/network-name",
v.SecondaryRangeName = "my-secondary" SubnetworkURL: "",
return v NodeTags: []string{"node-tag"},
NodeInstancePrefix: "node-prefix",
TokenSource: google.ComputeTokenSource(""),
UseMetadataServer: true,
AlphaFeatureGate: &AlphaFeatureGate{map[string]bool{}},
}, },
}, },
} }
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { config := newGenerateConfigDefaults()
resultCloud, err := generateCloudConfig(&ConfigFile{Global: tc.config()}) config.Multizone = tc.Multizone
if err != nil { config.ApiEndpoint = tc.ApiEndpoint
t.Fatalf("Unexpect error: %v", err) config.AlphaFeatures = tc.AlphaFeatures
} config.TokenBody = tc.TokenBody
if tc.TokenURL != "" {
config.TokenURL = tc.TokenURL
}
if tc.ProjectID != "" {
config.ProjectID = tc.ProjectID
}
if tc.NetworkName != "" {
config.NetworkName = tc.NetworkName
}
if tc.SubnetworkName != "" {
config.SubnetworkName = tc.SubnetworkName
}
if len(tc.NodeTags) > 0 {
config.NodeTags = tc.NodeTags
}
if tc.NodeInstancePrefix != "" {
config.NodeInstancePrefix = tc.NodeInstancePrefix
}
if tc.LocalZone != "" {
config.LocalZone = tc.LocalZone
}
v := tc.cloud() cloudConfig, err := generateCloudConfig(&ConfigFile{
if !reflect.DeepEqual(*resultCloud, v) { Global: struct {
t.Errorf("Got: \n%v\nWant\n%v\n", v, *resultCloud) TokenURL string `gcfg:"token-url"`
} TokenBody string `gcfg:"token-body"`
ProjectID string `gcfg:"project-id"`
NetworkName string `gcfg:"network-name"`
SubnetworkName string `gcfg:"subnetwork-name"`
SecondaryRangeName string `gcfg:"secondary-range-name"`
NodeTags []string `gcfg:"node-tags"`
NodeInstancePrefix string `gcfg:"node-instance-prefix"`
Multizone bool `gcfg:"multizone"`
ApiEndpoint string `gcfg:"api-endpoint"`
LocalZone string `gcfg:"local-zone"`
AlphaFeatures []string `gcfg:"alpha-features"`
}{
TokenURL: config.TokenURL,
TokenBody: config.TokenBody,
ProjectID: config.ProjectID,
NetworkName: config.NetworkName,
SubnetworkName: config.SubnetworkName,
SecondaryRangeName: config.SecondaryRangeName,
NodeTags: config.NodeTags,
NodeInstancePrefix: config.NodeInstancePrefix,
Multizone: config.Multizone,
ApiEndpoint: config.ApiEndpoint,
LocalZone: config.LocalZone,
AlphaFeatures: config.AlphaFeatures,
},
}) })
if err != nil {
t.Fatalf("Unexpect error: %v", err)
}
if !reflect.DeepEqual(cloudConfig, tc.cloudConfig) {
t.Errorf("Got %v, want %v", cloudConfig, tc.cloudConfig)
}
} }
} }
......
...@@ -110,6 +110,12 @@ func (i *Instances) ExternalID(name types.NodeName) (string, error) { ...@@ -110,6 +110,12 @@ func (i *Instances) ExternalID(name types.NodeName) (string, error) {
return srv.ID, nil return srv.ID, nil
} }
// InstanceExistsByProviderID returns true if the instance with the given provider id still exists and is running.
// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
func (i *Instances) InstanceExistsByProviderID(providerID string) (bool, error) {
return false, errors.New("unimplemented")
}
// InstanceID returns the kubelet's cloud provider ID. // InstanceID returns the kubelet's cloud provider ID.
func (os *OpenStack) InstanceID() (string, error) { func (os *OpenStack) InstanceID() (string, error) {
if len(os.localInstanceID) == 0 { if len(os.localInstanceID) == 0 {
......
...@@ -211,6 +211,12 @@ func (v *OVirtCloud) ExternalID(nodeName types.NodeName) (string, error) { ...@@ -211,6 +211,12 @@ func (v *OVirtCloud) ExternalID(nodeName types.NodeName) (string, error) {
return instance.UUID, nil return instance.UUID, nil
} }
// InstanceExistsByProviderID returns true if the instance with the given provider id still exists and is running.
// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
func (v *OVirtCloud) InstanceExistsByProviderID(providerID string) (bool, error) {
return false, errors.New("unimplemented")
}
// InstanceID returns the cloud provider ID of the node with the specified NodeName. // InstanceID returns the cloud provider ID of the node with the specified NodeName.
func (v *OVirtCloud) InstanceID(nodeName types.NodeName) (string, error) { func (v *OVirtCloud) InstanceID(nodeName types.NodeName) (string, error) {
name := mapNodeNameToInstanceName(nodeName) name := mapNodeNameToInstanceName(nodeName)
......
...@@ -470,6 +470,12 @@ func (pc *PCCloud) ExternalID(nodeName k8stypes.NodeName) (string, error) { ...@@ -470,6 +470,12 @@ func (pc *PCCloud) ExternalID(nodeName k8stypes.NodeName) (string, error) {
} }
} }
// InstanceExistsByProviderID returns true if the instance with the given provider id still exists and is running.
// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
func (pc *PCCloud) InstanceExistsByProviderID(providerID string) (bool, error) {
return false, errors.New("unimplemented")
}
// InstanceID returns the cloud provider ID of the specified instance. // InstanceID returns the cloud provider ID of the specified instance.
func (pc *PCCloud) InstanceID(nodeName k8stypes.NodeName) (string, error) { func (pc *PCCloud) InstanceID(nodeName k8stypes.NodeName) (string, error) {
name := string(nodeName) name := string(nodeName)
......
...@@ -436,6 +436,12 @@ func (i *Instances) ExternalID(nodeName types.NodeName) (string, error) { ...@@ -436,6 +436,12 @@ func (i *Instances) ExternalID(nodeName types.NodeName) (string, error) {
return probeInstanceID(i.compute, serverName) return probeInstanceID(i.compute, serverName)
} }
// InstanceExistsByProviderID returns true if the instance with the given provider id still exists and is running.
// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
func (i *Instances) InstanceExistsByProviderID(providerID string) (bool, error) {
return false, errors.New("unimplemented")
}
// InstanceID returns the cloud provider ID of the kubelet's instance. // InstanceID returns the cloud provider ID of the kubelet's instance.
func (rs *Rackspace) InstanceID() (string, error) { func (rs *Rackspace) InstanceID() (string, error) {
return readInstanceID() return readInstanceID()
......
...@@ -376,6 +376,12 @@ func (vs *VSphere) ExternalID(nodeName k8stypes.NodeName) (string, error) { ...@@ -376,6 +376,12 @@ func (vs *VSphere) ExternalID(nodeName k8stypes.NodeName) (string, error) {
return vs.InstanceID(nodeName) return vs.InstanceID(nodeName)
} }
// InstanceExistsByProviderID returns true if the instance with the given provider id still exists and is running.
// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
func (vs *VSphere) InstanceExistsByProviderID(providerID string) (bool, error) {
return false, errors.New("unimplemented")
}
// InstanceID returns the cloud provider ID of the node with the specified Name. // InstanceID returns the cloud provider ID of the node with the specified Name.
func (vs *VSphere) InstanceID(nodeName k8stypes.NodeName) (string, error) { func (vs *VSphere) InstanceID(nodeName k8stypes.NodeName) (string, error) {
if vs.localInstanceID == nodeNameToVMName(nodeName) { if vs.localInstanceID == nodeNameToVMName(nodeName) {
......
...@@ -237,26 +237,36 @@ func (cnc *CloudNodeController) MonitorNode() { ...@@ -237,26 +237,36 @@ func (cnc *CloudNodeController) MonitorNode() {
if currentReadyCondition.Status != v1.ConditionTrue { if currentReadyCondition.Status != v1.ConditionTrue {
// Check with the cloud provider to see if the node still exists. If it // Check with the cloud provider to see if the node still exists. If it
// doesn't, delete the node immediately. // doesn't, delete the node immediately.
if _, err := instances.ExternalID(types.NodeName(node.Name)); err != nil { exists, err := ensureNodeExistsByProviderIDOrExternalID(instances, node)
if err == cloudprovider.InstanceNotFound { if err != nil {
glog.V(2).Infof("Deleting node no longer present in cloud provider: %s", node.Name) glog.Errorf("Error getting data for node %s from cloud: %v", node.Name, err)
ref := &v1.ObjectReference{ continue
Kind: "Node", }
Name: node.Name,
UID: types.UID(node.UID), if exists {
Namespace: "", // Continue checking the remaining nodes since the current one is fine.
} continue
glog.V(2).Infof("Recording %s event message for node %s", "DeletingNode", node.Name) }
cnc.recorder.Eventf(ref, v1.EventTypeNormal, fmt.Sprintf("Deleting Node %v because it's not present according to cloud provider", node.Name), "Node %s event: %s", node.Name, "DeletingNode")
go func(nodeName string) { glog.V(2).Infof("Deleting node since it is no longer present in cloud provider: %s", node.Name)
defer utilruntime.HandleCrash()
if err := cnc.kubeClient.CoreV1().Nodes().Delete(node.Name, nil); err != nil { ref := &v1.ObjectReference{
glog.Errorf("unable to delete node %q: %v", node.Name, err) Kind: "Node",
} Name: node.Name,
}(node.Name) UID: types.UID(node.UID),
} Namespace: "",
glog.Errorf("Error getting node data from cloud: %v", err)
} }
glog.V(2).Infof("Recording %s event message for node %s", "DeletingNode", node.Name)
cnc.recorder.Eventf(ref, v1.EventTypeNormal, fmt.Sprintf("Deleting Node %v because it's not present according to cloud provider", node.Name), "Node %s event: %s", node.Name, "DeletingNode")
go func(nodeName string) {
defer utilruntime.HandleCrash()
if err := cnc.kubeClient.CoreV1().Nodes().Delete(nodeName, nil); err != nil {
glog.Errorf("unable to delete node %q: %v", nodeName, err)
}
}(node.Name)
} }
} }
} }
...@@ -384,6 +394,25 @@ func excludeTaintFromList(taints []v1.Taint, toExclude v1.Taint) []v1.Taint { ...@@ -384,6 +394,25 @@ func excludeTaintFromList(taints []v1.Taint, toExclude v1.Taint) []v1.Taint {
return newTaints return newTaints
} }
// ensureNodeExistsByProviderIDOrExternalID first checks if the instance exists by the provider id and then by calling external id with node name
func ensureNodeExistsByProviderIDOrExternalID(instances cloudprovider.Instances, node *v1.Node) (bool, error) {
exists, err := instances.InstanceExistsByProviderID(node.Spec.ProviderID)
if err != nil {
providerIDErr := err
_, err = instances.ExternalID(types.NodeName(node.Name))
if err == nil {
return true, nil
}
if err == cloudprovider.InstanceNotFound {
return false, nil
}
return false, fmt.Errorf("InstanceExistsByProviderID: Error fetching by providerID: %v Error fetching by NodeName: %v", providerIDErr, err)
}
return exists, nil
}
func getNodeAddressesByProviderIDOrName(instances cloudprovider.Instances, node *v1.Node) ([]v1.NodeAddress, error) { func getNodeAddressesByProviderIDOrName(instances cloudprovider.Instances, node *v1.Node) ([]v1.NodeAddress, error) {
nodeAddresses, err := instances.NodeAddressesByProviderID(node.Spec.ProviderID) nodeAddresses, err := instances.NodeAddressesByProviderID(node.Spec.ProviderID)
if err != nil { if err != nil {
......
...@@ -17,6 +17,8 @@ limitations under the License. ...@@ -17,6 +17,8 @@ limitations under the License.
package cloud package cloud
import ( import (
"errors"
"reflect"
"testing" "testing"
"time" "time"
...@@ -39,6 +41,119 @@ import ( ...@@ -39,6 +41,119 @@ import (
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm" "k8s.io/kubernetes/plugin/pkg/scheduler/algorithm"
) )
func TestEnsureNodeExistsByProviderIDOrNodeName(t *testing.T) {
testCases := []struct {
testName string
node *v1.Node
expectedCalls []string
existsByNodeName bool
existsByProviderID bool
nodeNameErr error
providerIDErr error
}{
{
testName: "node exists by provider id",
existsByProviderID: true,
providerIDErr: nil,
existsByNodeName: false,
nodeNameErr: errors.New("unimplemented"),
expectedCalls: []string{"instance-exists-by-provider-id"},
node: &v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node0",
},
Spec: v1.NodeSpec{
ProviderID: "node0",
},
},
},
{
testName: "does not exist by provider id",
existsByProviderID: false,
providerIDErr: nil,
existsByNodeName: false,
nodeNameErr: errors.New("unimplemented"),
expectedCalls: []string{"instance-exists-by-provider-id"},
node: &v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node0",
},
Spec: v1.NodeSpec{
ProviderID: "node0",
},
},
},
{
testName: "node exists by node name",
existsByProviderID: false,
providerIDErr: errors.New("unimplemented"),
existsByNodeName: true,
nodeNameErr: nil,
expectedCalls: []string{"instance-exists-by-provider-id", "external-id"},
node: &v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node0",
},
Spec: v1.NodeSpec{
ProviderID: "node0",
},
},
},
{
testName: "does not exist by node name",
existsByProviderID: false,
providerIDErr: errors.New("unimplemented"),
existsByNodeName: false,
nodeNameErr: cloudprovider.InstanceNotFound,
expectedCalls: []string{"instance-exists-by-provider-id", "external-id"},
node: &v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node0",
},
Spec: v1.NodeSpec{
ProviderID: "node0",
},
},
},
}
for _, tc := range testCases {
t.Run(tc.testName, func(t *testing.T) {
fc := &fakecloud.FakeCloud{
Exists: tc.existsByNodeName,
ExistsByProviderID: tc.existsByProviderID,
Err: tc.nodeNameErr,
ErrByProviderID: tc.providerIDErr,
}
instances, _ := fc.Instances()
exists, err := ensureNodeExistsByProviderIDOrExternalID(instances, tc.node)
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(fc.Calls, tc.expectedCalls) {
t.Errorf("expected cloud provider methods `%v` to be called but `%v` was called ", tc.expectedCalls, fc.Calls)
}
if tc.existsByProviderID && tc.existsByProviderID != exists {
t.Errorf("expected exist by provider id to be `%t` but got `%t`", tc.existsByProviderID, exists)
}
if tc.existsByNodeName && tc.existsByNodeName != exists {
t.Errorf("expected exist by node name to be `%t` but got `%t`", tc.existsByNodeName, exists)
}
if !tc.existsByNodeName && !tc.existsByProviderID && exists {
t.Error("node is not supposed to exist")
}
})
}
}
// This test checks that the node is deleted when kubelet stops reporting // This test checks that the node is deleted when kubelet stops reporting
// and cloud provider says node is gone // and cloud provider says node is gone
func TestNodeDeleted(t *testing.T) { func TestNodeDeleted(t *testing.T) {
...@@ -105,9 +220,12 @@ func TestNodeDeleted(t *testing.T) { ...@@ -105,9 +220,12 @@ func TestNodeDeleted(t *testing.T) {
eventBroadcaster := record.NewBroadcaster() eventBroadcaster := record.NewBroadcaster()
cloudNodeController := &CloudNodeController{ cloudNodeController := &CloudNodeController{
kubeClient: fnh, kubeClient: fnh,
nodeInformer: factory.Core().V1().Nodes(), nodeInformer: factory.Core().V1().Nodes(),
cloud: &fakecloud.FakeCloud{Err: cloudprovider.InstanceNotFound}, cloud: &fakecloud.FakeCloud{
ExistsByProviderID: false,
Err: nil,
},
nodeMonitorPeriod: 1 * time.Second, nodeMonitorPeriod: 1 * time.Second,
recorder: eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "cloud-controller-manager"}), recorder: eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "cloud-controller-manager"}),
nodeStatusUpdateFrequency: 1 * time.Second, nodeStatusUpdateFrequency: 1 * time.Second,
...@@ -520,7 +638,8 @@ func TestNodeAddresses(t *testing.T) { ...@@ -520,7 +638,8 @@ func TestNodeAddresses(t *testing.T) {
FailureDomain: "us-west-1a", FailureDomain: "us-west-1a",
Region: "us-west", Region: "us-west",
}, },
Err: nil, ExistsByProviderID: true,
Err: nil,
} }
eventBroadcaster := record.NewBroadcaster() eventBroadcaster := record.NewBroadcaster()
...@@ -639,7 +758,8 @@ func TestNodeProvidedIPAddresses(t *testing.T) { ...@@ -639,7 +758,8 @@ func TestNodeProvidedIPAddresses(t *testing.T) {
FailureDomain: "us-west-1a", FailureDomain: "us-west-1a",
Region: "us-west", Region: "us-west",
}, },
Err: nil, ExistsByProviderID: true,
Err: nil,
} }
eventBroadcaster := record.NewBroadcaster() eventBroadcaster := record.NewBroadcaster()
......
...@@ -183,14 +183,16 @@ func (nm *NamespaceController) Run(workers int, stopCh <-chan struct{}) { ...@@ -183,14 +183,16 @@ func (nm *NamespaceController) Run(workers int, stopCh <-chan struct{}) {
defer utilruntime.HandleCrash() defer utilruntime.HandleCrash()
defer nm.queue.ShutDown() defer nm.queue.ShutDown()
glog.Infof("Starting namespace controller")
defer glog.Infof("Shutting down namespace controller")
if !controller.WaitForCacheSync("namespace", stopCh, nm.listerSynced) { if !controller.WaitForCacheSync("namespace", stopCh, nm.listerSynced) {
return return
} }
glog.V(5).Info("Starting workers") glog.V(5).Info("Starting workers of namespace controller")
for i := 0; i < workers; i++ { for i := 0; i < workers; i++ {
go wait.Until(nm.worker, time.Second, stopCh) go wait.Until(nm.worker, time.Second, stopCh)
} }
<-stopCh <-stopCh
glog.V(1).Infof("Shutting down")
} }
...@@ -45,6 +45,7 @@ go_test( ...@@ -45,6 +45,7 @@ go_test(
"//pkg/controller:go_default_library", "//pkg/controller:go_default_library",
"//pkg/controller/volume/attachdetach/cache:go_default_library", "//pkg/controller/volume/attachdetach/cache:go_default_library",
"//pkg/controller/volume/attachdetach/testing:go_default_library", "//pkg/controller/volume/attachdetach/testing:go_default_library",
"//pkg/volume:go_default_library",
"//vendor/k8s.io/api/core/v1: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/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
......
...@@ -97,6 +97,7 @@ func NewAttachDetachController( ...@@ -97,6 +97,7 @@ func NewAttachDetachController(
pvInformer coreinformers.PersistentVolumeInformer, pvInformer coreinformers.PersistentVolumeInformer,
cloud cloudprovider.Interface, cloud cloudprovider.Interface,
plugins []volume.VolumePlugin, plugins []volume.VolumePlugin,
prober volume.DynamicPluginProber,
disableReconciliationSync bool, disableReconciliationSync bool,
reconcilerSyncDuration time.Duration, reconcilerSyncDuration time.Duration,
timerConfig TimerConfig) (AttachDetachController, error) { timerConfig TimerConfig) (AttachDetachController, error) {
...@@ -127,7 +128,7 @@ func NewAttachDetachController( ...@@ -127,7 +128,7 @@ func NewAttachDetachController(
cloud: cloud, cloud: cloud,
} }
if err := adc.volumePluginMgr.InitPlugins(plugins, adc); err != nil { if err := adc.volumePluginMgr.InitPlugins(plugins, prober, adc); err != nil {
return nil, fmt.Errorf("Could not initialize volume plugins for Attach/Detach Controller: %+v", err) return nil, fmt.Errorf("Could not initialize volume plugins for Attach/Detach Controller: %+v", err)
} }
......
...@@ -29,6 +29,7 @@ import ( ...@@ -29,6 +29,7 @@ import (
"k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache" "k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache"
controllervolumetesting "k8s.io/kubernetes/pkg/controller/volume/attachdetach/testing" controllervolumetesting "k8s.io/kubernetes/pkg/controller/volume/attachdetach/testing"
"k8s.io/kubernetes/pkg/volume"
) )
func Test_NewAttachDetachController_Positive(t *testing.T) { func Test_NewAttachDetachController_Positive(t *testing.T) {
...@@ -45,6 +46,7 @@ func Test_NewAttachDetachController_Positive(t *testing.T) { ...@@ -45,6 +46,7 @@ func Test_NewAttachDetachController_Positive(t *testing.T) {
informerFactory.Core().V1().PersistentVolumes(), informerFactory.Core().V1().PersistentVolumes(),
nil, /* cloud */ nil, /* cloud */
nil, /* plugins */ nil, /* plugins */
nil, /* prober */
false, false,
5*time.Second, 5*time.Second,
DefaultTimerConfig) DefaultTimerConfig)
...@@ -79,8 +81,9 @@ func Test_AttachDetachControllerStateOfWolrdPopulators_Positive(t *testing.T) { ...@@ -79,8 +81,9 @@ func Test_AttachDetachControllerStateOfWolrdPopulators_Positive(t *testing.T) {
// Act // Act
plugins := controllervolumetesting.CreateTestPlugin() plugins := controllervolumetesting.CreateTestPlugin()
var prober volume.DynamicPluginProber = nil // TODO (#51147) inject mock
if err := adc.volumePluginMgr.InitPlugins(plugins, adc); err != nil { if err := adc.volumePluginMgr.InitPlugins(plugins, prober, adc); err != nil {
t.Fatalf("Could not initialize volume plugins for Attach/Detach Controller: %+v", err) t.Fatalf("Could not initialize volume plugins for Attach/Detach Controller: %+v", err)
} }
...@@ -141,6 +144,7 @@ func attachDetachRecoveryTestCase(t *testing.T, extraPods1 []*v1.Pod, extraPods2 ...@@ -141,6 +144,7 @@ func attachDetachRecoveryTestCase(t *testing.T, extraPods1 []*v1.Pod, extraPods2
informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, time.Second*1) informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, time.Second*1)
//informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, time.Second*1) //informerFactory := informers.NewSharedInformerFactory(fakeKubeClient, time.Second*1)
plugins := controllervolumetesting.CreateTestPlugin() plugins := controllervolumetesting.CreateTestPlugin()
var prober volume.DynamicPluginProber = nil // TODO (#51147) inject mock
nodeInformer := informerFactory.Core().V1().Nodes().Informer() nodeInformer := informerFactory.Core().V1().Nodes().Informer()
podInformer := informerFactory.Core().V1().Pods().Informer() podInformer := informerFactory.Core().V1().Pods().Informer()
var podsNum, extraPodsNum, nodesNum, i int var podsNum, extraPodsNum, nodesNum, i int
...@@ -212,6 +216,7 @@ func attachDetachRecoveryTestCase(t *testing.T, extraPods1 []*v1.Pod, extraPods2 ...@@ -212,6 +216,7 @@ func attachDetachRecoveryTestCase(t *testing.T, extraPods1 []*v1.Pod, extraPods2
informerFactory.Core().V1().PersistentVolumes(), informerFactory.Core().V1().PersistentVolumes(),
nil, /* cloud */ nil, /* cloud */
plugins, plugins,
prober,
false, false,
1*time.Second, 1*time.Second,
DefaultTimerConfig) DefaultTimerConfig)
......
...@@ -827,7 +827,7 @@ func wrapTestWithPluginCalls(expectedRecycleCalls, expectedDeleteCalls []error, ...@@ -827,7 +827,7 @@ func wrapTestWithPluginCalls(expectedRecycleCalls, expectedDeleteCalls []error,
deleteCalls: expectedDeleteCalls, deleteCalls: expectedDeleteCalls,
provisionCalls: expectedProvisionCalls, provisionCalls: expectedProvisionCalls,
} }
ctrl.volumePluginMgr.InitPlugins([]vol.VolumePlugin{plugin}, ctrl) ctrl.volumePluginMgr.InitPlugins([]vol.VolumePlugin{plugin}, nil /* prober */, ctrl)
return toWrap(ctrl, reactor, test) return toWrap(ctrl, reactor, test)
} }
} }
......
...@@ -90,7 +90,8 @@ func NewController(p ControllerParameters) (*PersistentVolumeController, error) ...@@ -90,7 +90,8 @@ func NewController(p ControllerParameters) (*PersistentVolumeController, error)
resyncPeriod: p.SyncPeriod, resyncPeriod: p.SyncPeriod,
} }
if err := controller.volumePluginMgr.InitPlugins(p.VolumePlugins, controller); err != nil { // Prober is nil because PV is not aware of Flexvolume.
if err := controller.volumePluginMgr.InitPlugins(p.VolumePlugins, nil /* prober */, controller); err != nil {
return nil, fmt.Errorf("Could not initialize volume plugins for PersistentVolume Controller: %v", err) return nil, fmt.Errorf("Could not initialize volume plugins for PersistentVolume Controller: %v", err)
} }
......
...@@ -214,7 +214,7 @@ func (s *PodDisruptionBudgetV2Generator) validate() error { ...@@ -214,7 +214,7 @@ func (s *PodDisruptionBudgetV2Generator) validate() error {
return fmt.Errorf("a selector must be specified") return fmt.Errorf("a selector must be specified")
} }
if len(s.MaxUnavailable) > 0 && len(s.MinAvailable) > 0 { if len(s.MaxUnavailable) > 0 && len(s.MinAvailable) > 0 {
return fmt.Errorf("exactly one of min-available and max-available must be specified") return fmt.Errorf("min-available and max-unavailable cannot be both specified")
} }
return nil return nil
} }
...@@ -35,18 +35,9 @@ func CapacityFromMachineInfo(info *cadvisorapi.MachineInfo) v1.ResourceList { ...@@ -35,18 +35,9 @@ func CapacityFromMachineInfo(info *cadvisorapi.MachineInfo) v1.ResourceList {
return c return c
} }
func StorageScratchCapacityFromFsInfo(info cadvisorapi2.FsInfo) v1.ResourceList { func EphemeralStorageCapacityFromFsInfo(info cadvisorapi2.FsInfo) v1.ResourceList {
c := v1.ResourceList{ c := v1.ResourceList{
v1.ResourceStorageScratch: *resource.NewQuantity( v1.ResourceEphemeralStorage: *resource.NewQuantity(
int64(info.Capacity),
resource.BinarySI),
}
return c
}
func StorageOverlayCapacityFromFsInfo(info cadvisorapi2.FsInfo) v1.ResourceList {
c := v1.ResourceList{
v1.ResourceStorageOverlay: *resource.NewQuantity(
int64(info.Capacity), int64(info.Capacity),
resource.BinarySI), resource.BinarySI),
} }
......
...@@ -55,7 +55,6 @@ go_library( ...@@ -55,7 +55,6 @@ go_library(
"//pkg/util/procfs:go_default_library", "//pkg/util/procfs:go_default_library",
"//pkg/util/sysctl:go_default_library", "//pkg/util/sysctl:go_default_library",
"//pkg/util/version:go_default_library", "//pkg/util/version:go_default_library",
"//vendor/github.com/google/cadvisor/info/v2:go_default_library",
"//vendor/github.com/opencontainers/runc/libcontainer/cgroups:go_default_library", "//vendor/github.com/opencontainers/runc/libcontainer/cgroups:go_default_library",
"//vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs:go_default_library", "//vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs:go_default_library",
"//vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd:go_default_library", "//vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd:go_default_library",
......
...@@ -233,7 +233,7 @@ func (m *cgroupManagerImpl) Exists(name CgroupName) bool { ...@@ -233,7 +233,7 @@ func (m *cgroupManagerImpl) Exists(name CgroupName) bool {
// scoped to the set control groups it understands. this is being discussed // scoped to the set control groups it understands. this is being discussed
// in https://github.com/opencontainers/runc/issues/1440 // in https://github.com/opencontainers/runc/issues/1440
// once resolved, we can remove this code. // once resolved, we can remove this code.
whitelistControllers := sets.NewString("cpu", "cpuacct", "cpuset", "memory", "hugetlb", "systemd") whitelistControllers := sets.NewString("cpu", "cpuacct", "cpuset", "memory", "systemd")
// If even one cgroup path doesn't exist, then the cgroup doesn't exist. // If even one cgroup path doesn't exist, then the cgroup doesn't exist.
for controller, path := range cgroupPaths { for controller, path := range cgroupPaths {
......
...@@ -30,7 +30,6 @@ import ( ...@@ -30,7 +30,6 @@ import (
"time" "time"
"github.com/golang/glog" "github.com/golang/glog"
cadvisorapiv2 "github.com/google/cadvisor/info/v2"
"github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/cgroups/fs" "github.com/opencontainers/runc/libcontainer/cgroups/fs"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
...@@ -552,24 +551,11 @@ func (cm *containerManagerImpl) setFsCapacity() error { ...@@ -552,24 +551,11 @@ func (cm *containerManagerImpl) setFsCapacity() error {
if err != nil { if err != nil {
return fmt.Errorf("Fail to get rootfs information %v", err) return fmt.Errorf("Fail to get rootfs information %v", err)
} }
hasDedicatedImageFs, _ := cm.cadvisorInterface.HasDedicatedImageFs()
var imagesfs cadvisorapiv2.FsInfo
if hasDedicatedImageFs {
imagesfs, err = cm.cadvisorInterface.ImagesFsInfo()
if err != nil {
return fmt.Errorf("Fail to get imagefs information %v", err)
}
}
cm.Lock() cm.Lock()
for rName, rCap := range cadvisor.StorageScratchCapacityFromFsInfo(rootfs) { for rName, rCap := range cadvisor.EphemeralStorageCapacityFromFsInfo(rootfs) {
cm.capacity[rName] = rCap cm.capacity[rName] = rCap
} }
if hasDedicatedImageFs {
for rName, rCap := range cadvisor.StorageOverlayCapacityFromFsInfo(imagesfs) {
cm.capacity[rName] = rCap
}
}
cm.Unlock() cm.Unlock()
return nil return nil
} }
......
...@@ -218,9 +218,9 @@ func hardEvictionReservation(thresholds []evictionapi.Threshold, capacity v1.Res ...@@ -218,9 +218,9 @@ func hardEvictionReservation(thresholds []evictionapi.Threshold, capacity v1.Res
value := evictionapi.GetThresholdQuantity(threshold.Value, &memoryCapacity) value := evictionapi.GetThresholdQuantity(threshold.Value, &memoryCapacity)
ret[v1.ResourceMemory] = *value ret[v1.ResourceMemory] = *value
case evictionapi.SignalNodeFsAvailable: case evictionapi.SignalNodeFsAvailable:
storageCapacity := capacity[v1.ResourceStorageScratch] storageCapacity := capacity[v1.ResourceEphemeralStorage]
value := evictionapi.GetThresholdQuantity(threshold.Value, &storageCapacity) value := evictionapi.GetThresholdQuantity(threshold.Value, &storageCapacity)
ret[v1.ResourceStorageScratch] = *value ret[v1.ResourceEphemeralStorage] = *value
} }
} }
return ret return ret
......
...@@ -316,17 +316,17 @@ func TestNodeAllocatableInputValidation(t *testing.T) { ...@@ -316,17 +316,17 @@ func TestNodeAllocatableInputValidation(t *testing.T) {
invalidConfiguration bool invalidConfiguration bool
}{ }{
{ {
kubeReserved: getScratchResourceList("100Mi"), kubeReserved: getEphemeralStorageResourceList("100Mi"),
systemReserved: getScratchResourceList("50Mi"), systemReserved: getEphemeralStorageResourceList("50Mi"),
capacity: getScratchResourceList("500Mi"), capacity: getEphemeralStorageResourceList("500Mi"),
}, },
{ {
kubeReserved: getScratchResourceList("10Gi"), kubeReserved: getEphemeralStorageResourceList("10Gi"),
systemReserved: getScratchResourceList("10Gi"), systemReserved: getEphemeralStorageResourceList("10Gi"),
hardThreshold: evictionapi.ThresholdValue{ hardThreshold: evictionapi.ThresholdValue{
Quantity: &storageEvictionThreshold, Quantity: &storageEvictionThreshold,
}, },
capacity: getScratchResourceList("20Gi"), capacity: getEphemeralStorageResourceList("20Gi"),
invalidConfiguration: true, invalidConfiguration: true,
}, },
} }
...@@ -359,12 +359,12 @@ func TestNodeAllocatableInputValidation(t *testing.T) { ...@@ -359,12 +359,12 @@ func TestNodeAllocatableInputValidation(t *testing.T) {
} }
} }
// getScratchResourceList returns a ResourceList with the // getEphemeralStorageResourceList returns a ResourceList with the
// specified scratch storage resource values // specified ephemeral storage resource values
func getScratchResourceList(storage string) v1.ResourceList { func getEphemeralStorageResourceList(storage string) v1.ResourceList {
res := v1.ResourceList{} res := v1.ResourceList{}
if storage != "" { if storage != "" {
res[v1.ResourceStorageScratch] = resource.MustParse(storage) res[v1.ResourceEphemeralStorage] = resource.MustParse(storage)
} }
return res return res
} }
...@@ -48,6 +48,7 @@ go_library( ...@@ -48,6 +48,7 @@ go_library(
deps = [ deps = [
"//pkg/api:go_default_library", "//pkg/api:go_default_library",
"//pkg/api/v1/helper/qos:go_default_library", "//pkg/api/v1/helper/qos:go_default_library",
"//pkg/api/v1/resource:go_default_library",
"//pkg/features:go_default_library", "//pkg/features:go_default_library",
"//pkg/kubelet/apis/stats/v1alpha1:go_default_library", "//pkg/kubelet/apis/stats/v1alpha1:go_default_library",
"//pkg/kubelet/cm:go_default_library", "//pkg/kubelet/cm:go_default_library",
......
...@@ -31,6 +31,7 @@ import ( ...@@ -31,6 +31,7 @@ import (
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/tools/record" "k8s.io/client-go/tools/record"
v1qos "k8s.io/kubernetes/pkg/api/v1/helper/qos" v1qos "k8s.io/kubernetes/pkg/api/v1/helper/qos"
apiv1resource "k8s.io/kubernetes/pkg/api/v1/resource"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
statsapi "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1" statsapi "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
"k8s.io/kubernetes/pkg/kubelet/cm" "k8s.io/kubernetes/pkg/kubelet/cm"
...@@ -472,7 +473,12 @@ func (m *managerImpl) localStorageEviction(pods []*v1.Pod) []*v1.Pod { ...@@ -472,7 +473,12 @@ func (m *managerImpl) localStorageEviction(pods []*v1.Pod) []*v1.Pod {
continue continue
} }
if m.containerOverlayLimitEviction(podStats, pod) { if m.podEphemeralStorageLimitEviction(podStats, pod) {
evicted = append(evicted, pod)
continue
}
if m.containerEphemeralStorageLimitEviction(podStats, pod) {
evicted = append(evicted, pod) evicted = append(evicted, pod)
} }
} }
...@@ -496,23 +502,56 @@ func (m *managerImpl) emptyDirLimitEviction(podStats statsapi.PodStats, pod *v1. ...@@ -496,23 +502,56 @@ func (m *managerImpl) emptyDirLimitEviction(podStats statsapi.PodStats, pod *v1.
} }
} }
} }
return false
}
func (m *managerImpl) podEphemeralStorageLimitEviction(podStats statsapi.PodStats, pod *v1.Pod) bool {
_, podLimits := apiv1resource.PodRequestsAndLimits(pod)
_, found := podLimits[v1.ResourceEphemeralStorage]
if !found {
return false
}
podEphemeralStorageTotalUsage := &resource.Quantity{}
fsStatsSet := []fsStatsType{}
if *m.dedicatedImageFs {
fsStatsSet = []fsStatsType{fsStatsLogs, fsStatsLocalVolumeSource}
} else {
fsStatsSet = []fsStatsType{fsStatsRoot, fsStatsLogs, fsStatsLocalVolumeSource}
}
podUsage, err := podDiskUsage(podStats, pod, fsStatsSet)
if err != nil {
glog.Errorf("eviction manager: error getting pod disk usage %v", err)
return false
}
podEphemeralStorageTotalUsage.Add(podUsage[resourceDisk])
if podEphemeralStorageTotalUsage.Cmp(podLimits[v1.ResourceEphemeralStorage]) > 0 {
// the total usage of pod exceeds the total size limit of containers, evict the pod
return m.evictPod(pod, v1.ResourceEphemeralStorage, fmt.Sprintf("pod ephemeral local storage usage exceeds the total limit of containers %v", podLimits[v1.ResourceEphemeralStorage]))
}
return false return false
} }
func (m *managerImpl) containerOverlayLimitEviction(podStats statsapi.PodStats, pod *v1.Pod) bool { func (m *managerImpl) containerEphemeralStorageLimitEviction(podStats statsapi.PodStats, pod *v1.Pod) bool {
thresholdsMap := make(map[string]*resource.Quantity) thresholdsMap := make(map[string]*resource.Quantity)
for _, container := range pod.Spec.Containers { for _, container := range pod.Spec.Containers {
overlayLimit := container.Resources.Limits.StorageOverlay() ephemeralLimit := container.Resources.Limits.StorageEphemeral()
if overlayLimit != nil && overlayLimit.Value() != 0 { if ephemeralLimit != nil && ephemeralLimit.Value() != 0 {
thresholdsMap[container.Name] = overlayLimit thresholdsMap[container.Name] = ephemeralLimit
} }
} }
for _, containerStat := range podStats.Containers { for _, containerStat := range podStats.Containers {
rootfs := diskUsage(containerStat.Rootfs) containerUsed := diskUsage(containerStat.Logs)
if overlayThreshold, ok := thresholdsMap[containerStat.Name]; ok { if !*m.dedicatedImageFs {
if overlayThreshold.Cmp(*rootfs) < 0 { containerUsed.Add(*diskUsage(containerStat.Rootfs))
return m.evictPod(pod, v1.ResourceName("containerOverlay"), fmt.Sprintf("container's overlay usage exceeds the limit %q", overlayThreshold.String())) }
if ephemeralStorageThreshold, ok := thresholdsMap[containerStat.Name]; ok {
if ephemeralStorageThreshold.Cmp(*containerUsed) < 0 {
return m.evictPod(pod, v1.ResourceEphemeralStorage, fmt.Sprintf("container's ephemeral local storage usage exceeds the limit %q", ephemeralStorageThreshold.String()))
} }
} }
......
...@@ -54,8 +54,6 @@ const ( ...@@ -54,8 +54,6 @@ const (
resourceNodeFs v1.ResourceName = "nodefs" resourceNodeFs v1.ResourceName = "nodefs"
// nodefs inodes, number. internal to this module, used to account for local node root filesystem inodes. // nodefs inodes, number. internal to this module, used to account for local node root filesystem inodes.
resourceNodeFsInodes v1.ResourceName = "nodefsInodes" resourceNodeFsInodes v1.ResourceName = "nodefsInodes"
// container overlay storage, in bytes. internal to this module, used to account for local disk usage for container overlay.
resourceOverlay v1.ResourceName = "overlay"
) )
var ( var (
...@@ -400,12 +398,10 @@ func localVolumeNames(pod *v1.Pod) []string { ...@@ -400,12 +398,10 @@ func localVolumeNames(pod *v1.Pod) []string {
func podDiskUsage(podStats statsapi.PodStats, pod *v1.Pod, statsToMeasure []fsStatsType) (v1.ResourceList, error) { func podDiskUsage(podStats statsapi.PodStats, pod *v1.Pod, statsToMeasure []fsStatsType) (v1.ResourceList, error) {
disk := resource.Quantity{Format: resource.BinarySI} disk := resource.Quantity{Format: resource.BinarySI}
inodes := resource.Quantity{Format: resource.BinarySI} inodes := resource.Quantity{Format: resource.BinarySI}
overlay := resource.Quantity{Format: resource.BinarySI}
for _, container := range podStats.Containers { for _, container := range podStats.Containers {
if hasFsStatsType(statsToMeasure, fsStatsRoot) { if hasFsStatsType(statsToMeasure, fsStatsRoot) {
disk.Add(*diskUsage(container.Rootfs)) disk.Add(*diskUsage(container.Rootfs))
inodes.Add(*inodeUsage(container.Rootfs)) inodes.Add(*inodeUsage(container.Rootfs))
overlay.Add(*diskUsage(container.Rootfs))
} }
if hasFsStatsType(statsToMeasure, fsStatsLogs) { if hasFsStatsType(statsToMeasure, fsStatsLogs) {
disk.Add(*diskUsage(container.Logs)) disk.Add(*diskUsage(container.Logs))
...@@ -425,9 +421,8 @@ func podDiskUsage(podStats statsapi.PodStats, pod *v1.Pod, statsToMeasure []fsSt ...@@ -425,9 +421,8 @@ func podDiskUsage(podStats statsapi.PodStats, pod *v1.Pod, statsToMeasure []fsSt
} }
} }
return v1.ResourceList{ return v1.ResourceList{
resourceDisk: disk, resourceDisk: disk,
resourceInodes: inodes, resourceInodes: inodes,
resourceOverlay: overlay,
}, nil }, nil
} }
...@@ -727,7 +722,7 @@ func makeSignalObservations(summaryProvider stats.SummaryProvider, capacityProvi ...@@ -727,7 +722,7 @@ func makeSignalObservations(summaryProvider stats.SummaryProvider, capacityProvi
} }
} }
storageScratchCapacity, storageScratchAllocatable, exist := getResourceAllocatable(nodeCapacity, allocatableReservation, v1.ResourceStorageScratch) ephemeralStorageCapacity, ephemeralStorageAllocatable, exist := getResourceAllocatable(nodeCapacity, allocatableReservation, v1.ResourceEphemeralStorage)
if exist { if exist {
for _, pod := range pods { for _, pod := range pods {
podStat, ok := statsFunc(pod) podStat, ok := statsFunc(pod)
...@@ -735,25 +730,23 @@ func makeSignalObservations(summaryProvider stats.SummaryProvider, capacityProvi ...@@ -735,25 +730,23 @@ func makeSignalObservations(summaryProvider stats.SummaryProvider, capacityProvi
continue continue
} }
usage, err := podDiskUsage(podStat, pod, []fsStatsType{fsStatsLogs, fsStatsLocalVolumeSource, fsStatsRoot}) fsStatsSet := []fsStatsType{}
if withImageFs {
fsStatsSet = []fsStatsType{fsStatsLogs, fsStatsLocalVolumeSource}
} else {
fsStatsSet = []fsStatsType{fsStatsRoot, fsStatsLogs, fsStatsLocalVolumeSource}
}
usage, err := podDiskUsage(podStat, pod, fsStatsSet)
if err != nil { if err != nil {
glog.Warningf("eviction manager: error getting pod disk usage %v", err) glog.Warningf("eviction manager: error getting pod disk usage %v", err)
continue continue
} }
// If there is a seperate imagefs set up for container runtimes, the scratch disk usage from nodefs should exclude the overlay usage ephemeralStorageAllocatable.Sub(usage[resourceDisk])
if withImageFs {
diskUsage := usage[resourceDisk]
diskUsageP := &diskUsage
diskUsagep := diskUsageP.Copy()
diskUsagep.Sub(usage[resourceOverlay])
storageScratchAllocatable.Sub(*diskUsagep)
} else {
storageScratchAllocatable.Sub(usage[resourceDisk])
}
} }
result[evictionapi.SignalAllocatableNodeFsAvailable] = signalObservation{ result[evictionapi.SignalAllocatableNodeFsAvailable] = signalObservation{
available: storageScratchAllocatable, available: ephemeralStorageAllocatable,
capacity: storageScratchCapacity, capacity: ephemeralStorageCapacity,
} }
} }
......
...@@ -243,6 +243,7 @@ type Dependencies struct { ...@@ -243,6 +243,7 @@ type Dependencies struct {
Recorder record.EventRecorder Recorder record.EventRecorder
Writer kubeio.Writer Writer kubeio.Writer
VolumePlugins []volume.VolumePlugin VolumePlugins []volume.VolumePlugin
DynamicPluginProber volume.DynamicPluginProber
TLSOptions *server.TLSOptions TLSOptions *server.TLSOptions
KubeletConfigController *kubeletconfig.Controller KubeletConfigController *kubeletconfig.Controller
} }
...@@ -736,7 +737,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration, ...@@ -736,7 +737,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
kubeDeps.Recorder) kubeDeps.Recorder)
klet.volumePluginMgr, err = klet.volumePluginMgr, err =
NewInitializedVolumePluginMgr(klet, secretManager, configMapManager, kubeDeps.VolumePlugins) NewInitializedVolumePluginMgr(klet, secretManager, configMapManager, kubeDeps.VolumePlugins, kubeDeps.DynamicPluginProber)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -564,11 +564,7 @@ func (kl *Kubelet) setNodeStatusMachineInfo(node *v1.Node) { ...@@ -564,11 +564,7 @@ func (kl *Kubelet) setNodeStatusMachineInfo(node *v1.Node) {
// capacity for every node status request // capacity for every node status request
initialCapacity := kl.containerManager.GetCapacity() initialCapacity := kl.containerManager.GetCapacity()
if initialCapacity != nil { if initialCapacity != nil {
node.Status.Capacity[v1.ResourceStorageScratch] = initialCapacity[v1.ResourceStorageScratch] node.Status.Capacity[v1.ResourceEphemeralStorage] = initialCapacity[v1.ResourceEphemeralStorage]
imageCapacity, ok := initialCapacity[v1.ResourceStorageOverlay]
if ok {
node.Status.Capacity[v1.ResourceStorageOverlay] = imageCapacity
}
} }
} }
} }
......
...@@ -115,6 +115,11 @@ func (tk *TestKubelet) Cleanup() { ...@@ -115,6 +115,11 @@ func (tk *TestKubelet) Cleanup() {
} }
} }
func (tk *TestKubelet) chainMock() {
tk.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
tk.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
}
// newTestKubelet returns test kubelet with two images. // newTestKubelet returns test kubelet with two images.
func newTestKubelet(t *testing.T, controllerAttachDetachEnabled bool) *TestKubelet { func newTestKubelet(t *testing.T, controllerAttachDetachEnabled bool) *TestKubelet {
imageList := []kubecontainer.Image{ imageList := []kubecontainer.Image{
...@@ -272,8 +277,9 @@ func newTestKubeletWithImageList( ...@@ -272,8 +277,9 @@ func newTestKubeletWithImageList(
kubelet.admitHandlers.AddPodAdmitHandler(lifecycle.NewPredicateAdmitHandler(kubelet.getNodeAnyWay, lifecycle.NewAdmissionFailureHandlerStub())) kubelet.admitHandlers.AddPodAdmitHandler(lifecycle.NewPredicateAdmitHandler(kubelet.getNodeAnyWay, lifecycle.NewAdmissionFailureHandlerStub()))
plug := &volumetest.FakeVolumePlugin{PluginName: "fake", Host: nil} plug := &volumetest.FakeVolumePlugin{PluginName: "fake", Host: nil}
var prober volume.DynamicPluginProber = nil // TODO (#51147) inject mock
kubelet.volumePluginMgr, err = kubelet.volumePluginMgr, err =
NewInitializedVolumePluginMgr(kubelet, kubelet.secretManager, kubelet.configMapManager, []volume.VolumePlugin{plug}) NewInitializedVolumePluginMgr(kubelet, kubelet.secretManager, kubelet.configMapManager, []volume.VolumePlugin{plug}, prober)
require.NoError(t, err, "Failed to initialize VolumePluginMgr") require.NoError(t, err, "Failed to initialize VolumePluginMgr")
kubelet.mounter = &mount.FakeMounter{} kubelet.mounter = &mount.FakeMounter{}
...@@ -322,7 +328,6 @@ var emptyPodUIDs map[types.UID]kubetypes.SyncPodType ...@@ -322,7 +328,6 @@ var emptyPodUIDs map[types.UID]kubetypes.SyncPodType
func TestSyncLoopAbort(t *testing.T) { func TestSyncLoopAbort(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
kubelet := testKubelet.kubelet kubelet := testKubelet.kubelet
kubelet.runtimeState.setRuntimeSync(time.Now()) kubelet.runtimeState.setRuntimeSync(time.Now())
// The syncLoop waits on time.After(resyncInterval), set it really big so that we don't race for // The syncLoop waits on time.After(resyncInterval), set it really big so that we don't race for
...@@ -343,10 +348,6 @@ func TestSyncLoopAbort(t *testing.T) { ...@@ -343,10 +348,6 @@ func TestSyncLoopAbort(t *testing.T) {
func TestSyncPodsStartPod(t *testing.T) { func TestSyncPodsStartPod(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kubelet := testKubelet.kubelet kubelet := testKubelet.kubelet
fakeRuntime := testKubelet.fakeRuntime fakeRuntime := testKubelet.fakeRuntime
pods := []*v1.Pod{ pods := []*v1.Pod{
...@@ -367,9 +368,6 @@ func TestSyncPodsDeletesWhenSourcesAreReady(t *testing.T) { ...@@ -367,9 +368,6 @@ func TestSyncPodsDeletesWhenSourcesAreReady(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
fakeRuntime := testKubelet.fakeRuntime fakeRuntime := testKubelet.fakeRuntime
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kubelet := testKubelet.kubelet kubelet := testKubelet.kubelet
kubelet.sourcesReady = config.NewSourcesReady(func(_ sets.String) bool { return ready }) kubelet.sourcesReady = config.NewSourcesReady(func(_ sets.String) bool { return ready })
...@@ -415,14 +413,18 @@ func (ls testNodeLister) List(selector labels.Selector) ([]*v1.Node, error) { ...@@ -415,14 +413,18 @@ func (ls testNodeLister) List(selector labels.Selector) ([]*v1.Node, error) {
return ls.nodes, nil return ls.nodes, nil
} }
func checkPodStatus(t *testing.T, kl *Kubelet, pod *v1.Pod, phase v1.PodPhase) {
status, found := kl.statusManager.GetPodStatus(pod.UID)
require.True(t, found, "Status of pod %q is not found in the status map", pod.UID)
require.Equal(t, phase, status.Phase)
}
// Tests that we handle port conflicts correctly by setting the failed status in status map. // Tests that we handle port conflicts correctly by setting the failed status in status map.
func TestHandlePortConflicts(t *testing.T) { func TestHandlePortConflicts(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.chainMock()
kl := testKubelet.kubelet kl := testKubelet.kubelet
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kl.nodeInfo = testNodeInfo{nodes: []*v1.Node{ kl.nodeInfo = testNodeInfo{nodes: []*v1.Node{
{ {
...@@ -448,26 +450,18 @@ func TestHandlePortConflicts(t *testing.T) { ...@@ -448,26 +450,18 @@ func TestHandlePortConflicts(t *testing.T) {
fittingPod := pods[1] fittingPod := pods[1]
kl.HandlePodAdditions(pods) kl.HandlePodAdditions(pods)
// Check pod status stored in the status map.
// notfittingPod should be Failed
status, found := kl.statusManager.GetPodStatus(notfittingPod.UID)
require.True(t, found, "Status of pod %q is not found in the status map", notfittingPod.UID)
require.Equal(t, v1.PodFailed, status.Phase)
// fittingPod should be Pending // Check pod status stored in the status map.
status, found = kl.statusManager.GetPodStatus(fittingPod.UID) checkPodStatus(t, kl, notfittingPod, v1.PodFailed)
require.True(t, found, "Status of pod %q is not found in the status map", fittingPod.UID) checkPodStatus(t, kl, fittingPod, v1.PodPending)
require.Equal(t, v1.PodPending, status.Phase)
} }
// Tests that we handle host name conflicts correctly by setting the failed status in status map. // Tests that we handle host name conflicts correctly by setting the failed status in status map.
func TestHandleHostNameConflicts(t *testing.T) { func TestHandleHostNameConflicts(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.chainMock()
kl := testKubelet.kubelet kl := testKubelet.kubelet
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kl.nodeInfo = testNodeInfo{nodes: []*v1.Node{ kl.nodeInfo = testNodeInfo{nodes: []*v1.Node{
{ {
...@@ -490,22 +484,17 @@ func TestHandleHostNameConflicts(t *testing.T) { ...@@ -490,22 +484,17 @@ func TestHandleHostNameConflicts(t *testing.T) {
fittingPod := pods[1] fittingPod := pods[1]
kl.HandlePodAdditions(pods) kl.HandlePodAdditions(pods)
// Check pod status stored in the status map.
// notfittingPod should be Failed
status, found := kl.statusManager.GetPodStatus(notfittingPod.UID)
require.True(t, found, "Status of pod %q is not found in the status map", notfittingPod.UID)
require.Equal(t, v1.PodFailed, status.Phase)
// fittingPod should be Pending // Check pod status stored in the status map.
status, found = kl.statusManager.GetPodStatus(fittingPod.UID) checkPodStatus(t, kl, notfittingPod, v1.PodFailed)
require.True(t, found, "Status of pod %q is not found in the status map", fittingPod.UID) checkPodStatus(t, kl, fittingPod, v1.PodPending)
require.Equal(t, v1.PodPending, status.Phase)
} }
// Tests that we handle not matching labels selector correctly by setting the failed status in status map. // Tests that we handle not matching labels selector correctly by setting the failed status in status map.
func TestHandleNodeSelector(t *testing.T) { func TestHandleNodeSelector(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.chainMock()
kl := testKubelet.kubelet kl := testKubelet.kubelet
nodes := []*v1.Node{ nodes := []*v1.Node{
{ {
...@@ -518,9 +507,6 @@ func TestHandleNodeSelector(t *testing.T) { ...@@ -518,9 +507,6 @@ func TestHandleNodeSelector(t *testing.T) {
}, },
} }
kl.nodeInfo = testNodeInfo{nodes: nodes} kl.nodeInfo = testNodeInfo{nodes: nodes}
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
pods := []*v1.Pod{ pods := []*v1.Pod{
podWithUIDNameNsSpec("123456789", "podA", "foo", v1.PodSpec{NodeSelector: map[string]string{"key": "A"}}), podWithUIDNameNsSpec("123456789", "podA", "foo", v1.PodSpec{NodeSelector: map[string]string{"key": "A"}}),
podWithUIDNameNsSpec("987654321", "podB", "foo", v1.PodSpec{NodeSelector: map[string]string{"key": "B"}}), podWithUIDNameNsSpec("987654321", "podB", "foo", v1.PodSpec{NodeSelector: map[string]string{"key": "B"}}),
...@@ -530,22 +516,17 @@ func TestHandleNodeSelector(t *testing.T) { ...@@ -530,22 +516,17 @@ func TestHandleNodeSelector(t *testing.T) {
fittingPod := pods[1] fittingPod := pods[1]
kl.HandlePodAdditions(pods) kl.HandlePodAdditions(pods)
// Check pod status stored in the status map.
// notfittingPod should be Failed
status, found := kl.statusManager.GetPodStatus(notfittingPod.UID)
require.True(t, found, "Status of pod %q is not found in the status map", notfittingPod.UID)
require.Equal(t, v1.PodFailed, status.Phase)
// fittingPod should be Pending // Check pod status stored in the status map.
status, found = kl.statusManager.GetPodStatus(fittingPod.UID) checkPodStatus(t, kl, notfittingPod, v1.PodFailed)
require.True(t, found, "Status of pod %q is not found in the status map", fittingPod.UID) checkPodStatus(t, kl, fittingPod, v1.PodPending)
require.Equal(t, v1.PodPending, status.Phase)
} }
// Tests that we handle exceeded resources correctly by setting the failed status in status map. // Tests that we handle exceeded resources correctly by setting the failed status in status map.
func TestHandleMemExceeded(t *testing.T) { func TestHandleMemExceeded(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.chainMock()
kl := testKubelet.kubelet kl := testKubelet.kubelet
nodes := []*v1.Node{ nodes := []*v1.Node{
{ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname}, {ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname},
...@@ -556,9 +537,6 @@ func TestHandleMemExceeded(t *testing.T) { ...@@ -556,9 +537,6 @@ func TestHandleMemExceeded(t *testing.T) {
}}}, }}},
} }
kl.nodeInfo = testNodeInfo{nodes: nodes} kl.nodeInfo = testNodeInfo{nodes: nodes}
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
spec := v1.PodSpec{NodeName: string(kl.nodeName), spec := v1.PodSpec{NodeName: string(kl.nodeName),
Containers: []v1.Container{{Resources: v1.ResourceRequirements{ Containers: []v1.Container{{Resources: v1.ResourceRequirements{
...@@ -579,25 +557,17 @@ func TestHandleMemExceeded(t *testing.T) { ...@@ -579,25 +557,17 @@ func TestHandleMemExceeded(t *testing.T) {
fittingPod := pods[1] fittingPod := pods[1]
kl.HandlePodAdditions(pods) kl.HandlePodAdditions(pods)
// Check pod status stored in the status map.
// notfittingPod should be Failed
status, found := kl.statusManager.GetPodStatus(notfittingPod.UID)
require.True(t, found, "Status of pod %q is not found in the status map", notfittingPod.UID)
require.Equal(t, v1.PodFailed, status.Phase)
// fittingPod should be Pending // Check pod status stored in the status map.
status, found = kl.statusManager.GetPodStatus(fittingPod.UID) checkPodStatus(t, kl, notfittingPod, v1.PodFailed)
require.True(t, found, "Status of pod %q is not found in the status map", fittingPod.UID) checkPodStatus(t, kl, fittingPod, v1.PodPending)
require.Equal(t, v1.PodPending, status.Phase)
} }
// TODO(filipg): This test should be removed once StatusSyncer can do garbage collection without external signal. // TODO(filipg): This test should be removed once StatusSyncer can do garbage collection without external signal.
func TestPurgingObsoleteStatusMapEntries(t *testing.T) { func TestPurgingObsoleteStatusMapEntries(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
versionInfo := &cadvisorapi.VersionInfo{ versionInfo := &cadvisorapi.VersionInfo{
KernelVersion: "3.16.0-0.bpo.4-amd64", KernelVersion: "3.16.0-0.bpo.4-amd64",
ContainerOsVersion: "Debian GNU/Linux 7 (wheezy)", ContainerOsVersion: "Debian GNU/Linux 7 (wheezy)",
...@@ -735,11 +705,7 @@ func TestCreateMirrorPod(t *testing.T) { ...@@ -735,11 +705,7 @@ func TestCreateMirrorPod(t *testing.T) {
for _, updateType := range []kubetypes.SyncPodType{kubetypes.SyncPodCreate, kubetypes.SyncPodUpdate} { for _, updateType := range []kubetypes.SyncPodType{kubetypes.SyncPodCreate, kubetypes.SyncPodUpdate} {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("Start").Return(nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kl := testKubelet.kubelet kl := testKubelet.kubelet
manager := testKubelet.fakeMirrorClient manager := testKubelet.fakeMirrorClient
...@@ -762,11 +728,7 @@ func TestCreateMirrorPod(t *testing.T) { ...@@ -762,11 +728,7 @@ func TestCreateMirrorPod(t *testing.T) {
func TestDeleteOutdatedMirrorPod(t *testing.T) { func TestDeleteOutdatedMirrorPod(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("Start").Return(nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kl := testKubelet.kubelet kl := testKubelet.kubelet
manager := testKubelet.fakeMirrorClient manager := testKubelet.fakeMirrorClient
...@@ -805,10 +767,7 @@ func TestDeleteOutdatedMirrorPod(t *testing.T) { ...@@ -805,10 +767,7 @@ func TestDeleteOutdatedMirrorPod(t *testing.T) {
func TestDeleteOrphanedMirrorPods(t *testing.T) { func TestDeleteOrphanedMirrorPods(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("Start").Return(nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kl := testKubelet.kubelet kl := testKubelet.kubelet
manager := testKubelet.fakeMirrorClient manager := testKubelet.fakeMirrorClient
...@@ -928,11 +887,7 @@ func TestGetContainerInfoForMirrorPods(t *testing.T) { ...@@ -928,11 +887,7 @@ func TestGetContainerInfoForMirrorPods(t *testing.T) {
func TestHostNetworkAllowed(t *testing.T) { func TestHostNetworkAllowed(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("Start").Return(nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kubelet := testKubelet.kubelet kubelet := testKubelet.kubelet
...@@ -961,11 +916,7 @@ func TestHostNetworkAllowed(t *testing.T) { ...@@ -961,11 +916,7 @@ func TestHostNetworkAllowed(t *testing.T) {
func TestHostNetworkDisallowed(t *testing.T) { func TestHostNetworkDisallowed(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("Start").Return(nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kubelet := testKubelet.kubelet kubelet := testKubelet.kubelet
...@@ -993,11 +944,7 @@ func TestHostNetworkDisallowed(t *testing.T) { ...@@ -993,11 +944,7 @@ func TestHostNetworkDisallowed(t *testing.T) {
func TestHostPIDAllowed(t *testing.T) { func TestHostPIDAllowed(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("Start").Return(nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kubelet := testKubelet.kubelet kubelet := testKubelet.kubelet
...@@ -1026,11 +973,7 @@ func TestHostPIDAllowed(t *testing.T) { ...@@ -1026,11 +973,7 @@ func TestHostPIDAllowed(t *testing.T) {
func TestHostPIDDisallowed(t *testing.T) { func TestHostPIDDisallowed(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("Start").Return(nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kubelet := testKubelet.kubelet kubelet := testKubelet.kubelet
...@@ -1058,11 +1001,7 @@ func TestHostPIDDisallowed(t *testing.T) { ...@@ -1058,11 +1001,7 @@ func TestHostPIDDisallowed(t *testing.T) {
func TestHostIPCAllowed(t *testing.T) { func TestHostIPCAllowed(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("Start").Return(nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kubelet := testKubelet.kubelet kubelet := testKubelet.kubelet
...@@ -1091,11 +1030,7 @@ func TestHostIPCAllowed(t *testing.T) { ...@@ -1091,11 +1030,7 @@ func TestHostIPCAllowed(t *testing.T) {
func TestHostIPCDisallowed(t *testing.T) { func TestHostIPCDisallowed(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("Start").Return(nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kubelet := testKubelet.kubelet kubelet := testKubelet.kubelet
...@@ -1123,11 +1058,7 @@ func TestHostIPCDisallowed(t *testing.T) { ...@@ -1123,11 +1058,7 @@ func TestHostIPCDisallowed(t *testing.T) {
func TestPrivilegeContainerAllowed(t *testing.T) { func TestPrivilegeContainerAllowed(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("Start").Return(nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kubelet := testKubelet.kubelet kubelet := testKubelet.kubelet
...@@ -1153,10 +1084,7 @@ func TestPrivilegeContainerAllowed(t *testing.T) { ...@@ -1153,10 +1084,7 @@ func TestPrivilegeContainerAllowed(t *testing.T) {
func TestPrivilegedContainerDisallowed(t *testing.T) { func TestPrivilegedContainerDisallowed(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kubelet := testKubelet.kubelet kubelet := testKubelet.kubelet
capabilities.SetForTests(capabilities.Capabilities{ capabilities.SetForTests(capabilities.Capabilities{
...@@ -1180,10 +1108,7 @@ func TestPrivilegedContainerDisallowed(t *testing.T) { ...@@ -1180,10 +1108,7 @@ func TestPrivilegedContainerDisallowed(t *testing.T) {
func TestNetworkErrorsWithoutHostNetwork(t *testing.T) { func TestNetworkErrorsWithoutHostNetwork(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kubelet := testKubelet.kubelet kubelet := testKubelet.kubelet
kubelet.runtimeState.setNetworkState(fmt.Errorf("simulated network error")) kubelet.runtimeState.setNetworkState(fmt.Errorf("simulated network error"))
...@@ -1298,11 +1223,7 @@ func TestSyncPodsDoesNotSetPodsThatDidNotRunTooLongToFailed(t *testing.T) { ...@@ -1298,11 +1223,7 @@ func TestSyncPodsDoesNotSetPodsThatDidNotRunTooLongToFailed(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
fakeRuntime := testKubelet.fakeRuntime fakeRuntime := testKubelet.fakeRuntime
testKubelet.fakeCadvisor.On("Start").Return(nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kubelet := testKubelet.kubelet kubelet := testKubelet.kubelet
...@@ -1367,11 +1288,7 @@ func podWithUIDNameNsSpec(uid types.UID, name, namespace string, spec v1.PodSpec ...@@ -1367,11 +1288,7 @@ func podWithUIDNameNsSpec(uid types.UID, name, namespace string, spec v1.PodSpec
func TestDeletePodDirsForDeletedPods(t *testing.T) { func TestDeletePodDirsForDeletedPods(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("Start").Return(nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kl := testKubelet.kubelet kl := testKubelet.kubelet
pods := []*v1.Pod{ pods := []*v1.Pod{
podWithUIDNameNs("12345678", "pod1", "ns"), podWithUIDNameNs("12345678", "pod1", "ns"),
...@@ -1407,11 +1324,7 @@ func syncAndVerifyPodDir(t *testing.T, testKubelet *TestKubelet, pods []*v1.Pod, ...@@ -1407,11 +1324,7 @@ func syncAndVerifyPodDir(t *testing.T, testKubelet *TestKubelet, pods []*v1.Pod,
func TestDoesNotDeletePodDirsForTerminatedPods(t *testing.T) { func TestDoesNotDeletePodDirsForTerminatedPods(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("Start").Return(nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kl := testKubelet.kubelet kl := testKubelet.kubelet
pods := []*v1.Pod{ pods := []*v1.Pod{
podWithUIDNameNs("12345678", "pod1", "ns"), podWithUIDNameNs("12345678", "pod1", "ns"),
...@@ -1430,11 +1343,7 @@ func TestDoesNotDeletePodDirsForTerminatedPods(t *testing.T) { ...@@ -1430,11 +1343,7 @@ func TestDoesNotDeletePodDirsForTerminatedPods(t *testing.T) {
func TestDoesNotDeletePodDirsIfContainerIsRunning(t *testing.T) { func TestDoesNotDeletePodDirsIfContainerIsRunning(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("Start").Return(nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
runningPod := &kubecontainer.Pod{ runningPod := &kubecontainer.Pod{
ID: "12345678", ID: "12345678",
Name: "pod1", Name: "pod1",
...@@ -1494,10 +1403,7 @@ func TestGetPodsToSync(t *testing.T) { ...@@ -1494,10 +1403,7 @@ func TestGetPodsToSync(t *testing.T) {
func TestGenerateAPIPodStatusWithSortedContainers(t *testing.T) { func TestGenerateAPIPodStatusWithSortedContainers(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kubelet := testKubelet.kubelet kubelet := testKubelet.kubelet
numContainers := 10 numContainers := 10
expectedOrder := []string{} expectedOrder := []string{}
...@@ -1557,10 +1463,7 @@ func TestGenerateAPIPodStatusWithReasonCache(t *testing.T) { ...@@ -1557,10 +1463,7 @@ func TestGenerateAPIPodStatusWithReasonCache(t *testing.T) {
emptyContainerID := (&kubecontainer.ContainerID{}).String() emptyContainerID := (&kubecontainer.ContainerID{}).String()
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kubelet := testKubelet.kubelet kubelet := testKubelet.kubelet
pod := podWithUIDNameNs("12345678", "foo", "new") pod := podWithUIDNameNs("12345678", "foo", "new")
pod.Spec = v1.PodSpec{RestartPolicy: v1.RestartPolicyOnFailure} pod.Spec = v1.PodSpec{RestartPolicy: v1.RestartPolicyOnFailure}
...@@ -1747,10 +1650,7 @@ func TestGenerateAPIPodStatusWithDifferentRestartPolicies(t *testing.T) { ...@@ -1747,10 +1650,7 @@ func TestGenerateAPIPodStatusWithDifferentRestartPolicies(t *testing.T) {
emptyContainerID := (&kubecontainer.ContainerID{}).String() emptyContainerID := (&kubecontainer.ContainerID{}).String()
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil) testKubelet.chainMock()
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
kubelet := testKubelet.kubelet kubelet := testKubelet.kubelet
pod := podWithUIDNameNs("12345678", "foo", "new") pod := podWithUIDNameNs("12345678", "foo", "new")
containers := []v1.Container{{Name: "succeed"}, {Name: "failed"}} containers := []v1.Container{{Name: "succeed"}, {Name: "failed"}}
...@@ -1914,6 +1814,7 @@ func (a *testPodAdmitHandler) Admit(attrs *lifecycle.PodAdmitAttributes) lifecyc ...@@ -1914,6 +1814,7 @@ func (a *testPodAdmitHandler) Admit(attrs *lifecycle.PodAdmitAttributes) lifecyc
func TestHandlePodAdditionsInvokesPodAdmitHandlers(t *testing.T) { func TestHandlePodAdditionsInvokesPodAdmitHandlers(t *testing.T) {
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup() defer testKubelet.Cleanup()
testKubelet.chainMock()
kl := testKubelet.kubelet kl := testKubelet.kubelet
kl.nodeInfo = testNodeInfo{nodes: []*v1.Node{ kl.nodeInfo = testNodeInfo{nodes: []*v1.Node{
{ {
...@@ -1925,9 +1826,6 @@ func TestHandlePodAdditionsInvokesPodAdmitHandlers(t *testing.T) { ...@@ -1925,9 +1826,6 @@ func TestHandlePodAdditionsInvokesPodAdmitHandlers(t *testing.T) {
}, },
}, },
}} }}
testKubelet.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
testKubelet.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
testKubelet.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
pods := []*v1.Pod{ pods := []*v1.Pod{
{ {
...@@ -1952,16 +1850,10 @@ func TestHandlePodAdditionsInvokesPodAdmitHandlers(t *testing.T) { ...@@ -1952,16 +1850,10 @@ func TestHandlePodAdditionsInvokesPodAdmitHandlers(t *testing.T) {
kl.admitHandlers.AddPodAdmitHandler(&testPodAdmitHandler{podsToReject: podsToReject}) kl.admitHandlers.AddPodAdmitHandler(&testPodAdmitHandler{podsToReject: podsToReject})
kl.HandlePodAdditions(pods) kl.HandlePodAdditions(pods)
// Check pod status stored in the status map.
// podToReject should be Failed
status, found := kl.statusManager.GetPodStatus(podToReject.UID)
require.True(t, found, "Status of pod %q is not found in the status map", podToAdmit.UID)
require.Equal(t, v1.PodFailed, status.Phase)
// podToAdmit should be Pending // Check pod status stored in the status map.
status, found = kl.statusManager.GetPodStatus(podToAdmit.UID) checkPodStatus(t, kl, podToReject, v1.PodFailed)
require.True(t, found, "Status of pod %q is not found in the status map", podToAdmit.UID) checkPodStatus(t, kl, podToAdmit, v1.PodPending)
require.Equal(t, v1.PodPending, status.Phase)
} }
// testPodSyncLoopHandler is a lifecycle.PodSyncLoopHandler that is used for testing. // testPodSyncLoopHandler is a lifecycle.PodSyncLoopHandler that is used for testing.
...@@ -2065,10 +1957,9 @@ func TestSyncPodKillPod(t *testing.T) { ...@@ -2065,10 +1957,9 @@ func TestSyncPodKillPod(t *testing.T) {
}, },
}) })
require.NoError(t, err) require.NoError(t, err)
// Check pod status stored in the status map. // Check pod status stored in the status map.
status, found := kl.statusManager.GetPodStatus(pod.UID) checkPodStatus(t, kl, pod, v1.PodFailed)
require.True(t, found, "Status of pod %q is not found in the status map", pod.UID)
require.Equal(t, v1.PodFailed, status.Phase)
} }
func waitForVolumeUnmount( func waitForVolumeUnmount(
......
...@@ -30,6 +30,7 @@ import ( ...@@ -30,6 +30,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
kubetypes "k8s.io/apimachinery/pkg/types" kubetypes "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/util/flowcontrol" "k8s.io/client-go/util/flowcontrol"
"k8s.io/kubernetes/pkg/credentialprovider" "k8s.io/kubernetes/pkg/credentialprovider"
apitest "k8s.io/kubernetes/pkg/kubelet/apis/cri/testing" apitest "k8s.io/kubernetes/pkg/kubelet/apis/cri/testing"
...@@ -216,15 +217,12 @@ func verifyPods(a, b []*kubecontainer.Pod) bool { ...@@ -216,15 +217,12 @@ func verifyPods(a, b []*kubecontainer.Pod) bool {
return reflect.DeepEqual(a, b) return reflect.DeepEqual(a, b)
} }
func verifyFakeContainerList(fakeRuntime *apitest.FakeRuntimeService, expected []string) ([]string, bool) { func verifyFakeContainerList(fakeRuntime *apitest.FakeRuntimeService, expected sets.String) (sets.String, bool) {
actual := []string{} actual := sets.NewString()
for _, c := range fakeRuntime.Containers { for _, c := range fakeRuntime.Containers {
actual = append(actual, c.Id) actual.Insert(c.Id)
} }
sort.Sort(sort.StringSlice(actual)) return actual, actual.Equal(expected)
sort.Sort(sort.StringSlice(expected))
return actual, reflect.DeepEqual(expected, actual)
} }
type containerRecord struct { type containerRecord struct {
...@@ -618,9 +616,9 @@ func TestPruneInitContainers(t *testing.T) { ...@@ -618,9 +616,9 @@ func TestPruneInitContainers(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
m.pruneInitContainersBeforeStart(pod, podStatus) m.pruneInitContainersBeforeStart(pod, podStatus)
expectedContainers := []string{fakes[0].Id, fakes[2].Id} expectedContainers := sets.NewString(fakes[0].Id, fakes[2].Id)
if actual, ok := verifyFakeContainerList(fakeRuntime, expectedContainers); !ok { if actual, ok := verifyFakeContainerList(fakeRuntime, expectedContainers); !ok {
t.Errorf("expected %q, got %q", expectedContainers, actual) t.Errorf("expected %v, got %v", expectedContainers, actual)
} }
} }
......
...@@ -187,6 +187,11 @@ func (a *noNewPrivsAdmitHandler) Admit(attrs *PodAdmitAttributes) PodAdmitResult ...@@ -187,6 +187,11 @@ func (a *noNewPrivsAdmitHandler) Admit(attrs *PodAdmitAttributes) PodAdmitResult
return PodAdmitResult{Admit: true} return PodAdmitResult{Admit: true}
} }
// Always admit for remote runtime.
if a.Runtime.Type() == kubetypes.RemoteContainerRuntime {
return PodAdmitResult{Admit: true}
}
// Make sure it is either docker or rkt runtimes. // Make sure it is either docker or rkt runtimes.
if a.Runtime.Type() != kubetypes.DockerContainerRuntime && a.Runtime.Type() != kubetypes.RktContainerRuntime { if a.Runtime.Type() != kubetypes.DockerContainerRuntime && a.Runtime.Type() != kubetypes.RktContainerRuntime {
return PodAdmitResult{ return PodAdmitResult{
...@@ -196,7 +201,7 @@ func (a *noNewPrivsAdmitHandler) Admit(attrs *PodAdmitAttributes) PodAdmitResult ...@@ -196,7 +201,7 @@ func (a *noNewPrivsAdmitHandler) Admit(attrs *PodAdmitAttributes) PodAdmitResult
} }
} }
if a.Runtime.Type() != kubetypes.DockerContainerRuntime { if a.Runtime.Type() == kubetypes.DockerContainerRuntime {
// Make sure docker api version is valid. // Make sure docker api version is valid.
rversion, err := a.Runtime.APIVersion() rversion, err := a.Runtime.APIVersion()
if err != nil { if err != nil {
...@@ -206,7 +211,7 @@ func (a *noNewPrivsAdmitHandler) Admit(attrs *PodAdmitAttributes) PodAdmitResult ...@@ -206,7 +211,7 @@ func (a *noNewPrivsAdmitHandler) Admit(attrs *PodAdmitAttributes) PodAdmitResult
Message: fmt.Sprintf("Cannot enforce NoNewPrivs: %v", err), Message: fmt.Sprintf("Cannot enforce NoNewPrivs: %v", err),
} }
} }
v, err := rversion.Compare("1.23") v, err := rversion.Compare("1.23.0")
if err != nil { if err != nil {
return PodAdmitResult{ return PodAdmitResult{
Admit: false, Admit: false,
......
...@@ -92,7 +92,7 @@ func TestRunOnce(t *testing.T) { ...@@ -92,7 +92,7 @@ func TestRunOnce(t *testing.T) {
plug := &volumetest.FakeVolumePlugin{PluginName: "fake", Host: nil} plug := &volumetest.FakeVolumePlugin{PluginName: "fake", Host: nil}
kb.volumePluginMgr, err = kb.volumePluginMgr, err =
NewInitializedVolumePluginMgr(kb, fakeSecretManager, fakeConfigMapManager, []volume.VolumePlugin{plug}) NewInitializedVolumePluginMgr(kb, fakeSecretManager, fakeConfigMapManager, []volume.VolumePlugin{plug}, nil /* prober */)
if err != nil { if err != nil {
t.Fatalf("failed to initialize VolumePluginMgr: %v", err) t.Fatalf("failed to initialize VolumePluginMgr: %v", err)
} }
......
...@@ -41,7 +41,8 @@ func NewInitializedVolumePluginMgr( ...@@ -41,7 +41,8 @@ func NewInitializedVolumePluginMgr(
kubelet *Kubelet, kubelet *Kubelet,
secretManager secret.Manager, secretManager secret.Manager,
configMapManager configmap.Manager, configMapManager configmap.Manager,
plugins []volume.VolumePlugin) (*volume.VolumePluginMgr, error) { plugins []volume.VolumePlugin,
prober volume.DynamicPluginProber) (*volume.VolumePluginMgr, error) {
kvh := &kubeletVolumeHost{ kvh := &kubeletVolumeHost{
kubelet: kubelet, kubelet: kubelet,
volumePluginMgr: volume.VolumePluginMgr{}, volumePluginMgr: volume.VolumePluginMgr{},
...@@ -49,7 +50,7 @@ func NewInitializedVolumePluginMgr( ...@@ -49,7 +50,7 @@ func NewInitializedVolumePluginMgr(
configMapManager: configMapManager, configMapManager: configMapManager,
} }
if err := kvh.volumePluginMgr.InitPlugins(plugins, kvh); err != nil { if err := kvh.volumePluginMgr.InitPlugins(plugins, prober, kvh); err != nil {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"Could not initialize volume plugins for KubeletVolumePluginMgr: %v", "Could not initialize volume plugins for KubeletVolumePluginMgr: %v",
err) err)
......
...@@ -217,7 +217,8 @@ func newTestVolumeManager(tmpDir string, podManager pod.Manager, kubeClient clie ...@@ -217,7 +217,8 @@ func newTestVolumeManager(tmpDir string, podManager pod.Manager, kubeClient clie
plug := &volumetest.FakeVolumePlugin{PluginName: "fake", Host: nil} plug := &volumetest.FakeVolumePlugin{PluginName: "fake", Host: nil}
fakeRecorder := &record.FakeRecorder{} fakeRecorder := &record.FakeRecorder{}
plugMgr := &volume.VolumePluginMgr{} plugMgr := &volume.VolumePluginMgr{}
plugMgr.InitPlugins([]volume.VolumePlugin{plug}, volumetest.NewFakeVolumeHost(tmpDir, kubeClient, nil)) // TODO (#51147) inject mock prober
plugMgr.InitPlugins([]volume.VolumePlugin{plug}, nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, kubeClient, nil))
statusManager := status.NewManager(kubeClient, podManager, &statustest.FakePodDeletionSafetyProvider{}) statusManager := status.NewManager(kubeClient, podManager, &statustest.FakePodDeletionSafetyProvider{})
vm := NewVolumeManager( vm := NewVolumeManager(
......
...@@ -2470,7 +2470,11 @@ func describeNode(node *api.Node, nodeNonTerminatedPodsList *api.PodList, events ...@@ -2470,7 +2470,11 @@ func describeNode(node *api.Node, nodeNonTerminatedPodsList *api.PodList, events
return tabbedString(func(out io.Writer) error { return tabbedString(func(out io.Writer) error {
w := NewPrefixWriter(out) w := NewPrefixWriter(out)
w.Write(LEVEL_0, "Name:\t%s\n", node.Name) w.Write(LEVEL_0, "Name:\t%s\n", node.Name)
w.Write(LEVEL_0, "Role:\t%s\n", findNodeRole(node)) if roles := findNodeRoles(node); len(roles) > 0 {
w.Write(LEVEL_0, "Roles:\t%s\n", strings.Join(roles, ","))
} else {
w.Write(LEVEL_0, "Roles:\t%s\n", "<none>")
}
printLabelsMultiline(w, "Labels", node.Labels) printLabelsMultiline(w, "Labels", node.Labels)
printAnnotationsMultiline(w, "Annotations", node.Annotations) printAnnotationsMultiline(w, "Annotations", node.Annotations)
printNodeTaintsMultiline(w, "Taints", node.Spec.Taints) printNodeTaintsMultiline(w, "Taints", node.Spec.Taints)
......
...@@ -62,16 +62,12 @@ import ( ...@@ -62,16 +62,12 @@ import (
const ( const (
loadBalancerWidth = 16 loadBalancerWidth = 16
// LabelNodeRoleMaster specifies that a node is a master // labelNodeRolePrefix is a label prefix for node roles
// It's copied over to here until it's merged in core: https://github.com/kubernetes/kubernetes/pull/39112 // It's copied over to here until it's merged in core: https://github.com/kubernetes/kubernetes/pull/39112
LabelNodeRoleMaster = "node-role.kubernetes.io/master" labelNodeRolePrefix = "node-role.kubernetes.io/"
// NodeLabelRole specifies the role of a node // nodeLabelRole specifies the role of a node
NodeLabelRole = "kubernetes.io/role" nodeLabelRole = "kubernetes.io/role"
// NodeLabelKubeadmAlphaRole is a label that kubeadm applies to a Node as a hint that it has a particular purpose.
// Use of NodeLabelRole is preferred.
NodeLabelKubeadmAlphaRole = "kubeadm.alpha.kubernetes.io/role"
) )
// AddHandlers adds print handlers for default Kubernetes types dealing with internal versions. // AddHandlers adds print handlers for default Kubernetes types dealing with internal versions.
...@@ -221,6 +217,7 @@ func AddHandlers(h printers.PrintHandler) { ...@@ -221,6 +217,7 @@ func AddHandlers(h printers.PrintHandler) {
nodeColumnDefinitions := []metav1alpha1.TableColumnDefinition{ nodeColumnDefinitions := []metav1alpha1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]}, {Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
{Name: "Status", Type: "string", Description: "The status of the node"}, {Name: "Status", Type: "string", Description: "The status of the node"},
{Name: "Roles", Type: "string", Description: "The roles of the node"},
{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]}, {Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
{Name: "Version", Type: "string", Description: apiv1.NodeSystemInfo{}.SwaggerDoc()["kubeletVersion"]}, {Name: "Version", Type: "string", Description: apiv1.NodeSystemInfo{}.SwaggerDoc()["kubeletVersion"]},
{Name: "External-IP", Type: "string", Priority: 1, Description: apiv1.NodeStatus{}.SwaggerDoc()["addresses"]}, {Name: "External-IP", Type: "string", Priority: 1, Description: apiv1.NodeStatus{}.SwaggerDoc()["addresses"]},
...@@ -1167,12 +1164,13 @@ func printNode(obj *api.Node, options printers.PrintOptions) ([]metav1alpha1.Tab ...@@ -1167,12 +1164,13 @@ func printNode(obj *api.Node, options printers.PrintOptions) ([]metav1alpha1.Tab
if obj.Spec.Unschedulable { if obj.Spec.Unschedulable {
status = append(status, "SchedulingDisabled") status = append(status, "SchedulingDisabled")
} }
role := findNodeRole(obj)
if role != "" { roles := strings.Join(findNodeRoles(obj), ",")
status = append(status, role) if len(roles) == 0 {
roles = "<none>"
} }
row.Cells = append(row.Cells, obj.Name, strings.Join(status, ","), translateTimestamp(obj.CreationTimestamp), obj.Status.NodeInfo.KubeletVersion) row.Cells = append(row.Cells, obj.Name, strings.Join(status, ","), roles, translateTimestamp(obj.CreationTimestamp), obj.Status.NodeInfo.KubeletVersion)
if options.Wide { if options.Wide {
osImage, kernelVersion, crVersion := obj.Status.NodeInfo.OSImage, obj.Status.NodeInfo.KernelVersion, obj.Status.NodeInfo.ContainerRuntimeVersion osImage, kernelVersion, crVersion := obj.Status.NodeInfo.OSImage, obj.Status.NodeInfo.KernelVersion, obj.Status.NodeInfo.ContainerRuntimeVersion
if osImage == "" { if osImage == "" {
...@@ -1201,22 +1199,24 @@ func getNodeExternalIP(node *api.Node) string { ...@@ -1201,22 +1199,24 @@ func getNodeExternalIP(node *api.Node) string {
return "<none>" return "<none>"
} }
// findNodeRole returns the role of a given node, or "" if none found. // findNodeRoles returns the roles of a given node.
// The role is determined by looking in order for: // The roles are determined by looking for:
// * a node-role.kubernetes.io/master label // * a node-role.kubernetes.io/<role>="" label
// * a kubernetes.io/role label // * a kubernetes.io/role="<role>" label
// * a kubeadm.alpha.kubernetes.io/role label func findNodeRoles(node *api.Node) []string {
func findNodeRole(node *api.Node) string { roles := sets.NewString()
if _, ok := node.Labels[LabelNodeRoleMaster]; ok { for k, v := range node.Labels {
return "Master" switch {
} case strings.HasPrefix(k, labelNodeRolePrefix):
if role := node.Labels[NodeLabelRole]; role != "" { if role := strings.TrimPrefix(k, labelNodeRolePrefix); len(role) > 0 {
return strings.Title(role) roles.Insert(role)
} }
if role := node.Labels[NodeLabelKubeadmAlphaRole]; role != "" {
return strings.Title(role) case k == nodeLabelRole && v != "":
roles.Insert(v)
}
} }
return "" return roles.List()
} }
func printNodeList(list *api.NodeList, options printers.PrintOptions) ([]metav1alpha1.TableRow, error) { func printNodeList(list *api.NodeList, options printers.PrintOptions) ([]metav1alpha1.TableRow, error) {
......
...@@ -825,33 +825,50 @@ func TestPrintNodeStatus(t *testing.T) { ...@@ -825,33 +825,50 @@ func TestPrintNodeStatus(t *testing.T) {
}, },
status: "Unknown,SchedulingDisabled", status: "Unknown,SchedulingDisabled",
}, },
}
for _, test := range table {
buffer := &bytes.Buffer{}
err := printer.PrintObj(&test.node, buffer)
if err != nil {
t.Fatalf("An error occurred printing Node: %#v", err)
}
if !contains(strings.Fields(buffer.String()), test.status) {
t.Fatalf("Expect printing node %s with status %#v, got: %#v", test.node.Name, test.status, buffer.String())
}
}
}
func TestPrintNodeRole(t *testing.T) {
printer := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{})
AddHandlers(printer)
table := []struct {
node api.Node
expected string
}{
{ {
node: api.Node{ node: api.Node{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{Name: "foo9"},
Name: "foo10",
Labels: map[string]string{"node-role.kubernetes.io/master": "", "kubernetes.io/role": "node", "kubeadm.alpha.kubernetes.io/role": "node"},
},
}, },
status: "Unknown,Master", expected: "<none>",
}, },
{ {
node: api.Node{ node: api.Node{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "foo11", Name: "foo10",
Labels: map[string]string{"kubernetes.io/role": "node", "kubeadm.alpha.kubernetes.io/role": "node"}, Labels: map[string]string{"node-role.kubernetes.io/master": "", "node-role.kubernetes.io/proxy": "", "kubernetes.io/role": "node"},
}, },
}, },
status: "Unknown,Node", expected: "master,node,proxy",
}, },
{ {
node: api.Node{ node: api.Node{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "foo12", Name: "foo11",
Labels: map[string]string{"kubeadm.alpha.kubernetes.io/role": "node"}, Labels: map[string]string{"kubernetes.io/role": "node"},
}, },
Status: api.NodeStatus{Conditions: []api.NodeCondition{{Type: api.NodeReady, Status: api.ConditionTrue}}},
}, },
status: "Ready,Node", expected: "node",
}, },
} }
...@@ -861,8 +878,8 @@ func TestPrintNodeStatus(t *testing.T) { ...@@ -861,8 +878,8 @@ func TestPrintNodeStatus(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("An error occurred printing Node: %#v", err) t.Fatalf("An error occurred printing Node: %#v", err)
} }
if !contains(strings.Fields(buffer.String()), test.status) { if !contains(strings.Fields(buffer.String()), test.expected) {
t.Fatalf("Expect printing node %s with status %#v, got: %#v", test.node.Name, test.status, buffer.String()) t.Fatalf("Expect printing node %s with role %#v, got: %#v", test.node.Name, test.expected, buffer.String())
} }
} }
} }
......
...@@ -25,6 +25,7 @@ go_library( ...@@ -25,6 +25,7 @@ go_library(
"//pkg/api/helper/qos:go_default_library", "//pkg/api/helper/qos:go_default_library",
"//pkg/api/v1:go_default_library", "//pkg/api/v1:go_default_library",
"//pkg/api/validation:go_default_library", "//pkg/api/validation:go_default_library",
"//pkg/kubeapiserver/admission/util:go_default_library",
"//pkg/quota:go_default_library", "//pkg/quota:go_default_library",
"//pkg/quota/generic:go_default_library", "//pkg/quota/generic:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library",
......
...@@ -32,6 +32,7 @@ import ( ...@@ -32,6 +32,7 @@ import (
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/helper" "k8s.io/kubernetes/pkg/api/helper"
k8s_api_v1 "k8s.io/kubernetes/pkg/api/v1" k8s_api_v1 "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/kubeapiserver/admission/util"
"k8s.io/kubernetes/pkg/quota" "k8s.io/kubernetes/pkg/quota"
"k8s.io/kubernetes/pkg/quota/generic" "k8s.io/kubernetes/pkg/quota/generic"
) )
...@@ -141,8 +142,18 @@ func (p *pvcEvaluator) GroupKind() schema.GroupKind { ...@@ -141,8 +142,18 @@ func (p *pvcEvaluator) GroupKind() schema.GroupKind {
} }
// Handles returns true if the evaluator should handle the specified operation. // Handles returns true if the evaluator should handle the specified operation.
func (p *pvcEvaluator) Handles(operation admission.Operation) bool { func (p *pvcEvaluator) Handles(a admission.Attributes) bool {
return admission.Create == operation op := a.GetOperation()
if op == admission.Create {
return true
}
updateUninitialized, err := util.IsUpdatingUninitializedObject(a)
if err != nil {
// fail closed, will try to give an evaluation.
return true
}
// only uninitialized pvc might be updated.
return updateUninitialized
} }
// Matches returns true if the evaluator matches the specified quota with the provided input item // Matches returns true if the evaluator matches the specified quota with the provided input item
......
...@@ -34,6 +34,7 @@ import ( ...@@ -34,6 +34,7 @@ import (
"k8s.io/kubernetes/pkg/api/helper/qos" "k8s.io/kubernetes/pkg/api/helper/qos"
k8s_api_v1 "k8s.io/kubernetes/pkg/api/v1" k8s_api_v1 "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/api/validation" "k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/kubeapiserver/admission/util"
"k8s.io/kubernetes/pkg/quota" "k8s.io/kubernetes/pkg/quota"
"k8s.io/kubernetes/pkg/quota/generic" "k8s.io/kubernetes/pkg/quota/generic"
) )
...@@ -131,10 +132,19 @@ func (p *podEvaluator) GroupKind() schema.GroupKind { ...@@ -131,10 +132,19 @@ func (p *podEvaluator) GroupKind() schema.GroupKind {
return api.Kind("Pod") return api.Kind("Pod")
} }
// Handles returns true of the evaluator should handle the specified operation. // Handles returns true of the evaluator should handle the specified attributes.
func (p *podEvaluator) Handles(operation admission.Operation) bool { func (p *podEvaluator) Handles(a admission.Attributes) bool {
// TODO: update this if/when pods support resizing resource requirements. op := a.GetOperation()
return admission.Create == operation if op == admission.Create {
return true
}
updateUninitialized, err := util.IsUpdatingUninitializedObject(a)
if err != nil {
// fail closed, will try to give an evaluation.
return true
}
// only uninitialized pods might be updated.
return updateUninitialized
} }
// Matches returns true if the evaluator matches the specified quota with the provided input item // Matches returns true if the evaluator matches the specified quota with the provided input item
......
...@@ -108,7 +108,8 @@ func (p *serviceEvaluator) GroupKind() schema.GroupKind { ...@@ -108,7 +108,8 @@ func (p *serviceEvaluator) GroupKind() schema.GroupKind {
} }
// Handles returns true of the evaluator should handle the specified operation. // Handles returns true of the evaluator should handle the specified operation.
func (p *serviceEvaluator) Handles(operation admission.Operation) bool { func (p *serviceEvaluator) Handles(a admission.Attributes) bool {
operation := a.GetOperation()
// We handle create and update because a service type can change. // We handle create and update because a service type can change.
return admission.Create == operation || admission.Update == operation return admission.Create == operation || admission.Update == operation
} }
......
...@@ -150,8 +150,9 @@ func (o *ObjectCountEvaluator) GroupKind() schema.GroupKind { ...@@ -150,8 +150,9 @@ func (o *ObjectCountEvaluator) GroupKind() schema.GroupKind {
return o.InternalGroupKind return o.InternalGroupKind
} }
// Handles returns true if the object count evaluator needs to track this operation. // Handles returns true if the object count evaluator needs to track this attributes.
func (o *ObjectCountEvaluator) Handles(operation admission.Operation) bool { func (o *ObjectCountEvaluator) Handles(a admission.Attributes) bool {
operation := a.GetOperation()
return operation == admission.Create || (o.AllowCreateOnUpdate && operation == admission.Update) return operation == admission.Create || (o.AllowCreateOnUpdate && operation == admission.Update)
} }
......
...@@ -45,9 +45,9 @@ type Evaluator interface { ...@@ -45,9 +45,9 @@ type Evaluator interface {
Constraints(required []api.ResourceName, item runtime.Object) error Constraints(required []api.ResourceName, item runtime.Object) error
// GroupKind returns the groupKind that this object knows how to evaluate // GroupKind returns the groupKind that this object knows how to evaluate
GroupKind() schema.GroupKind GroupKind() schema.GroupKind
// Handles determines if quota could be impacted by the specified operation. // Handles determines if quota could be impacted by the specified attribute.
// If true, admission control must perform quota processing for the operation, otherwise it is safe to ignore quota. // If true, admission control must perform quota processing for the operation, otherwise it is safe to ignore quota.
Handles(operation admission.Operation) bool Handles(operation admission.Attributes) bool
// Matches returns true if the specified quota matches the input item // Matches returns true if the specified quota matches the input item
Matches(resourceQuota *api.ResourceQuota, item runtime.Object) (bool, error) Matches(resourceQuota *api.ResourceQuota, item runtime.Object) (bool, error)
// MatchingResources takes the input specified list of resources and returns the set of resources evaluator matches. // MatchingResources takes the input specified list of resources and returns the set of resources evaluator matches.
......
...@@ -196,9 +196,10 @@ func PodToSelectableFields(pod *api.Pod) fields.Set { ...@@ -196,9 +196,10 @@ func PodToSelectableFields(pod *api.Pod) fields.Set {
// amount of allocations needed to create the fields.Set. If you add any // amount of allocations needed to create the fields.Set. If you add any
// field here or the number of object-meta related fields changes, this should // field here or the number of object-meta related fields changes, this should
// be adjusted. // be adjusted.
podSpecificFieldsSet := make(fields.Set, 6) podSpecificFieldsSet := make(fields.Set, 7)
podSpecificFieldsSet["spec.nodeName"] = pod.Spec.NodeName podSpecificFieldsSet["spec.nodeName"] = pod.Spec.NodeName
podSpecificFieldsSet["spec.restartPolicy"] = string(pod.Spec.RestartPolicy) podSpecificFieldsSet["spec.restartPolicy"] = string(pod.Spec.RestartPolicy)
podSpecificFieldsSet["spec.schedulerName"] = string(pod.Spec.SchedulerName)
podSpecificFieldsSet["status.phase"] = string(pod.Status.Phase) podSpecificFieldsSet["status.phase"] = string(pod.Status.Phase)
podSpecificFieldsSet["status.podIP"] = string(pod.Status.PodIP) podSpecificFieldsSet["status.podIP"] = string(pod.Status.PodIP)
return generic.AddObjectMetaFieldsSet(podSpecificFieldsSet, &pod.ObjectMeta, true) return generic.AddObjectMetaFieldsSet(podSpecificFieldsSet, &pod.ObjectMeta, true)
......
...@@ -73,6 +73,20 @@ func TestMatchPod(t *testing.T) { ...@@ -73,6 +73,20 @@ func TestMatchPod(t *testing.T) {
}, },
{ {
in: &api.Pod{ in: &api.Pod{
Spec: api.PodSpec{SchedulerName: "scheduler1"},
},
fieldSelector: fields.ParseSelectorOrDie("spec.schedulerName=scheduler1"),
expectMatch: true,
},
{
in: &api.Pod{
Spec: api.PodSpec{SchedulerName: "scheduler1"},
},
fieldSelector: fields.ParseSelectorOrDie("spec.schedulerName=scheduler2"),
expectMatch: false,
},
{
in: &api.Pod{
Status: api.PodStatus{Phase: api.PodRunning}, Status: api.PodStatus{Phase: api.PodRunning},
}, },
fieldSelector: fields.ParseSelectorOrDie("status.phase=Running"), fieldSelector: fields.ParseSelectorOrDie("status.phase=Running"),
......
...@@ -40,7 +40,7 @@ func TestCanSupport(t *testing.T) { ...@@ -40,7 +40,7 @@ func TestCanSupport(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/aws-ebs") plug, err := plugMgr.FindPluginByName("kubernetes.io/aws-ebs")
if err != nil { if err != nil {
...@@ -64,7 +64,7 @@ func TestGetAccessModes(t *testing.T) { ...@@ -64,7 +64,7 @@ func TestGetAccessModes(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/aws-ebs") plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/aws-ebs")
if err != nil { if err != nil {
...@@ -113,7 +113,7 @@ func TestPlugin(t *testing.T) { ...@@ -113,7 +113,7 @@ func TestPlugin(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/aws-ebs") plug, err := plugMgr.FindPluginByName("kubernetes.io/aws-ebs")
if err != nil { if err != nil {
...@@ -186,6 +186,9 @@ func TestPlugin(t *testing.T) { ...@@ -186,6 +186,9 @@ func TestPlugin(t *testing.T) {
PersistentVolumeReclaimPolicy: v1.PersistentVolumeReclaimDelete, PersistentVolumeReclaimPolicy: v1.PersistentVolumeReclaimDelete,
} }
provisioner, err := plug.(*awsElasticBlockStorePlugin).newProvisionerInternal(options, &fakePDManager{}) provisioner, err := plug.(*awsElasticBlockStorePlugin).newProvisionerInternal(options, &fakePDManager{})
if err != nil {
t.Errorf("Error creating new provisioner:%v", err)
}
persistentSpec, err := provisioner.Provision() persistentSpec, err := provisioner.Provision()
if err != nil { if err != nil {
t.Errorf("Provision() failed: %v", err) t.Errorf("Provision() failed: %v", err)
...@@ -209,6 +212,9 @@ func TestPlugin(t *testing.T) { ...@@ -209,6 +212,9 @@ func TestPlugin(t *testing.T) {
PersistentVolume: persistentSpec, PersistentVolume: persistentSpec,
} }
deleter, err := plug.(*awsElasticBlockStorePlugin).newDeleterInternal(volSpec, &fakePDManager{}) deleter, err := plug.(*awsElasticBlockStorePlugin).newDeleterInternal(volSpec, &fakePDManager{})
if err != nil {
t.Errorf("Error creating new deleter:%v", err)
}
err = deleter.Delete() err = deleter.Delete()
if err != nil { if err != nil {
t.Errorf("Deleter() failed: %v", err) t.Errorf("Deleter() failed: %v", err)
...@@ -251,7 +257,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { ...@@ -251,7 +257,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, clientset, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, clientset, nil))
plug, _ := plugMgr.FindPluginByName(awsElasticBlockStorePluginName) plug, _ := plugMgr.FindPluginByName(awsElasticBlockStorePluginName)
// readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes
...@@ -271,7 +277,7 @@ func TestMounterAndUnmounterTypeAssert(t *testing.T) { ...@@ -271,7 +277,7 @@ func TestMounterAndUnmounterTypeAssert(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/aws-ebs") plug, err := plugMgr.FindPluginByName("kubernetes.io/aws-ebs")
if err != nil { if err != nil {
...@@ -288,11 +294,17 @@ func TestMounterAndUnmounterTypeAssert(t *testing.T) { ...@@ -288,11 +294,17 @@ func TestMounterAndUnmounterTypeAssert(t *testing.T) {
} }
mounter, err := plug.(*awsElasticBlockStorePlugin).newMounterInternal(volume.NewSpecFromVolume(spec), types.UID("poduid"), &fakePDManager{}, &mount.FakeMounter{}) mounter, err := plug.(*awsElasticBlockStorePlugin).newMounterInternal(volume.NewSpecFromVolume(spec), types.UID("poduid"), &fakePDManager{}, &mount.FakeMounter{})
if err != nil {
t.Errorf("Error creating new mounter:%v", err)
}
if _, ok := mounter.(volume.Unmounter); ok { if _, ok := mounter.(volume.Unmounter); ok {
t.Errorf("Volume Mounter can be type-assert to Unmounter") t.Errorf("Volume Mounter can be type-assert to Unmounter")
} }
unmounter, err := plug.(*awsElasticBlockStorePlugin).newUnmounterInternal("vol1", types.UID("poduid"), &fakePDManager{}, &mount.FakeMounter{}) unmounter, err := plug.(*awsElasticBlockStorePlugin).newUnmounterInternal("vol1", types.UID("poduid"), &fakePDManager{}, &mount.FakeMounter{})
if err != nil {
t.Errorf("Error creating new unmounter:%v", err)
}
if _, ok := unmounter.(volume.Mounter); ok { if _, ok := unmounter.(volume.Mounter); ok {
t.Errorf("Volume Unmounter can be type-assert to Mounter") t.Errorf("Volume Unmounter can be type-assert to Mounter")
} }
......
...@@ -33,7 +33,7 @@ func TestCanSupport(t *testing.T) { ...@@ -33,7 +33,7 @@ func TestCanSupport(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName(azureDataDiskPluginName) plug, err := plugMgr.FindPluginByName(azureDataDiskPluginName)
if err != nil { if err != nil {
......
...@@ -41,7 +41,7 @@ func TestCanSupport(t *testing.T) { ...@@ -41,7 +41,7 @@ func TestCanSupport(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/azure-file") plug, err := plugMgr.FindPluginByName("kubernetes.io/azure-file")
if err != nil { if err != nil {
...@@ -65,7 +65,7 @@ func TestGetAccessModes(t *testing.T) { ...@@ -65,7 +65,7 @@ func TestGetAccessModes(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/azure-file") plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/azure-file")
if err != nil { if err != nil {
...@@ -131,7 +131,7 @@ func TestPluginWithOtherCloudProvider(t *testing.T) { ...@@ -131,7 +131,7 @@ func TestPluginWithOtherCloudProvider(t *testing.T) {
func testPlugin(t *testing.T, tmpDir string, volumeHost volume.VolumeHost) { func testPlugin(t *testing.T, tmpDir string, volumeHost volume.VolumeHost) {
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumeHost) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumeHost)
plug, err := plugMgr.FindPluginByName("kubernetes.io/azure-file") plug, err := plugMgr.FindPluginByName("kubernetes.io/azure-file")
if err != nil { if err != nil {
...@@ -228,7 +228,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { ...@@ -228,7 +228,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
client := fake.NewSimpleClientset(pv, claim) client := fake.NewSimpleClientset(pv, claim)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost("/tmp/fake", client, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost("/tmp/fake", client, nil))
plug, _ := plugMgr.FindPluginByName(azureFilePluginName) plug, _ := plugMgr.FindPluginByName(azureFilePluginName)
// readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes
...@@ -260,7 +260,7 @@ func TestMounterAndUnmounterTypeAssert(t *testing.T) { ...@@ -260,7 +260,7 @@ func TestMounterAndUnmounterTypeAssert(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/azure-file") plug, err := plugMgr.FindPluginByName("kubernetes.io/azure-file")
if err != nil { if err != nil {
......
...@@ -36,7 +36,7 @@ func TestCanSupport(t *testing.T) { ...@@ -36,7 +36,7 @@ func TestCanSupport(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/cephfs") plug, err := plugMgr.FindPluginByName("kubernetes.io/cephfs")
if err != nil { if err != nil {
t.Errorf("Can't find the plugin by name") t.Errorf("Can't find the plugin by name")
...@@ -59,7 +59,7 @@ func TestPlugin(t *testing.T) { ...@@ -59,7 +59,7 @@ func TestPlugin(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/cephfs") plug, err := plugMgr.FindPluginByName("kubernetes.io/cephfs")
if err != nil { if err != nil {
t.Errorf("Can't find the plugin by name") t.Errorf("Can't find the plugin by name")
...@@ -123,7 +123,7 @@ func TestConstructVolumeSpec(t *testing.T) { ...@@ -123,7 +123,7 @@ func TestConstructVolumeSpec(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/cephfs") plug, err := plugMgr.FindPluginByName("kubernetes.io/cephfs")
if err != nil { if err != nil {
t.Errorf("can't find cephfs plugin by name") t.Errorf("can't find cephfs plugin by name")
......
...@@ -650,6 +650,10 @@ func (instances *instances) InstanceTypeByProviderID(providerID string) (string, ...@@ -650,6 +650,10 @@ func (instances *instances) InstanceTypeByProviderID(providerID string) (string,
return "", errors.New("Not implemented") return "", errors.New("Not implemented")
} }
func (instances *instances) InstanceExistsByProviderID(providerID string) (bool, error) {
return false, errors.New("unimplemented")
}
func (instances *instances) List(filter string) ([]types.NodeName, error) { func (instances *instances) List(filter string) ([]types.NodeName, error) {
return []types.NodeName{}, errors.New("Not implemented") return []types.NodeName{}, errors.New("Not implemented")
} }
......
...@@ -38,7 +38,7 @@ func TestCanSupport(t *testing.T) { ...@@ -38,7 +38,7 @@ func TestCanSupport(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/cinder") plug, err := plugMgr.FindPluginByName("kubernetes.io/cinder")
if err != nil { if err != nil {
...@@ -134,7 +134,7 @@ func TestPlugin(t *testing.T) { ...@@ -134,7 +134,7 @@ func TestPlugin(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/cinder") plug, err := plugMgr.FindPluginByName("kubernetes.io/cinder")
if err != nil { if err != nil {
......
...@@ -272,7 +272,7 @@ func TestCanSupport(t *testing.T) { ...@@ -272,7 +272,7 @@ func TestCanSupport(t *testing.T) {
pluginMgr := volume.VolumePluginMgr{} pluginMgr := volume.VolumePluginMgr{}
tempDir, host := newTestHost(t, nil) tempDir, host := newTestHost(t, nil)
defer os.RemoveAll(tempDir) defer os.RemoveAll(tempDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host) pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plugin, err := pluginMgr.FindPluginByName(configMapPluginName) plugin, err := pluginMgr.FindPluginByName(configMapPluginName)
if err != nil { if err != nil {
...@@ -304,7 +304,7 @@ func TestPlugin(t *testing.T) { ...@@ -304,7 +304,7 @@ func TestPlugin(t *testing.T) {
) )
defer os.RemoveAll(tempDir) defer os.RemoveAll(tempDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host) pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plugin, err := pluginMgr.FindPluginByName(configMapPluginName) plugin, err := pluginMgr.FindPluginByName(configMapPluginName)
if err != nil { if err != nil {
...@@ -368,7 +368,7 @@ func TestPluginReboot(t *testing.T) { ...@@ -368,7 +368,7 @@ func TestPluginReboot(t *testing.T) {
) )
defer os.RemoveAll(rootDir) defer os.RemoveAll(rootDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host) pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plugin, err := pluginMgr.FindPluginByName(configMapPluginName) plugin, err := pluginMgr.FindPluginByName(configMapPluginName)
if err != nil { if err != nil {
...@@ -424,7 +424,7 @@ func TestPluginOptional(t *testing.T) { ...@@ -424,7 +424,7 @@ func TestPluginOptional(t *testing.T) {
volumeSpec.VolumeSource.ConfigMap.Optional = &trueVal volumeSpec.VolumeSource.ConfigMap.Optional = &trueVal
defer os.RemoveAll(tempDir) defer os.RemoveAll(tempDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host) pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plugin, err := pluginMgr.FindPluginByName(configMapPluginName) plugin, err := pluginMgr.FindPluginByName(configMapPluginName)
if err != nil { if err != nil {
...@@ -499,7 +499,7 @@ func TestPluginKeysOptional(t *testing.T) { ...@@ -499,7 +499,7 @@ func TestPluginKeysOptional(t *testing.T) {
volumeSpec.VolumeSource.ConfigMap.Optional = &trueVal volumeSpec.VolumeSource.ConfigMap.Optional = &trueVal
defer os.RemoveAll(tempDir) defer os.RemoveAll(tempDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host) pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plugin, err := pluginMgr.FindPluginByName(configMapPluginName) plugin, err := pluginMgr.FindPluginByName(configMapPluginName)
if err != nil { if err != nil {
......
...@@ -54,7 +54,7 @@ func TestCanSupport(t *testing.T) { ...@@ -54,7 +54,7 @@ func TestCanSupport(t *testing.T) {
pluginMgr := volume.VolumePluginMgr{} pluginMgr := volume.VolumePluginMgr{}
tmpDir, host := newTestHost(t, nil) tmpDir, host := newTestHost(t, nil)
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host) pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName) plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName)
if err != nil { if err != nil {
...@@ -219,7 +219,7 @@ func newDownwardAPITest(t *testing.T, name string, volumeFiles, podLabels, podAn ...@@ -219,7 +219,7 @@ func newDownwardAPITest(t *testing.T, name string, volumeFiles, podLabels, podAn
pluginMgr := volume.VolumePluginMgr{} pluginMgr := volume.VolumePluginMgr{}
rootDir, host := newTestHost(t, clientset) rootDir, host := newTestHost(t, clientset)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host) pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName) plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName)
if err != nil { if err != nil {
t.Errorf("Can't find the plugin by name") t.Errorf("Can't find the plugin by name")
......
...@@ -36,7 +36,7 @@ import ( ...@@ -36,7 +36,7 @@ import (
// Construct an instance of a plugin, by name. // Construct an instance of a plugin, by name.
func makePluginUnderTest(t *testing.T, plugName, basePath string) volume.VolumePlugin { func makePluginUnderTest(t *testing.T, plugName, basePath string) volume.VolumePlugin {
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(basePath, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(basePath, nil, nil))
plug, err := plugMgr.FindPluginByName(plugName) plug, err := plugMgr.FindPluginByName(plugName)
if err != nil { if err != nil {
......
...@@ -39,7 +39,7 @@ func TestCanSupport(t *testing.T) { ...@@ -39,7 +39,7 @@ func TestCanSupport(t *testing.T) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/fc") plug, err := plugMgr.FindPluginByName("kubernetes.io/fc")
if err != nil { if err != nil {
...@@ -61,7 +61,7 @@ func TestGetAccessModes(t *testing.T) { ...@@ -61,7 +61,7 @@ func TestGetAccessModes(t *testing.T) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/fc") plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/fc")
if err != nil { if err != nil {
...@@ -132,7 +132,7 @@ func doTestPlugin(t *testing.T, spec *volume.Spec) { ...@@ -132,7 +132,7 @@ func doTestPlugin(t *testing.T, spec *volume.Spec) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/fc") plug, err := plugMgr.FindPluginByName("kubernetes.io/fc")
if err != nil { if err != nil {
...@@ -202,7 +202,7 @@ func doTestPluginNilMounter(t *testing.T, spec *volume.Spec) { ...@@ -202,7 +202,7 @@ func doTestPluginNilMounter(t *testing.T, spec *volume.Spec) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/fc") plug, err := plugMgr.FindPluginByName("kubernetes.io/fc")
if err != nil { if err != nil {
...@@ -355,7 +355,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { ...@@ -355,7 +355,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
client := fake.NewSimpleClientset(pv, claim) client := fake.NewSimpleClientset(pv, claim)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, client, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, client, nil))
plug, _ := plugMgr.FindPluginByName(fcPluginName) plug, _ := plugMgr.FindPluginByName(fcPluginName)
// readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes
......
...@@ -29,9 +29,11 @@ go_library( ...@@ -29,9 +29,11 @@ go_library(
"//pkg/util/strings:go_default_library", "//pkg/util/strings:go_default_library",
"//pkg/volume:go_default_library", "//pkg/volume:go_default_library",
"//pkg/volume/util:go_default_library", "//pkg/volume/util:go_default_library",
"//vendor/github.com/fsnotify/fsnotify:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library",
], ],
) )
......
...@@ -174,7 +174,7 @@ func TestCanSupport(t *testing.T) { ...@@ -174,7 +174,7 @@ func TestCanSupport(t *testing.T) {
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
installPluginUnderTest(t, "kubernetes.io", "fakeAttacher", tmpDir, execScriptTempl1, nil) installPluginUnderTest(t, "kubernetes.io", "fakeAttacher", tmpDir, execScriptTempl1, nil)
plugMgr.InitPlugins(ProbeVolumePlugins(tmpDir), volumetest.NewFakeVolumeHost("fake", nil, nil)) plugMgr.InitPlugins(nil, GetDynamicPluginProber(tmpDir), volumetest.NewFakeVolumeHost("fake", nil, nil))
plugin, err := plugMgr.FindPluginByName("flexvolume-kubernetes.io/fakeAttacher") plugin, err := plugMgr.FindPluginByName("flexvolume-kubernetes.io/fakeAttacher")
if err != nil { if err != nil {
t.Fatalf("Can't find the plugin by name") t.Fatalf("Can't find the plugin by name")
...@@ -202,7 +202,7 @@ func TestGetAccessModes(t *testing.T) { ...@@ -202,7 +202,7 @@ func TestGetAccessModes(t *testing.T) {
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
installPluginUnderTest(t, "kubernetes.io", "fakeAttacher", tmpDir, execScriptTempl1, nil) installPluginUnderTest(t, "kubernetes.io", "fakeAttacher", tmpDir, execScriptTempl1, nil)
plugMgr.InitPlugins(ProbeVolumePlugins(tmpDir), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(nil, GetDynamicPluginProber(tmpDir), volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plugin, err := plugMgr.FindPersistentPluginByName("flexvolume-kubernetes.io/fakeAttacher") plugin, err := plugMgr.FindPersistentPluginByName("flexvolume-kubernetes.io/fakeAttacher")
if err != nil { if err != nil {
......
...@@ -19,28 +19,217 @@ package flexvolume ...@@ -19,28 +19,217 @@ package flexvolume
import ( import (
"io/ioutil" "io/ioutil"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume"
"os"
"fmt"
"path/filepath"
"sync"
"time"
"github.com/fsnotify/fsnotify"
"k8s.io/apimachinery/pkg/util/errors"
)
type flexVolumeProber struct {
mutex sync.Mutex
pluginDir string // Flexvolume driver directory
watcher *fsnotify.Watcher
probeNeeded bool // Must only read and write this through testAndSetProbeNeeded.
lastUpdated time.Time // Last time probeNeeded was updated.
watchEventCount int
}
const (
// TODO (cxing) Tune these params based on test results.
// watchEventLimit is the max allowable number of processed watches within watchEventInterval.
watchEventInterval = 5 * time.Second
watchEventLimit = 20
) )
// This is the primary entrypoint for volume plugins. func GetDynamicPluginProber(pluginDir string) volume.DynamicPluginProber {
func ProbeVolumePlugins(pluginDir string) []volume.VolumePlugin { return &flexVolumeProber{pluginDir: pluginDir}
plugins := []volume.VolumePlugin{} }
func (prober *flexVolumeProber) Init() error {
prober.testAndSetProbeNeeded(true)
prober.lastUpdated = time.Now()
if err := prober.createPluginDir(); err != nil {
return err
}
if err := prober.initWatcher(); err != nil {
return err
}
go func() {
defer prober.watcher.Close()
for {
select {
case event := <-prober.watcher.Events:
if err := prober.handleWatchEvent(event); err != nil {
glog.Errorf("Flexvolume prober watch: %s", err)
}
case err := <-prober.watcher.Errors:
glog.Errorf("Received an error from watcher: %s", err)
}
}
}()
files, _ := ioutil.ReadDir(pluginDir) return nil
}
// Probes for Flexvolume drivers.
// If a filesystem update has occurred since the last probe, updated = true
// and the list of probed plugins is returned.
// Otherwise, update = false and probedPlugins = nil.
//
// If an error occurs, updated and plugins are set arbitrarily.
func (prober *flexVolumeProber) Probe() (updated bool, plugins []volume.VolumePlugin, err error) {
probeNeeded := prober.testAndSetProbeNeeded(false)
if !probeNeeded {
return false, nil, nil
}
files, err := ioutil.ReadDir(prober.pluginDir)
if err != nil {
return false, nil, fmt.Errorf("Error reading the Flexvolume directory: %s", err)
}
plugins = []volume.VolumePlugin{}
allErrs := []error{}
for _, f := range files { for _, f := range files {
// only directories are counted as plugins // only directories with names that do not begin with '.' are counted as plugins
// and pluginDir/dirname/dirname should be an executable // and pluginDir/dirname/dirname should be an executable
// unless dirname contains '~' for escaping namespace // unless dirname contains '~' for escaping namespace
// e.g. dirname = vendor~cifs // e.g. dirname = vendor~cifs
// then, executable will be pluginDir/dirname/cifs // then, executable will be pluginDir/dirname/cifs
if f.IsDir() { if f.IsDir() && filepath.Base(f.Name())[0] != '.' {
plugin, err := NewFlexVolumePlugin(pluginDir, f.Name()) plugin, pluginErr := NewFlexVolumePlugin(prober.pluginDir, f.Name())
if err != nil { if pluginErr != nil {
pluginErr = fmt.Errorf(
"Error creating Flexvolume plugin from directory %s, skipping. Error: %s",
f.Name(), pluginErr)
allErrs = append(allErrs, pluginErr)
continue continue
} }
plugins = append(plugins, plugin) plugins = append(plugins, plugin)
} }
} }
return plugins
return true, plugins, errors.NewAggregate(allErrs)
}
func (prober *flexVolumeProber) handleWatchEvent(event fsnotify.Event) error {
// event.Name is the watched path.
if filepath.Base(event.Name)[0] == '.' {
// Ignore files beginning with '.'
return nil
}
eventPathAbs, err := filepath.Abs(event.Name)
if err != nil {
return err
}
pluginDirAbs, err := filepath.Abs(prober.pluginDir)
if err != nil {
return err
}
// If the Flexvolume plugin directory is removed, need to recreate it
// in order to keep it under watch.
if eventOpIs(event, fsnotify.Remove) && eventPathAbs == pluginDirAbs {
glog.Warningf("Flexvolume plugin directory at %s is removed. Recreating.", pluginDirAbs)
if err := prober.createPluginDir(); err != nil {
return err
}
if err := prober.addWatchRecursive(pluginDirAbs); err != nil {
return err
}
} else if eventOpIs(event, fsnotify.Create) {
if err := prober.addWatchRecursive(eventPathAbs); err != nil {
return err
}
}
prober.updateProbeNeeded()
return nil
}
func (prober *flexVolumeProber) updateProbeNeeded() {
// Within 'watchEventInterval' seconds, a max of 'watchEventLimit' watch events is processed.
// The watch event will not be registered if the limit is reached.
// This prevents increased disk usage from Probe() being triggered too frequently (either
// accidentally or maliciously).
if time.Since(prober.lastUpdated) > watchEventInterval {
// Update, then reset the timer and watch count.
prober.testAndSetProbeNeeded(true)
prober.lastUpdated = time.Now()
prober.watchEventCount = 1
} else if prober.watchEventCount < watchEventLimit {
prober.testAndSetProbeNeeded(true)
prober.watchEventCount++
}
}
// Recursively adds to watch all directories inside and including the file specified by the given filename.
// If the file is a symlink to a directory, it will watch the symlink but not any of the subdirectories.
//
// Each file or directory change triggers two events: one from the watch on itself, another from the watch
// on its parent directory.
func (prober *flexVolumeProber) addWatchRecursive(filename string) error {
addWatch := func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
if err := prober.watcher.Add(path); err != nil {
glog.Errorf("Error recursively adding watch: %v", err)
}
}
return nil
}
return filepath.Walk(filename, addWatch)
}
// Creates a new filesystem watcher and adds watches for the plugin directory
// and all of its subdirectories.
func (prober *flexVolumeProber) initWatcher() error {
var err error
if prober.watcher, err = fsnotify.NewWatcher(); err != nil {
return fmt.Errorf("Error creating new watcher: %s", err)
}
if err = prober.addWatchRecursive(prober.pluginDir); err != nil {
return fmt.Errorf("Error adding watch on Flexvolume directory: %s", err)
}
return nil
}
// Creates the plugin directory, if it doesn't already exist.
func (prober *flexVolumeProber) createPluginDir() error {
if _, err := os.Stat(prober.pluginDir); os.IsNotExist(err) {
err := os.MkdirAll(prober.pluginDir, 0755)
if err != nil {
return fmt.Errorf("Error (re-)creating driver directory: %s", err)
}
}
return nil
}
func (prober *flexVolumeProber) testAndSetProbeNeeded(newval bool) (oldval bool) {
prober.mutex.Lock()
defer prober.mutex.Unlock()
oldval, prober.probeNeeded = prober.probeNeeded, newval
return
}
func eventOpIs(event fsnotify.Event, op fsnotify.Op) bool {
return event.Op&op == op
} }
...@@ -127,7 +127,7 @@ func newInitializedVolumePlugMgr(t *testing.T) (*volume.VolumePluginMgr, string) ...@@ -127,7 +127,7 @@ func newInitializedVolumePlugMgr(t *testing.T) (*volume.VolumePluginMgr, string)
plugMgr := &volume.VolumePluginMgr{} plugMgr := &volume.VolumePluginMgr{}
dir, err := utiltesting.MkTmpdir("flocker") dir, err := utiltesting.MkTmpdir("flocker")
assert.NoError(t, err) assert.NoError(t, err)
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(dir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(dir, nil, nil))
return plugMgr, dir return plugMgr, dir
} }
...@@ -138,7 +138,7 @@ func TestPlugin(t *testing.T) { ...@@ -138,7 +138,7 @@ func TestPlugin(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/flocker") plug, err := plugMgr.FindPluginByName("kubernetes.io/flocker")
if err != nil { if err != nil {
......
...@@ -35,7 +35,7 @@ func newTestableProvisioner(assert *assert.Assertions, options volume.VolumeOpti ...@@ -35,7 +35,7 @@ func newTestableProvisioner(assert *assert.Assertions, options volume.VolumeOpti
assert.NoError(err, fmt.Sprintf("can't make a temp dir: %v", err)) assert.NoError(err, fmt.Sprintf("can't make a temp dir: %v", err))
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName(pluginName) plug, err := plugMgr.FindPluginByName(pluginName)
assert.NoError(err, "Can't find the plugin by name") assert.NoError(err, "Can't find the plugin by name")
......
...@@ -202,7 +202,8 @@ func newPlugin() *gcePersistentDiskPlugin { ...@@ -202,7 +202,8 @@ func newPlugin() *gcePersistentDiskPlugin {
host := volumetest.NewFakeVolumeHost( host := volumetest.NewFakeVolumeHost(
"/tmp", /* rootDir */ "/tmp", /* rootDir */
nil, /* kubeClient */ nil, /* kubeClient */
nil /* plugins */) nil, /* plugins */
)
plugins := ProbeVolumePlugins() plugins := ProbeVolumePlugins()
plugin := plugins[0] plugin := plugins[0]
plugin.Init(host) plugin.Init(host)
......
...@@ -39,7 +39,7 @@ func TestCanSupport(t *testing.T) { ...@@ -39,7 +39,7 @@ func TestCanSupport(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/gce-pd") plug, err := plugMgr.FindPluginByName("kubernetes.io/gce-pd")
if err != nil { if err != nil {
...@@ -63,7 +63,7 @@ func TestGetAccessModes(t *testing.T) { ...@@ -63,7 +63,7 @@ func TestGetAccessModes(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/gce-pd") plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/gce-pd")
if err != nil { if err != nil {
...@@ -106,7 +106,7 @@ func TestPlugin(t *testing.T) { ...@@ -106,7 +106,7 @@ func TestPlugin(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/gce-pd") plug, err := plugMgr.FindPluginByName("kubernetes.io/gce-pd")
if err != nil { if err != nil {
...@@ -250,7 +250,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { ...@@ -250,7 +250,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, client, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, client, nil))
plug, _ := plugMgr.FindPluginByName(gcePersistentDiskPluginName) plug, _ := plugMgr.FindPluginByName(gcePersistentDiskPluginName)
// readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes
......
...@@ -47,7 +47,7 @@ func TestCanSupport(t *testing.T) { ...@@ -47,7 +47,7 @@ func TestCanSupport(t *testing.T) {
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
tempDir, host := newTestHost(t) tempDir, host := newTestHost(t)
defer os.RemoveAll(tempDir) defer os.RemoveAll(tempDir)
plugMgr.InitPlugins(ProbeVolumePlugins(), host) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plug, err := plugMgr.FindPluginByName("kubernetes.io/git-repo") plug, err := plugMgr.FindPluginByName("kubernetes.io/git-repo")
if err != nil { if err != nil {
...@@ -225,7 +225,7 @@ func doTestPlugin(scenario struct { ...@@ -225,7 +225,7 @@ func doTestPlugin(scenario struct {
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
rootDir, host := newTestHost(t) rootDir, host := newTestHost(t)
defer os.RemoveAll(rootDir) defer os.RemoveAll(rootDir)
plugMgr.InitPlugins(ProbeVolumePlugins(), host) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plug, err := plugMgr.FindPluginByName("kubernetes.io/git-repo") plug, err := plugMgr.FindPluginByName("kubernetes.io/git-repo")
if err != nil { if err != nil {
......
...@@ -43,7 +43,7 @@ func TestCanSupport(t *testing.T) { ...@@ -43,7 +43,7 @@ func TestCanSupport(t *testing.T) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/glusterfs") plug, err := plugMgr.FindPluginByName("kubernetes.io/glusterfs")
if err != nil { if err != nil {
t.Errorf("Can't find the plugin by name") t.Errorf("Can't find the plugin by name")
...@@ -67,7 +67,7 @@ func TestGetAccessModes(t *testing.T) { ...@@ -67,7 +67,7 @@ func TestGetAccessModes(t *testing.T) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/glusterfs") plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/glusterfs")
if err != nil { if err != nil {
...@@ -95,7 +95,7 @@ func doTestPlugin(t *testing.T, spec *volume.Spec) { ...@@ -95,7 +95,7 @@ func doTestPlugin(t *testing.T, spec *volume.Spec) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/glusterfs") plug, err := plugMgr.FindPluginByName("kubernetes.io/glusterfs")
if err != nil { if err != nil {
t.Errorf("Can't find the plugin by name") t.Errorf("Can't find the plugin by name")
...@@ -213,7 +213,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { ...@@ -213,7 +213,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
client := fake.NewSimpleClientset(pv, claim, ep) client := fake.NewSimpleClientset(pv, claim, ep)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, client, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, client, nil))
plug, _ := plugMgr.FindPluginByName(glusterfsPluginName) plug, _ := plugMgr.FindPluginByName(glusterfsPluginName)
// readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes
......
...@@ -51,7 +51,7 @@ func newHostPathTypeList(pathType ...string) []*v1.HostPathType { ...@@ -51,7 +51,7 @@ func newHostPathTypeList(pathType ...string) []*v1.HostPathType {
func TestCanSupport(t *testing.T) { func TestCanSupport(t *testing.T) {
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), volumetest.NewFakeVolumeHost("fake", nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), nil /* prober */, volumetest.NewFakeVolumeHost("fake", nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/host-path") plug, err := plugMgr.FindPluginByName("kubernetes.io/host-path")
if err != nil { if err != nil {
...@@ -73,7 +73,7 @@ func TestCanSupport(t *testing.T) { ...@@ -73,7 +73,7 @@ func TestCanSupport(t *testing.T) {
func TestGetAccessModes(t *testing.T) { func TestGetAccessModes(t *testing.T) {
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), volumetest.NewFakeVolumeHost("/tmp/fake", nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), nil /* prober */, volumetest.NewFakeVolumeHost("/tmp/fake", nil, nil))
plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/host-path") plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/host-path")
if err != nil { if err != nil {
...@@ -87,7 +87,7 @@ func TestGetAccessModes(t *testing.T) { ...@@ -87,7 +87,7 @@ func TestGetAccessModes(t *testing.T) {
func TestRecycler(t *testing.T) { func TestRecycler(t *testing.T) {
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
pluginHost := volumetest.NewFakeVolumeHost("/tmp/fake", nil, nil) pluginHost := volumetest.NewFakeVolumeHost("/tmp/fake", nil, nil)
plugMgr.InitPlugins([]volume.VolumePlugin{&hostPathPlugin{nil, volume.VolumeConfig{}}}, pluginHost) plugMgr.InitPlugins([]volume.VolumePlugin{&hostPathPlugin{nil, volume.VolumeConfig{}}}, nil, pluginHost)
spec := &volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{HostPath: &v1.HostPathVolumeSource{Path: "/foo"}}}}} spec := &volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{HostPath: &v1.HostPathVolumeSource{Path: "/foo"}}}}}
_, err := plugMgr.FindRecyclablePluginBySpec(spec) _, err := plugMgr.FindRecyclablePluginBySpec(spec)
...@@ -106,7 +106,7 @@ func TestDeleter(t *testing.T) { ...@@ -106,7 +106,7 @@ func TestDeleter(t *testing.T) {
} }
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), volumetest.NewFakeVolumeHost("/tmp/fake", nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), nil /* prober */, volumetest.NewFakeVolumeHost("/tmp/fake", nil, nil))
spec := &volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{HostPath: &v1.HostPathVolumeSource{Path: tempPath}}}}} spec := &volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{HostPath: &v1.HostPathVolumeSource{Path: tempPath}}}}}
plug, err := plugMgr.FindDeletablePluginBySpec(spec) plug, err := plugMgr.FindDeletablePluginBySpec(spec)
...@@ -140,7 +140,7 @@ func TestDeleterTempDir(t *testing.T) { ...@@ -140,7 +140,7 @@ func TestDeleterTempDir(t *testing.T) {
for name, test := range tests { for name, test := range tests {
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), volumetest.NewFakeVolumeHost("/tmp/fake", nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), nil /* prober */, volumetest.NewFakeVolumeHost("/tmp/fake", nil, nil))
spec := &volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{HostPath: &v1.HostPathVolumeSource{Path: test.path}}}}} spec := &volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{HostPath: &v1.HostPathVolumeSource{Path: test.path}}}}}
plug, _ := plugMgr.FindDeletablePluginBySpec(spec) plug, _ := plugMgr.FindDeletablePluginBySpec(spec)
deleter, _ := plug.NewDeleter(spec) deleter, _ := plug.NewDeleter(spec)
...@@ -161,6 +161,7 @@ func TestProvisioner(t *testing.T) { ...@@ -161,6 +161,7 @@ func TestProvisioner(t *testing.T) {
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{ProvisioningEnabled: true}), plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{ProvisioningEnabled: true}),
nil,
volumetest.NewFakeVolumeHost("/tmp/fake", nil, nil)) volumetest.NewFakeVolumeHost("/tmp/fake", nil, nil))
spec := &volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{HostPath: &v1.HostPathVolumeSource{Path: tempPath}}}}} spec := &volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{HostPath: &v1.HostPathVolumeSource{Path: tempPath}}}}}
plug, err := plugMgr.FindCreatablePluginBySpec(spec) plug, err := plugMgr.FindCreatablePluginBySpec(spec)
...@@ -199,7 +200,7 @@ func TestProvisioner(t *testing.T) { ...@@ -199,7 +200,7 @@ func TestProvisioner(t *testing.T) {
func TestInvalidHostPath(t *testing.T) { func TestInvalidHostPath(t *testing.T) {
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), volumetest.NewFakeVolumeHost("fake", nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), nil /* prober */, volumetest.NewFakeVolumeHost("fake", nil, nil))
plug, err := plugMgr.FindPluginByName(hostPathPluginName) plug, err := plugMgr.FindPluginByName(hostPathPluginName)
if err != nil { if err != nil {
...@@ -224,7 +225,7 @@ func TestInvalidHostPath(t *testing.T) { ...@@ -224,7 +225,7 @@ func TestInvalidHostPath(t *testing.T) {
func TestPlugin(t *testing.T) { func TestPlugin(t *testing.T) {
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), volumetest.NewFakeVolumeHost("fake", nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), nil /* prober */, volumetest.NewFakeVolumeHost("fake", nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/host-path") plug, err := plugMgr.FindPluginByName("kubernetes.io/host-path")
if err != nil { if err != nil {
...@@ -300,7 +301,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { ...@@ -300,7 +301,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
client := fake.NewSimpleClientset(pv, claim) client := fake.NewSimpleClientset(pv, claim)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), volumetest.NewFakeVolumeHost("/tmp/fake", client, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), nil /* prober */, volumetest.NewFakeVolumeHost("/tmp/fake", client, nil))
plug, _ := plugMgr.FindPluginByName(hostPathPluginName) plug, _ := plugMgr.FindPluginByName(hostPathPluginName)
// readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes
......
...@@ -39,7 +39,7 @@ func TestCanSupport(t *testing.T) { ...@@ -39,7 +39,7 @@ func TestCanSupport(t *testing.T) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/iscsi") plug, err := plugMgr.FindPluginByName("kubernetes.io/iscsi")
if err != nil { if err != nil {
...@@ -61,7 +61,7 @@ func TestGetAccessModes(t *testing.T) { ...@@ -61,7 +61,7 @@ func TestGetAccessModes(t *testing.T) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/iscsi") plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/iscsi")
if err != nil { if err != nil {
...@@ -132,7 +132,7 @@ func doTestPlugin(t *testing.T, spec *volume.Spec) { ...@@ -132,7 +132,7 @@ func doTestPlugin(t *testing.T, spec *volume.Spec) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/iscsi") plug, err := plugMgr.FindPluginByName("kubernetes.io/iscsi")
if err != nil { if err != nil {
...@@ -276,7 +276,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { ...@@ -276,7 +276,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
client := fake.NewSimpleClientset(pv, claim) client := fake.NewSimpleClientset(pv, claim)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, client, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, client, nil))
plug, _ := plugMgr.FindPluginByName(iscsiPluginName) plug, _ := plugMgr.FindPluginByName(iscsiPluginName)
// readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes
......
...@@ -294,6 +294,9 @@ func (util *ISCSIUtil) AttachDisk(b iscsiDiskMounter) error { ...@@ -294,6 +294,9 @@ func (util *ISCSIUtil) AttachDisk(b iscsiDiskMounter) error {
// mount it // mount it
globalPDPath := b.manager.MakeGlobalPDName(*b.iscsiDisk) globalPDPath := b.manager.MakeGlobalPDName(*b.iscsiDisk)
notMnt, err := b.mounter.IsLikelyNotMountPoint(globalPDPath) notMnt, err := b.mounter.IsLikelyNotMountPoint(globalPDPath)
if err != nil {
return fmt.Errorf("Heuristic determination of mount point failed:%v", err)
}
if !notMnt { if !notMnt {
glog.Infof("iscsi: %s already mounted", globalPDPath) glog.Infof("iscsi: %s already mounted", globalPDPath)
return nil return nil
......
...@@ -44,7 +44,7 @@ func getPlugin(t *testing.T) (string, volume.VolumePlugin) { ...@@ -44,7 +44,7 @@ func getPlugin(t *testing.T) (string, volume.VolumePlugin) {
} }
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName(localVolumePluginName) plug, err := plugMgr.FindPluginByName(localVolumePluginName)
if err != nil { if err != nil {
...@@ -64,7 +64,7 @@ func getPersistentPlugin(t *testing.T) (string, volume.PersistentVolumePlugin) { ...@@ -64,7 +64,7 @@ func getPersistentPlugin(t *testing.T) (string, volume.PersistentVolumePlugin) {
} }
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPersistentPluginByName(localVolumePluginName) plug, err := plugMgr.FindPersistentPluginByName(localVolumePluginName)
if err != nil { if err != nil {
...@@ -337,7 +337,7 @@ func TestUnsupportedPlugins(t *testing.T) { ...@@ -337,7 +337,7 @@ func TestUnsupportedPlugins(t *testing.T) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
spec := getTestVolume(false, tmpDir) spec := getTestVolume(false, tmpDir)
recyclePlug, err := plugMgr.FindRecyclablePluginBySpec(spec) recyclePlug, err := plugMgr.FindRecyclablePluginBySpec(spec)
......
...@@ -39,7 +39,7 @@ func TestCanSupport(t *testing.T) { ...@@ -39,7 +39,7 @@ func TestCanSupport(t *testing.T) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/nfs") plug, err := plugMgr.FindPluginByName("kubernetes.io/nfs")
if err != nil { if err != nil {
t.Errorf("Can't find the plugin by name") t.Errorf("Can't find the plugin by name")
...@@ -67,7 +67,7 @@ func TestGetAccessModes(t *testing.T) { ...@@ -67,7 +67,7 @@ func TestGetAccessModes(t *testing.T) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/nfs") plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/nfs")
if err != nil { if err != nil {
...@@ -86,7 +86,7 @@ func TestRecycler(t *testing.T) { ...@@ -86,7 +86,7 @@ func TestRecycler(t *testing.T) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins([]volume.VolumePlugin{&nfsPlugin{nil, volume.VolumeConfig{}}}, volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins([]volume.VolumePlugin{&nfsPlugin{nil, volume.VolumeConfig{}}}, nil, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
spec := &volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{NFS: &v1.NFSVolumeSource{Path: "/foo"}}}}} spec := &volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{NFS: &v1.NFSVolumeSource{Path: "/foo"}}}}}
_, plugin_err := plugMgr.FindRecyclablePluginBySpec(spec) _, plugin_err := plugMgr.FindRecyclablePluginBySpec(spec)
...@@ -112,7 +112,7 @@ func doTestPlugin(t *testing.T, spec *volume.Spec) { ...@@ -112,7 +112,7 @@ func doTestPlugin(t *testing.T, spec *volume.Spec) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/nfs") plug, err := plugMgr.FindPluginByName("kubernetes.io/nfs")
if err != nil { if err != nil {
t.Errorf("Can't find the plugin by name") t.Errorf("Can't find the plugin by name")
...@@ -240,7 +240,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { ...@@ -240,7 +240,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
client := fake.NewSimpleClientset(pv, claim) client := fake.NewSimpleClientset(pv, claim)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), volumetest.NewFakeVolumeHost(tmpDir, client, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, client, nil))
plug, _ := plugMgr.FindPluginByName(nfsPluginName) plug, _ := plugMgr.FindPluginByName(nfsPluginName)
// readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes
......
...@@ -37,7 +37,7 @@ func TestCanSupport(t *testing.T) { ...@@ -37,7 +37,7 @@ func TestCanSupport(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/photon-pd") plug, err := plugMgr.FindPluginByName("kubernetes.io/photon-pd")
if err != nil { if err != nil {
...@@ -61,7 +61,7 @@ func TestGetAccessModes(t *testing.T) { ...@@ -61,7 +61,7 @@ func TestGetAccessModes(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/photon-pd") plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/photon-pd")
if err != nil { if err != nil {
...@@ -106,7 +106,7 @@ func TestPlugin(t *testing.T) { ...@@ -106,7 +106,7 @@ func TestPlugin(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/photon-pd") plug, err := plugMgr.FindPluginByName("kubernetes.io/photon-pd")
if err != nil { if err != nil {
...@@ -179,6 +179,9 @@ func TestPlugin(t *testing.T) { ...@@ -179,6 +179,9 @@ func TestPlugin(t *testing.T) {
PersistentVolumeReclaimPolicy: v1.PersistentVolumeReclaimDelete, PersistentVolumeReclaimPolicy: v1.PersistentVolumeReclaimDelete,
} }
provisioner, err := plug.(*photonPersistentDiskPlugin).newProvisionerInternal(options, &fakePDManager{}) provisioner, err := plug.(*photonPersistentDiskPlugin).newProvisionerInternal(options, &fakePDManager{})
if err != nil {
t.Fatalf("Error creating new provisioner:%v", err)
}
persistentSpec, err := provisioner.Provision() persistentSpec, err := provisioner.Provision()
if err != nil { if err != nil {
t.Errorf("Provision() failed: %v", err) t.Errorf("Provision() failed: %v", err)
...@@ -198,6 +201,9 @@ func TestPlugin(t *testing.T) { ...@@ -198,6 +201,9 @@ func TestPlugin(t *testing.T) {
PersistentVolume: persistentSpec, PersistentVolume: persistentSpec,
} }
deleter, err := plug.(*photonPersistentDiskPlugin).newDeleterInternal(volSpec, &fakePDManager{}) deleter, err := plug.(*photonPersistentDiskPlugin).newDeleterInternal(volSpec, &fakePDManager{})
if err != nil {
t.Fatalf("Error creating new deleter:%v", err)
}
err = deleter.Delete() err = deleter.Delete()
if err != nil { if err != nil {
t.Errorf("Deleter() failed: %v", err) t.Errorf("Deleter() failed: %v", err)
...@@ -211,7 +217,7 @@ func TestMounterAndUnmounterTypeAssert(t *testing.T) { ...@@ -211,7 +217,7 @@ func TestMounterAndUnmounterTypeAssert(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/photon-pd") plug, err := plugMgr.FindPluginByName("kubernetes.io/photon-pd")
if err != nil { if err != nil {
...@@ -228,11 +234,17 @@ func TestMounterAndUnmounterTypeAssert(t *testing.T) { ...@@ -228,11 +234,17 @@ func TestMounterAndUnmounterTypeAssert(t *testing.T) {
} }
mounter, err := plug.(*photonPersistentDiskPlugin).newMounterInternal(volume.NewSpecFromVolume(spec), types.UID("poduid"), &fakePDManager{}, &mount.FakeMounter{}) mounter, err := plug.(*photonPersistentDiskPlugin).newMounterInternal(volume.NewSpecFromVolume(spec), types.UID("poduid"), &fakePDManager{}, &mount.FakeMounter{})
if err != nil {
t.Fatalf("Error creating new mounter:%v", err)
}
if _, ok := mounter.(volume.Unmounter); ok { if _, ok := mounter.(volume.Unmounter); ok {
t.Errorf("Volume Mounter can be type-assert to Unmounter") t.Errorf("Volume Mounter can be type-assert to Unmounter")
} }
unmounter, err := plug.(*photonPersistentDiskPlugin).newUnmounterInternal("vol1", types.UID("poduid"), &fakePDManager{}, &mount.FakeMounter{}) unmounter, err := plug.(*photonPersistentDiskPlugin).newUnmounterInternal("vol1", types.UID("poduid"), &fakePDManager{}, &mount.FakeMounter{})
if err != nil {
t.Fatalf("Error creating new unmounter:%v", err)
}
if _, ok := unmounter.(volume.Mounter); ok { if _, ok := unmounter.(volume.Mounter); ok {
t.Errorf("Volume Unmounter can be type-assert to Mounter") t.Errorf("Volume Unmounter can be type-assert to Mounter")
} }
......
...@@ -70,6 +70,17 @@ type VolumeOptions struct { ...@@ -70,6 +70,17 @@ type VolumeOptions struct {
Containerized bool Containerized bool
} }
type DynamicPluginProber interface {
Init() error
// If an update has occurred since the last probe, updated = true
// and the list of probed plugins is returned.
// Otherwise, update = false and probedPlugins = nil.
//
// If an error occurs, updated and probedPlugins are undefined.
Probe() (updated bool, probedPlugins []VolumePlugin, err error)
}
// VolumePlugin is an interface to volume plugins that can be used on a // VolumePlugin is an interface to volume plugins that can be used on a
// kubernetes node (e.g. by kubelet) to instantiate and manage volumes. // kubernetes node (e.g. by kubelet) to instantiate and manage volumes.
type VolumePlugin interface { type VolumePlugin interface {
...@@ -255,9 +266,11 @@ type VolumeHost interface { ...@@ -255,9 +266,11 @@ type VolumeHost interface {
// VolumePluginMgr tracks registered plugins. // VolumePluginMgr tracks registered plugins.
type VolumePluginMgr struct { type VolumePluginMgr struct {
mutex sync.Mutex mutex sync.Mutex
plugins map[string]VolumePlugin plugins map[string]VolumePlugin
Host VolumeHost prober DynamicPluginProber
probedPlugins []VolumePlugin
Host VolumeHost
} }
// Spec is an internal representation of a volume. All API volume types translate to Spec. // Spec is an internal representation of a volume. All API volume types translate to Spec.
...@@ -352,11 +365,24 @@ func NewSpecFromPersistentVolume(pv *v1.PersistentVolume, readOnly bool) *Spec { ...@@ -352,11 +365,24 @@ func NewSpecFromPersistentVolume(pv *v1.PersistentVolume, readOnly bool) *Spec {
// InitPlugins initializes each plugin. All plugins must have unique names. // InitPlugins initializes each plugin. All plugins must have unique names.
// This must be called exactly once before any New* methods are called on any // This must be called exactly once before any New* methods are called on any
// plugins. // plugins.
func (pm *VolumePluginMgr) InitPlugins(plugins []VolumePlugin, host VolumeHost) error { func (pm *VolumePluginMgr) InitPlugins(plugins []VolumePlugin, prober DynamicPluginProber, host VolumeHost) error {
pm.mutex.Lock() pm.mutex.Lock()
defer pm.mutex.Unlock() defer pm.mutex.Unlock()
pm.Host = host pm.Host = host
if prober == nil {
// Use a dummy prober to prevent nil deference.
pm.prober = &dummyPluginProber{}
} else {
pm.prober = prober
}
if err := pm.prober.Init(); err != nil {
// Prober init failure should not affect the initialization of other plugins.
glog.Errorf("Error initializing dynamic plugin prober: %s", err)
pm.prober = &dummyPluginProber{}
}
if pm.plugins == nil { if pm.plugins == nil {
pm.plugins = map[string]VolumePlugin{} pm.plugins = map[string]VolumePlugin{}
} }
...@@ -385,6 +411,21 @@ func (pm *VolumePluginMgr) InitPlugins(plugins []VolumePlugin, host VolumeHost) ...@@ -385,6 +411,21 @@ func (pm *VolumePluginMgr) InitPlugins(plugins []VolumePlugin, host VolumeHost)
return utilerrors.NewAggregate(allErrs) return utilerrors.NewAggregate(allErrs)
} }
func (pm *VolumePluginMgr) initProbedPlugin(probedPlugin VolumePlugin) error {
name := probedPlugin.GetPluginName()
if errs := validation.IsQualifiedName(name); len(errs) != 0 {
return fmt.Errorf("volume plugin has invalid name: %q: %s", name, strings.Join(errs, ";"))
}
err := probedPlugin.Init(pm.Host)
if err != nil {
return fmt.Errorf("Failed to load volume plugin %s, error: %s", name, err.Error())
}
glog.V(1).Infof("Loaded volume plugin %q", name)
return nil
}
// FindPluginBySpec looks for a plugin that can support a given volume // FindPluginBySpec looks for a plugin that can support a given volume
// specification. If no plugins can support or more than one plugin can // specification. If no plugins can support or more than one plugin can
// support it, return error. // support it, return error.
...@@ -396,19 +437,30 @@ func (pm *VolumePluginMgr) FindPluginBySpec(spec *Spec) (VolumePlugin, error) { ...@@ -396,19 +437,30 @@ func (pm *VolumePluginMgr) FindPluginBySpec(spec *Spec) (VolumePlugin, error) {
return nil, fmt.Errorf("Could not find plugin because volume spec is nil") return nil, fmt.Errorf("Could not find plugin because volume spec is nil")
} }
matches := []string{} matchedPluginNames := []string{}
matches := []VolumePlugin{}
for k, v := range pm.plugins { for k, v := range pm.plugins {
if v.CanSupport(spec) { if v.CanSupport(spec) {
matches = append(matches, k) matchedPluginNames = append(matchedPluginNames, k)
matches = append(matches, v)
} }
} }
pm.refreshProbedPlugins()
for _, plugin := range pm.probedPlugins {
if plugin.CanSupport(spec) {
matchedPluginNames = append(matchedPluginNames, plugin.GetPluginName())
matches = append(matches, plugin)
}
}
if len(matches) == 0 { if len(matches) == 0 {
return nil, fmt.Errorf("no volume plugin matched") return nil, fmt.Errorf("no volume plugin matched")
} }
if len(matches) > 1 { if len(matches) > 1 {
return nil, fmt.Errorf("multiple volume plugins matched: %s", strings.Join(matches, ",")) return nil, fmt.Errorf("multiple volume plugins matched: %s", strings.Join(matchedPluginNames, ","))
} }
return pm.plugins[matches[0]], nil return matches[0], nil
} }
// FindPluginByName fetches a plugin by name or by legacy name. If no plugin // FindPluginByName fetches a plugin by name or by legacy name. If no plugin
...@@ -418,19 +470,52 @@ func (pm *VolumePluginMgr) FindPluginByName(name string) (VolumePlugin, error) { ...@@ -418,19 +470,52 @@ func (pm *VolumePluginMgr) FindPluginByName(name string) (VolumePlugin, error) {
defer pm.mutex.Unlock() defer pm.mutex.Unlock()
// Once we can get rid of legacy names we can reduce this to a map lookup. // Once we can get rid of legacy names we can reduce this to a map lookup.
matches := []string{} matchedPluginNames := []string{}
matches := []VolumePlugin{}
for k, v := range pm.plugins { for k, v := range pm.plugins {
if v.GetPluginName() == name { if v.GetPluginName() == name {
matches = append(matches, k) matchedPluginNames = append(matchedPluginNames, k)
matches = append(matches, v)
} }
} }
pm.refreshProbedPlugins()
for _, plugin := range pm.probedPlugins {
if plugin.GetPluginName() == name {
matchedPluginNames = append(matchedPluginNames, plugin.GetPluginName())
matches = append(matches, plugin)
}
}
if len(matches) == 0 { if len(matches) == 0 {
return nil, fmt.Errorf("no volume plugin matched") return nil, fmt.Errorf("no volume plugin matched")
} }
if len(matches) > 1 { if len(matches) > 1 {
return nil, fmt.Errorf("multiple volume plugins matched: %s", strings.Join(matches, ",")) return nil, fmt.Errorf("multiple volume plugins matched: %s", strings.Join(matchedPluginNames, ","))
}
return matches[0], nil
}
// Check if probedPlugin cache update is required.
// If it is, initialize all probed plugins and replace the cache with them.
func (pm *VolumePluginMgr) refreshProbedPlugins() {
updated, plugins, err := pm.prober.Probe()
if err != nil {
glog.Errorf("Error dynamically probing plugins: %s", err)
return // Use cached plugins upon failure.
}
if updated {
pm.probedPlugins = []VolumePlugin{}
for _, plugin := range plugins {
if err := pm.initProbedPlugin(plugin); err != nil {
glog.Errorf("Error initializing dynamically probed plugin %s; error: %s",
plugin.GetPluginName(), err)
continue
}
pm.probedPlugins = append(pm.probedPlugins, plugin)
}
} }
return pm.plugins[matches[0]], nil
} }
// FindPersistentPluginBySpec looks for a persistent volume plugin that can // FindPersistentPluginBySpec looks for a persistent volume plugin that can
...@@ -618,3 +703,8 @@ func ValidateRecyclerPodTemplate(pod *v1.Pod) error { ...@@ -618,3 +703,8 @@ func ValidateRecyclerPodTemplate(pod *v1.Pod) error {
} }
return nil return nil
} }
type dummyPluginProber struct{}
func (*dummyPluginProber) Init() error { return nil }
func (*dummyPluginProber) Probe() (bool, []VolumePlugin, error) { return false, nil, nil }
...@@ -103,7 +103,8 @@ func newTestPlugin() []VolumePlugin { ...@@ -103,7 +103,8 @@ func newTestPlugin() []VolumePlugin {
func TestVolumePluginMgrFunc(t *testing.T) { func TestVolumePluginMgrFunc(t *testing.T) {
vpm := VolumePluginMgr{} vpm := VolumePluginMgr{}
vpm.InitPlugins(newTestPlugin(), nil) var prober DynamicPluginProber = nil // TODO (#51147) inject mock
vpm.InitPlugins(newTestPlugin(), prober, nil)
plug, err := vpm.FindPluginByName("testPlugin") plug, err := vpm.FindPluginByName("testPlugin")
if err != nil { if err != nil {
......
...@@ -41,7 +41,7 @@ func TestCanSupport(t *testing.T) { ...@@ -41,7 +41,7 @@ func TestCanSupport(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/portworx-volume") plug, err := plugMgr.FindPluginByName("kubernetes.io/portworx-volume")
if err != nil { if err != nil {
...@@ -65,7 +65,7 @@ func TestGetAccessModes(t *testing.T) { ...@@ -65,7 +65,7 @@ func TestGetAccessModes(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/portworx-volume") plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/portworx-volume")
if err != nil { if err != nil {
...@@ -135,7 +135,7 @@ func TestPlugin(t *testing.T) { ...@@ -135,7 +135,7 @@ func TestPlugin(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/portworx-volume") plug, err := plugMgr.FindPluginByName("kubernetes.io/portworx-volume")
if err != nil { if err != nil {
......
...@@ -670,7 +670,7 @@ func TestCanSupport(t *testing.T) { ...@@ -670,7 +670,7 @@ func TestCanSupport(t *testing.T) {
pluginMgr := volume.VolumePluginMgr{} pluginMgr := volume.VolumePluginMgr{}
tempDir, host := newTestHost(t, nil) tempDir, host := newTestHost(t, nil)
defer os.RemoveAll(tempDir) defer os.RemoveAll(tempDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host) pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plugin, err := pluginMgr.FindPluginByName(projectedPluginName) plugin, err := pluginMgr.FindPluginByName(projectedPluginName)
if err != nil { if err != nil {
...@@ -701,7 +701,7 @@ func TestPlugin(t *testing.T) { ...@@ -701,7 +701,7 @@ func TestPlugin(t *testing.T) {
rootDir, host = newTestHost(t, client) rootDir, host = newTestHost(t, client)
) )
defer os.RemoveAll(rootDir) defer os.RemoveAll(rootDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host) pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plugin, err := pluginMgr.FindPluginByName(projectedPluginName) plugin, err := pluginMgr.FindPluginByName(projectedPluginName)
if err != nil { if err != nil {
...@@ -765,7 +765,7 @@ func TestPluginReboot(t *testing.T) { ...@@ -765,7 +765,7 @@ func TestPluginReboot(t *testing.T) {
rootDir, host = newTestHost(t, client) rootDir, host = newTestHost(t, client)
) )
defer os.RemoveAll(rootDir) defer os.RemoveAll(rootDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host) pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plugin, err := pluginMgr.FindPluginByName(projectedPluginName) plugin, err := pluginMgr.FindPluginByName(projectedPluginName)
if err != nil { if err != nil {
...@@ -819,7 +819,7 @@ func TestPluginOptional(t *testing.T) { ...@@ -819,7 +819,7 @@ func TestPluginOptional(t *testing.T) {
) )
volumeSpec.VolumeSource.Projected.Sources[0].Secret.Optional = &trueVal volumeSpec.VolumeSource.Projected.Sources[0].Secret.Optional = &trueVal
defer os.RemoveAll(rootDir) defer os.RemoveAll(rootDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host) pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plugin, err := pluginMgr.FindPluginByName(projectedPluginName) plugin, err := pluginMgr.FindPluginByName(projectedPluginName)
if err != nil { if err != nil {
...@@ -896,7 +896,7 @@ func TestPluginOptionalKeys(t *testing.T) { ...@@ -896,7 +896,7 @@ func TestPluginOptionalKeys(t *testing.T) {
} }
volumeSpec.VolumeSource.Projected.Sources[0].Secret.Optional = &trueVal volumeSpec.VolumeSource.Projected.Sources[0].Secret.Optional = &trueVal
defer os.RemoveAll(rootDir) defer os.RemoveAll(rootDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host) pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plugin, err := pluginMgr.FindPluginByName(projectedPluginName) plugin, err := pluginMgr.FindPluginByName(projectedPluginName)
if err != nil { if err != nil {
......
...@@ -39,7 +39,7 @@ func TestCanSupport(t *testing.T) { ...@@ -39,7 +39,7 @@ func TestCanSupport(t *testing.T) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/quobyte") plug, err := plugMgr.FindPluginByName("kubernetes.io/quobyte")
if err != nil { if err != nil {
t.Errorf("Can't find the plugin by name") t.Errorf("Can't find the plugin by name")
...@@ -63,7 +63,7 @@ func TestGetAccessModes(t *testing.T) { ...@@ -63,7 +63,7 @@ func TestGetAccessModes(t *testing.T) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/quobyte") plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/quobyte")
if err != nil { if err != nil {
...@@ -91,7 +91,7 @@ func doTestPlugin(t *testing.T, spec *volume.Spec) { ...@@ -91,7 +91,7 @@ func doTestPlugin(t *testing.T, spec *volume.Spec) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/quobyte") plug, err := plugMgr.FindPluginByName("kubernetes.io/quobyte")
if err != nil { if err != nil {
t.Errorf("Can't find the plugin by name") t.Errorf("Can't find the plugin by name")
...@@ -187,7 +187,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { ...@@ -187,7 +187,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
client := fake.NewSimpleClientset(pv, claim) client := fake.NewSimpleClientset(pv, claim)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, client, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, client, nil))
plug, _ := plugMgr.FindPluginByName(quobytePluginName) plug, _ := plugMgr.FindPluginByName(quobytePluginName)
// readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes
......
...@@ -39,7 +39,7 @@ func TestCanSupport(t *testing.T) { ...@@ -39,7 +39,7 @@ func TestCanSupport(t *testing.T) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/rbd") plug, err := plugMgr.FindPluginByName("kubernetes.io/rbd")
if err != nil { if err != nil {
...@@ -104,7 +104,7 @@ func doTestPlugin(t *testing.T, spec *volume.Spec) { ...@@ -104,7 +104,7 @@ func doTestPlugin(t *testing.T, spec *volume.Spec) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/rbd") plug, err := plugMgr.FindPluginByName("kubernetes.io/rbd")
if err != nil { if err != nil {
...@@ -229,7 +229,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { ...@@ -229,7 +229,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
client := fake.NewSimpleClientset(pv, claim) client := fake.NewSimpleClientset(pv, claim)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, client, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, client, nil))
plug, _ := plugMgr.FindPluginByName(rbdPluginName) plug, _ := plugMgr.FindPluginByName(rbdPluginName)
// readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes
......
...@@ -212,10 +212,13 @@ func TestUtilLoadConfig(t *testing.T) { ...@@ -212,10 +212,13 @@ func TestUtilLoadConfig(t *testing.T) {
configFile := path.Join(tmpDir, sioConfigFileName) configFile := path.Join(tmpDir, sioConfigFileName)
if err := saveConfig(configFile, config); err != nil { if err := saveConfig(configFile, config); err != nil {
t.Fatal("failed while saving data", err) t.Fatalf("failed to save configFile %s error:%v", configFile, err)
} }
dataRcvd, err := loadConfig(configFile) dataRcvd, err := loadConfig(configFile)
if err != nil {
t.Fatalf("failed to load configFile %s error:%v", configFile, err)
}
if dataRcvd[confKey.gateway] != config[confKey.gateway] || if dataRcvd[confKey.gateway] != config[confKey.gateway] ||
dataRcvd[confKey.system] != config[confKey.system] { dataRcvd[confKey.system] != config[confKey.system] {
t.Fatal("loaded config data not matching saved config data") t.Fatal("loaded config data not matching saved config data")
......
...@@ -63,7 +63,7 @@ func newPluginMgr(t *testing.T) (*volume.VolumePluginMgr, string) { ...@@ -63,7 +63,7 @@ func newPluginMgr(t *testing.T) (*volume.VolumePluginMgr, string) {
fakeClient := fakeclient.NewSimpleClientset(config) fakeClient := fakeclient.NewSimpleClientset(config)
host := volumetest.NewFakeVolumeHost(tmpDir, fakeClient, nil) host := volumetest.NewFakeVolumeHost(tmpDir, fakeClient, nil)
plugMgr := &volume.VolumePluginMgr{} plugMgr := &volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), host) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
return plugMgr, tmpDir return plugMgr, tmpDir
} }
......
...@@ -275,7 +275,7 @@ func TestCanSupport(t *testing.T) { ...@@ -275,7 +275,7 @@ func TestCanSupport(t *testing.T) {
pluginMgr := volume.VolumePluginMgr{} pluginMgr := volume.VolumePluginMgr{}
tempDir, host := newTestHost(t, nil) tempDir, host := newTestHost(t, nil)
defer os.RemoveAll(tempDir) defer os.RemoveAll(tempDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host) pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plugin, err := pluginMgr.FindPluginByName(secretPluginName) plugin, err := pluginMgr.FindPluginByName(secretPluginName)
if err != nil { if err != nil {
...@@ -306,7 +306,7 @@ func TestPlugin(t *testing.T) { ...@@ -306,7 +306,7 @@ func TestPlugin(t *testing.T) {
rootDir, host = newTestHost(t, client) rootDir, host = newTestHost(t, client)
) )
defer os.RemoveAll(rootDir) defer os.RemoveAll(rootDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host) pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plugin, err := pluginMgr.FindPluginByName(secretPluginName) plugin, err := pluginMgr.FindPluginByName(secretPluginName)
if err != nil { if err != nil {
...@@ -379,7 +379,7 @@ func TestPluginReboot(t *testing.T) { ...@@ -379,7 +379,7 @@ func TestPluginReboot(t *testing.T) {
rootDir, host = newTestHost(t, client) rootDir, host = newTestHost(t, client)
) )
defer os.RemoveAll(rootDir) defer os.RemoveAll(rootDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host) pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plugin, err := pluginMgr.FindPluginByName(secretPluginName) plugin, err := pluginMgr.FindPluginByName(secretPluginName)
if err != nil { if err != nil {
...@@ -433,7 +433,7 @@ func TestPluginOptional(t *testing.T) { ...@@ -433,7 +433,7 @@ func TestPluginOptional(t *testing.T) {
) )
volumeSpec.Secret.Optional = &trueVal volumeSpec.Secret.Optional = &trueVal
defer os.RemoveAll(rootDir) defer os.RemoveAll(rootDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host) pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plugin, err := pluginMgr.FindPluginByName(secretPluginName) plugin, err := pluginMgr.FindPluginByName(secretPluginName)
if err != nil { if err != nil {
...@@ -510,7 +510,7 @@ func TestPluginOptionalKeys(t *testing.T) { ...@@ -510,7 +510,7 @@ func TestPluginOptionalKeys(t *testing.T) {
} }
volumeSpec.Secret.Optional = &trueVal volumeSpec.Secret.Optional = &trueVal
defer os.RemoveAll(rootDir) defer os.RemoveAll(rootDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host) pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
plugin, err := pluginMgr.FindPluginByName(secretPluginName) plugin, err := pluginMgr.FindPluginByName(secretPluginName)
if err != nil { if err != nil {
......
...@@ -39,7 +39,7 @@ func TestCanSupport(t *testing.T) { ...@@ -39,7 +39,7 @@ func TestCanSupport(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/storageos") plug, err := plugMgr.FindPluginByName("kubernetes.io/storageos")
if err != nil { if err != nil {
...@@ -63,7 +63,7 @@ func TestGetAccessModes(t *testing.T) { ...@@ -63,7 +63,7 @@ func TestGetAccessModes(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/storageos") plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/storageos")
if err != nil { if err != nil {
...@@ -138,7 +138,7 @@ func TestPlugin(t *testing.T) { ...@@ -138,7 +138,7 @@ func TestPlugin(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/storageos") plug, err := plugMgr.FindPluginByName("kubernetes.io/storageos")
if err != nil { if err != nil {
...@@ -353,7 +353,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { ...@@ -353,7 +353,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
client := fake.NewSimpleClientset(pv, claim) client := fake.NewSimpleClientset(pv, claim)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, client, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, client, nil))
plug, _ := plugMgr.FindPluginByName(storageosPluginName) plug, _ := plugMgr.FindPluginByName(storageosPluginName)
// readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes
......
...@@ -117,7 +117,7 @@ func TestCreateVolume(t *testing.T) { ...@@ -117,7 +117,7 @@ func TestCreateVolume(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, _ := plugMgr.FindPluginByName("kubernetes.io/storageos") plug, _ := plugMgr.FindPluginByName("kubernetes.io/storageos")
// Use real util with stubbed api // Use real util with stubbed api
...@@ -209,7 +209,7 @@ func TestAttachVolume(t *testing.T) { ...@@ -209,7 +209,7 @@ func TestAttachVolume(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, _ := plugMgr.FindPluginByName("kubernetes.io/storageos") plug, _ := plugMgr.FindPluginByName("kubernetes.io/storageos")
// Use real util with stubbed api // Use real util with stubbed api
......
...@@ -73,7 +73,7 @@ func newFakeVolumeHost(rootDir string, kubeClient clientset.Interface, plugins [ ...@@ -73,7 +73,7 @@ func newFakeVolumeHost(rootDir string, kubeClient clientset.Interface, plugins [
host.mounter = &mount.FakeMounter{} host.mounter = &mount.FakeMounter{}
host.writer = &io.StdWriter{} host.writer = &io.StdWriter{}
host.exec = mount.NewFakeExec(nil) host.exec = mount.NewFakeExec(nil)
host.pluginMgr.InitPlugins(plugins, host) host.pluginMgr.InitPlugins(plugins, nil /* prober */, host)
return host return host
} }
...@@ -768,7 +768,7 @@ func GetTestVolumePluginMgr( ...@@ -768,7 +768,7 @@ func GetTestVolumePluginMgr(
nil, /* plugins */ nil, /* plugins */
) )
plugins := ProbeVolumePlugins(VolumeConfig{}) plugins := ProbeVolumePlugins(VolumeConfig{})
if err := v.pluginMgr.InitPlugins(plugins, v); err != nil { if err := v.pluginMgr.InitPlugins(plugins, nil /* prober */, v); err != nil {
t.Fatal(err) t.Fatal(err)
} }
......
...@@ -37,7 +37,7 @@ func TestCanSupport(t *testing.T) { ...@@ -37,7 +37,7 @@ func TestCanSupport(t *testing.T) {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/vsphere-volume") plug, err := plugMgr.FindPluginByName("kubernetes.io/vsphere-volume")
if err != nil { if err != nil {
...@@ -90,7 +90,7 @@ func TestPlugin(t *testing.T) { ...@@ -90,7 +90,7 @@ func TestPlugin(t *testing.T) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
plug, err := plugMgr.FindPluginByName("kubernetes.io/vsphere-volume") plug, err := plugMgr.FindPluginByName("kubernetes.io/vsphere-volume")
if err != nil { if err != nil {
......
...@@ -375,8 +375,7 @@ func (e *quotaEvaluator) checkRequest(quotas []api.ResourceQuota, a admission.At ...@@ -375,8 +375,7 @@ func (e *quotaEvaluator) checkRequest(quotas []api.ResourceQuota, a admission.At
return quotas, nil return quotas, nil
} }
op := a.GetOperation() if !evaluator.Handles(a) {
if !evaluator.Handles(op) {
return quotas, nil return quotas, nil
} }
...@@ -463,7 +462,7 @@ func (e *quotaEvaluator) checkRequest(quotas []api.ResourceQuota, a admission.At ...@@ -463,7 +462,7 @@ func (e *quotaEvaluator) checkRequest(quotas []api.ResourceQuota, a admission.At
return nil, admission.NewForbidden(a, fmt.Errorf("quota usage is negative for resource(s): %s", prettyPrintResourceNames(negativeUsage))) return nil, admission.NewForbidden(a, fmt.Errorf("quota usage is negative for resource(s): %s", prettyPrintResourceNames(negativeUsage)))
} }
if admission.Update == op { if admission.Update == a.GetOperation() {
prevItem := a.GetOldObject() prevItem := a.GetOldObject()
if prevItem == nil { if prevItem == nil {
return nil, admission.NewForbidden(a, fmt.Errorf("unable to get previous usage since prior version of object was not found")) return nil, admission.NewForbidden(a, fmt.Errorf("unable to get previous usage since prior version of object was not found"))
...@@ -529,8 +528,7 @@ func (e *quotaEvaluator) Evaluate(a admission.Attributes) error { ...@@ -529,8 +528,7 @@ func (e *quotaEvaluator) Evaluate(a admission.Attributes) error {
} }
// for this kind, check if the operation could mutate any quota resources // for this kind, check if the operation could mutate any quota resources
// if no resources tracked by quota are impacted, then just return // if no resources tracked by quota are impacted, then just return
op := a.GetOperation() if !evaluator.Handles(a) {
if !evaluator.Handles(op) {
return nil return nil
} }
......
...@@ -84,8 +84,8 @@ func setupProviderConfig() error { ...@@ -84,8 +84,8 @@ func setupProviderConfig() error {
Region: region, Region: region,
Zone: zone, Zone: zone,
ManagedZones: managedZones, ManagedZones: managedZones,
NetworkName: "", // TODO: Change this to use framework.TestContext.CloudConfig.Network? NetworkURL: "",
SubnetworkName: "", SubnetworkURL: "",
NodeTags: nil, NodeTags: nil,
NodeInstancePrefix: "", NodeInstancePrefix: "",
TokenSource: nil, TokenSource: nil,
......
...@@ -214,7 +214,7 @@ func (config *NetworkingTestConfig) DialFromContainer(protocol, containerIP, tar ...@@ -214,7 +214,7 @@ func (config *NetworkingTestConfig) DialFromContainer(protocol, containerIP, tar
} }
config.diagnoseMissingEndpoints(eps) config.diagnoseMissingEndpoints(eps)
Failf("Failed to find expected endpoints:\nTries %d\nCommand %v\nretrieved %v\nexpected %v\n", minTries, cmd, eps, expectedEps) Failf("Failed to find expected endpoints:\nTries %d\nCommand %v\nretrieved %v\nexpected %v\n", maxTries, cmd, eps, expectedEps)
} }
// DialFromNode executes a tcp or udp request based on protocol via kubectl exec // DialFromNode executes a tcp or udp request based on protocol via kubectl exec
...@@ -270,7 +270,7 @@ func (config *NetworkingTestConfig) DialFromNode(protocol, targetIP string, targ ...@@ -270,7 +270,7 @@ func (config *NetworkingTestConfig) DialFromNode(protocol, targetIP string, targ
} }
config.diagnoseMissingEndpoints(eps) config.diagnoseMissingEndpoints(eps)
Failf("Failed to find expected endpoints:\nTries %d\nCommand %v\nretrieved %v\nexpected %v\n", minTries, cmd, eps, expectedEps) Failf("Failed to find expected endpoints:\nTries %d\nCommand %v\nretrieved %v\nexpected %v\n", maxTries, cmd, eps, expectedEps)
} }
// GetSelfURL executes a curl against the given path via kubectl exec into a // GetSelfURL executes a curl against the given path via kubectl exec into a
......
...@@ -147,6 +147,85 @@ var _ = framework.KubeDescribe("ResourceQuota", func() { ...@@ -147,6 +147,85 @@ var _ = framework.KubeDescribe("ResourceQuota", func() {
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
}) })
It("should create a ResourceQuota and capture the life of an uninitialized pod.", func() {
// TODO: uncomment the test when #50344 is merged.
// By("Creating a ResourceQuota")
// quotaName := "test-quota"
// resourceQuota := newTestResourceQuota(quotaName)
// resourceQuota, err := createResourceQuota(f.ClientSet, f.Namespace.Name, resourceQuota)
// Expect(err).NotTo(HaveOccurred())
// By("Ensuring resource quota status is calculated")
// usedResources := v1.ResourceList{}
// usedResources[v1.ResourceQuotas] = resource.MustParse("1")
// err = waitForResourceQuota(f.ClientSet, f.Namespace.Name, quotaName, usedResources)
// Expect(err).NotTo(HaveOccurred())
// By("Creating an uninitialized Pod that fits quota")
// podName := "test-pod"
// requests := v1.ResourceList{}
// requests[v1.ResourceCPU] = resource.MustParse("500m")
// requests[v1.ResourceMemory] = resource.MustParse("252Mi")
// pod := newTestPodForQuota(f, podName, requests, v1.ResourceList{})
// pod.Initializers = &metav1.Initializers{Pending: []metav1.Initializer{{Name: "unhandled"}}}
// _, err = f.ClientSet.Core().Pods(f.Namespace.Name).Create(pod)
// // because no one is handling the initializer, server will return a 504 timeout
// if err != nil && !errors.IsTimeout(err) {
// framework.Failf("expect err to be timeout error, got %v", err)
// }
// podToUpdate, err := f.ClientSet.Core().Pods(f.Namespace.Name).Get(podName, metav1.GetOptions{})
// Expect(err).NotTo(HaveOccurred())
// By("Ensuring ResourceQuota status captures the pod usage")
// usedResources[v1.ResourceQuotas] = resource.MustParse("1")
// usedResources[v1.ResourcePods] = resource.MustParse("1")
// usedResources[v1.ResourceCPU] = requests[v1.ResourceCPU]
// usedResources[v1.ResourceMemory] = requests[v1.ResourceMemory]
// err = waitForResourceQuota(f.ClientSet, f.Namespace.Name, quotaName, usedResources)
// Expect(err).NotTo(HaveOccurred())
// By("Not allowing an uninitialized pod to be created that exceeds remaining quota")
// requests = v1.ResourceList{}
// requests[v1.ResourceCPU] = resource.MustParse("600m")
// requests[v1.ResourceMemory] = resource.MustParse("100Mi")
// pod = newTestPodForQuota(f, "fail-pod", requests, v1.ResourceList{})
// pod.Initializers = &metav1.Initializers{Pending: []metav1.Initializer{{Name: "unhandled"}}}
// pod, err = f.ClientSet.Core().Pods(f.Namespace.Name).Create(pod)
// Expect(err).To(HaveOccurred())
// fmt.Println("CHAO: err=", err)
// By("Ensuring an uninitialized pod can update its resource requirements")
// // a pod cannot dynamically update its resource requirements.
// requests = v1.ResourceList{}
// requests[v1.ResourceCPU] = resource.MustParse("100m")
// requests[v1.ResourceMemory] = resource.MustParse("100Mi")
// podToUpdate.Spec.Containers[0].Resources.Requests = requests
// _, err = f.ClientSet.Core().Pods(f.Namespace.Name).Update(podToUpdate)
// Expect(err).NotTo(HaveOccurred())
// By("Ensuring attempts to update pod resource requirements did change quota usage")
// usedResources[v1.ResourceQuotas] = resource.MustParse("1")
// usedResources[v1.ResourcePods] = resource.MustParse("1")
// usedResources[v1.ResourceCPU] = requests[v1.ResourceCPU]
// usedResources[v1.ResourceMemory] = requests[v1.ResourceMemory]
// err = waitForResourceQuota(f.ClientSet, f.Namespace.Name, quotaName, usedResources)
// Expect(err).NotTo(HaveOccurred())
// TODO: uncomment the test when the replenishment_controller uses the
// sharedInformer that list/watches uninitialized objects.
// By("Deleting the pod")
// err = f.ClientSet.Core().Pods(f.Namespace.Name).Delete(podName, metav1.NewDeleteOptions(0))
// Expect(err).NotTo(HaveOccurred())
//
// By("Ensuring resource quota status released the pod usage")
// usedResources[v1.ResourceQuotas] = resource.MustParse("1")
// usedResources[v1.ResourcePods] = resource.MustParse("0")
// usedResources[v1.ResourceCPU] = resource.MustParse("0")
// usedResources[v1.ResourceMemory] = resource.MustParse("0")
// err = waitForResourceQuota(f.ClientSet, f.Namespace.Name, quotaName, usedResources)
// Expect(err).NotTo(HaveOccurred())
})
It("should create a ResourceQuota and capture the life of a pod.", func() { It("should create a ResourceQuota and capture the life of a pod.", func() {
By("Creating a ResourceQuota") By("Creating a ResourceQuota")
quotaName := "test-quota" quotaName := "test-quota"
......
...@@ -24,6 +24,21 @@ ...@@ -24,6 +24,21 @@
"groups": ["docker", "sudo"] "groups": ["docker", "sudo"]
} }
}] }]
},
"storage": {
"files": [
{
"filesystem": "root",
"path": "/etc/ssh/sshd_config",
"contents": {
"source": "data:,%23%20Use%20most%20defaults%20for%20sshd%20configuration.%0AUsePrivilegeSeparation%20sandbox%0ASubsystem%20sftp%20internal-sftp%0AClientAliveInterval%20180%0AUseDNS%20no%0AUsePAM%20yes%0APrintLastLog%20no%20%23%20handled%20by%20PAM%0APrintMotd%20no%20%23%20handled%20by%20PAM%0AAuthenticationMethods%20publickey",
"verification": {}
},
"mode": 384,
"user": {},
"group": {}
}
]
} }
} }
...@@ -36,7 +36,7 @@ type podEvictSpec struct { ...@@ -36,7 +36,7 @@ type podEvictSpec struct {
} }
const ( const (
totalEvict = 3 totalEvict = 4
) )
// Eviction Policy is described here: // Eviction Policy is described here:
...@@ -48,7 +48,7 @@ var _ = framework.KubeDescribe("LocalStorageCapacityIsolationEviction [Slow] [Se ...@@ -48,7 +48,7 @@ var _ = framework.KubeDescribe("LocalStorageCapacityIsolationEviction [Slow] [Se
emptyDirVolumeName := "volume-emptydir-pod" emptyDirVolumeName := "volume-emptydir-pod"
podTestSpecs := []podEvictSpec{ podTestSpecs := []podEvictSpec{
{evicted: true, // This pod should be evicted because emptyDir (defualt storage type) usage violation {evicted: true, // This pod should be evicted because emptyDir (default storage type) usage violation
pod: v1.Pod{ pod: v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "emptydir-hog-pod"}, ObjectMeta: metav1.ObjectMeta{Name: "emptydir-hog-pod"},
Spec: v1.PodSpec{ Spec: v1.PodSpec{
...@@ -157,7 +157,7 @@ var _ = framework.KubeDescribe("LocalStorageCapacityIsolationEviction [Slow] [Se ...@@ -157,7 +157,7 @@ var _ = framework.KubeDescribe("LocalStorageCapacityIsolationEviction [Slow] [Se
}, },
}, },
{evicted: true, // This pod should be evicted because container overlay usage violation {evicted: true, // This pod should be evicted because container ephemeral storage usage violation
pod: v1.Pod{ pod: v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "container-hog-pod"}, ObjectMeta: metav1.ObjectMeta{Name: "container-hog-pod"},
Spec: v1.PodSpec{ Spec: v1.PodSpec{
...@@ -173,7 +173,7 @@ var _ = framework.KubeDescribe("LocalStorageCapacityIsolationEviction [Slow] [Se ...@@ -173,7 +173,7 @@ var _ = framework.KubeDescribe("LocalStorageCapacityIsolationEviction [Slow] [Se
}, },
Resources: v1.ResourceRequirements{ Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{ Limits: v1.ResourceList{
v1.ResourceStorageOverlay: *resource.NewMilliQuantity( v1.ResourceEphemeralStorage: *resource.NewMilliQuantity(
int64(40000), int64(40000),
resource.BinarySI), resource.BinarySI),
}, },
...@@ -183,10 +183,53 @@ var _ = framework.KubeDescribe("LocalStorageCapacityIsolationEviction [Slow] [Se ...@@ -183,10 +183,53 @@ var _ = framework.KubeDescribe("LocalStorageCapacityIsolationEviction [Slow] [Se
}, },
}, },
}, },
{evicted: true, // This pod should be evicted because pod ephemeral storage usage violation
pod: v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "emptydir-container-hog-pod"},
Spec: v1.PodSpec{
RestartPolicy: v1.RestartPolicyNever,
Containers: []v1.Container{
{
Image: "gcr.io/google_containers/busybox:1.24",
Name: "emptydir-container-hog-pod",
Command: []string{
"sh",
"-c",
"sleep 5; dd if=/dev/urandom of=target-file of=/cache/target-file bs=50000 count=1; while true; do sleep 5; done",
},
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceEphemeralStorage: *resource.NewMilliQuantity(
int64(40000),
resource.BinarySI),
},
},
VolumeMounts: []v1.VolumeMount{
{
Name: emptyDirVolumeName,
MountPath: "/cache",
},
},
},
},
Volumes: []v1.Volume{
{
Name: emptyDirVolumeName,
VolumeSource: v1.VolumeSource{
EmptyDir: &v1.EmptyDirVolumeSource{
SizeLimit: *resource.NewQuantity(int64(100000), resource.BinarySI),
},
},
},
},
},
},
},
} }
evictionTestTimeout := 10 * time.Minute evictionTestTimeout := 10 * time.Minute
testCondition := "EmptyDir/ContainerOverlay usage limit violation" testCondition := "EmptyDir/ContainerContainerEphemeralStorage usage limit violation"
Context(fmt.Sprintf("EmptyDirEviction when we run containers that should cause %s", testCondition), func() { Context(fmt.Sprintf("EmptyDirEviction when we run containers that should cause %s", testCondition), func() {
tempSetCurrentKubeletConfig(f, func(initialConfig *kubeletconfig.KubeletConfiguration) { tempSetCurrentKubeletConfig(f, func(initialConfig *kubeletconfig.KubeletConfiguration) {
initialConfig.FeatureGates += ", LocalStorageCapacityIsolation=true" initialConfig.FeatureGates += ", LocalStorageCapacityIsolation=true"
......
...@@ -361,6 +361,7 @@ func createAdClients(ns *v1.Namespace, t *testing.T, server *httptest.Server, sy ...@@ -361,6 +361,7 @@ func createAdClients(ns *v1.Namespace, t *testing.T, server *httptest.Server, sy
informers.Core().V1().PersistentVolumes(), informers.Core().V1().PersistentVolumes(),
cloud, cloud,
plugins, plugins,
nil, /* prober */
false, false,
5*time.Second, 5*time.Second,
timers) timers)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment