mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
ConfigMap volume source
This commit is contained in:
parent
44d12a1389
commit
d1dc259ef2
@ -32,6 +32,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/volume/azure_file"
|
"k8s.io/kubernetes/pkg/volume/azure_file"
|
||||||
"k8s.io/kubernetes/pkg/volume/cephfs"
|
"k8s.io/kubernetes/pkg/volume/cephfs"
|
||||||
"k8s.io/kubernetes/pkg/volume/cinder"
|
"k8s.io/kubernetes/pkg/volume/cinder"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/configmap"
|
||||||
"k8s.io/kubernetes/pkg/volume/downwardapi"
|
"k8s.io/kubernetes/pkg/volume/downwardapi"
|
||||||
"k8s.io/kubernetes/pkg/volume/empty_dir"
|
"k8s.io/kubernetes/pkg/volume/empty_dir"
|
||||||
"k8s.io/kubernetes/pkg/volume/fc"
|
"k8s.io/kubernetes/pkg/volume/fc"
|
||||||
@ -80,6 +81,7 @@ func ProbeVolumePlugins(pluginDir string) []volume.VolumePlugin {
|
|||||||
allPlugins = append(allPlugins, flocker.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, flocker.ProbeVolumePlugins()...)
|
||||||
allPlugins = append(allPlugins, flexvolume.ProbeVolumePlugins(pluginDir)...)
|
allPlugins = append(allPlugins, flexvolume.ProbeVolumePlugins(pluginDir)...)
|
||||||
allPlugins = append(allPlugins, azure_file.ProbeVolumePlugins()...)
|
allPlugins = append(allPlugins, azure_file.ProbeVolumePlugins()...)
|
||||||
|
allPlugins = append(allPlugins, configmap.ProbeVolumePlugins()...)
|
||||||
return allPlugins
|
return allPlugins
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ cluster/aws/templates/configure-vm-aws.sh: # We set the hostname_override to th
|
|||||||
cluster/aws/templates/configure-vm-aws.sh: api_servers: '${API_SERVERS}'
|
cluster/aws/templates/configure-vm-aws.sh: api_servers: '${API_SERVERS}'
|
||||||
cluster/aws/templates/configure-vm-aws.sh: env-to-grains "hostname_override"
|
cluster/aws/templates/configure-vm-aws.sh: env-to-grains "hostname_override"
|
||||||
cluster/aws/templates/configure-vm-aws.sh: env-to-grains "runtime_config"
|
cluster/aws/templates/configure-vm-aws.sh: env-to-grains "runtime_config"
|
||||||
cluster/aws/templates/salt-minion.sh:# We set the hostname_override to the full EC2 private dns name
|
|
||||||
cluster/centos/util.sh: local node_ip=${node#*@}
|
cluster/centos/util.sh: local node_ip=${node#*@}
|
||||||
cluster/gce/configure-vm.sh: advertise_address: '${EXTERNAL_IP}'
|
cluster/gce/configure-vm.sh: advertise_address: '${EXTERNAL_IP}'
|
||||||
cluster/gce/configure-vm.sh: api_servers: '${KUBERNETES_MASTER_NAME}'
|
cluster/gce/configure-vm.sh: api_servers: '${KUBERNETES_MASTER_NAME}'
|
||||||
@ -95,12 +94,11 @@ hack/local-up-cluster.sh: runtime_config=""
|
|||||||
pkg/kubelet/qos/memory_policy_test.go: t.Errorf("oom_score_adj should be between %d and %d, but was %d", test.lowOOMScoreAdj, test.highOOMScoreAdj, oomScoreAdj)
|
pkg/kubelet/qos/memory_policy_test.go: t.Errorf("oom_score_adj should be between %d and %d, but was %d", test.lowOOMScoreAdj, test.highOOMScoreAdj, oomScoreAdj)
|
||||||
pkg/kubelet/qos/memory_policy_test.go: highOOMScoreAdj int // The min oom_score_adj score the container should be assigned.
|
pkg/kubelet/qos/memory_policy_test.go: highOOMScoreAdj int // The min oom_score_adj score the container should be assigned.
|
||||||
pkg/kubelet/qos/memory_policy_test.go: lowOOMScoreAdj int // The max oom_score_adj score the container should be assigned.
|
pkg/kubelet/qos/memory_policy_test.go: lowOOMScoreAdj int // The max oom_score_adj score the container should be assigned.
|
||||||
pkg/util/oom/oom_linux.go: err = fmt.Errorf("failed to read oom_score_adj: %v", readErr)
|
|
||||||
pkg/util/oom/oom_linux.go: err = fmt.Errorf("failed to set oom_score_adj to %d: %v", oomScoreAdj, writeErr)
|
|
||||||
pkg/util/oom/oom_linux.go: return fmt.Errorf("invalid PID %d specified for oom_score_adj", pid)
|
pkg/util/oom/oom_linux.go: return fmt.Errorf("invalid PID %d specified for oom_score_adj", pid)
|
||||||
pkg/util/oom/oom_linux.go: oomScoreAdjPath := path.Join("/proc", pidStr, "oom_score_adj")
|
pkg/util/oom/oom_linux.go: oomScoreAdjPath := path.Join("/proc", pidStr, "oom_score_adj")
|
||||||
pkg/util/oom/oom_linux.go:// Writes 'value' to /proc/<pid>/oom_score_adj for all processes in cgroup cgroupName.
|
pkg/util/oom/oom_linux.go:// Writes 'value' to /proc/<pid>/oom_score_adj for all processes in cgroup cgroupName.
|
||||||
pkg/util/oom/oom_linux.go:// Writes 'value' to /proc/<pid>/oom_score_adj. PID = 0 means self
|
pkg/util/oom/oom_linux.go:// Writes 'value' to /proc/<pid>/oom_score_adj. PID = 0 means self
|
||||||
|
test/e2e/configmap.go: Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/configmap-volume/data-1"},
|
||||||
test/e2e/downwardapi_volume.go: Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=" + filePath},
|
test/e2e/downwardapi_volume.go: Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=" + filePath},
|
||||||
test/e2e/es_cluster_logging.go: Failf("No cluster_name field in Elasticsearch response: %v", esResponse)
|
test/e2e/es_cluster_logging.go: Failf("No cluster_name field in Elasticsearch response: %v", esResponse)
|
||||||
test/e2e/es_cluster_logging.go: // Check to see if have a cluster_name field.
|
test/e2e/es_cluster_logging.go: // Check to see if have a cluster_name field.
|
||||||
|
@ -217,6 +217,8 @@ type VolumeSource struct {
|
|||||||
FC *FCVolumeSource `json:"fc,omitempty"`
|
FC *FCVolumeSource `json:"fc,omitempty"`
|
||||||
// AzureFile represents an Azure File Service mount on the host and bind mount to the pod.
|
// AzureFile represents an Azure File Service mount on the host and bind mount to the pod.
|
||||||
AzureFile *AzureFileVolumeSource `json:"azureFile,omitempty"`
|
AzureFile *AzureFileVolumeSource `json:"azureFile,omitempty"`
|
||||||
|
// ConfigMap represents a configMap that should populate this volume
|
||||||
|
ConfigMap *ConfigMapVolumeSource `json:"configMap,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Similar to VolumeSource but meant for the administrator who creates PVs.
|
// Similar to VolumeSource but meant for the administrator who creates PVs.
|
||||||
@ -688,6 +690,36 @@ type AzureFileVolumeSource struct {
|
|||||||
ReadOnly bool `json:"readOnly,omitempty"`
|
ReadOnly bool `json:"readOnly,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adapts a ConfigMap into a volume.
|
||||||
|
//
|
||||||
|
// The contents of the target ConfigMap's Data field will be presented in a
|
||||||
|
// volume as files using the keys in the Data field as the file names, unless
|
||||||
|
// the items element is populated with specific mappings of keys to paths.
|
||||||
|
// ConfigMap volumes support ownership management and SELinux relabeling.
|
||||||
|
type ConfigMapVolumeSource struct {
|
||||||
|
LocalObjectReference `json:",inline"`
|
||||||
|
// If unspecified, each key-value pair in the Data field of the referenced
|
||||||
|
// ConfigMap will be projected into the volume as a file whose name is the
|
||||||
|
// key and content is the value. If specified, the listed keys will be
|
||||||
|
// projected into the specified paths, and unlisted keys will not be
|
||||||
|
// present. If a key is specified which is not present in the ConfigMap,
|
||||||
|
// the volume setup will error. Paths must be relative and may not contain
|
||||||
|
// the '..' path or start with '..'.
|
||||||
|
Items []KeyToPath `json:"items,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maps a string key to a path within a volume.
|
||||||
|
type KeyToPath struct {
|
||||||
|
// The key to project.
|
||||||
|
Key string `json:"key"`
|
||||||
|
|
||||||
|
// The relative path of the file to map the key to.
|
||||||
|
// May not be an absolute path.
|
||||||
|
// May not contain the path element '..'.
|
||||||
|
// May not start with the string '..'.
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
// ContainerPort represents a network port in a single container
|
// ContainerPort represents a network port in a single container
|
||||||
type ContainerPort struct {
|
type ContainerPort struct {
|
||||||
// Optional: If specified, this must be an IANA_SVC_NAME Each named port
|
// Optional: If specified, this must be an IANA_SVC_NAME Each named port
|
||||||
|
@ -263,6 +263,8 @@ type VolumeSource struct {
|
|||||||
FC *FCVolumeSource `json:"fc,omitempty"`
|
FC *FCVolumeSource `json:"fc,omitempty"`
|
||||||
// AzureFile represents an Azure File Service mount on the host and bind mount to the pod.
|
// AzureFile represents an Azure File Service mount on the host and bind mount to the pod.
|
||||||
AzureFile *AzureFileVolumeSource `json:"azureFile,omitempty"`
|
AzureFile *AzureFileVolumeSource `json:"azureFile,omitempty"`
|
||||||
|
// ConfigMap represents a configMap that should populate this volume
|
||||||
|
ConfigMap *ConfigMapVolumeSource `json:"configMap,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace.
|
// PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace.
|
||||||
@ -808,6 +810,36 @@ type AzureFileVolumeSource struct {
|
|||||||
ReadOnly bool `json:"readOnly,omitempty"`
|
ReadOnly bool `json:"readOnly,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adapts a ConfigMap into a volume.
|
||||||
|
//
|
||||||
|
// The contents of the target ConfigMap's Data field will be presented in a
|
||||||
|
// volume as files using the keys in the Data field as the file names, unless
|
||||||
|
// the items element is populated with specific mappings of keys to paths.
|
||||||
|
// ConfigMap volumes support ownership management and SELinux relabeling.
|
||||||
|
type ConfigMapVolumeSource struct {
|
||||||
|
LocalObjectReference `json:",inline"`
|
||||||
|
// If unspecified, each key-value pair in the Data field of the referenced
|
||||||
|
// ConfigMap will be projected into the volume as a file whose name is the
|
||||||
|
// key and content is the value. If specified, the listed keys will be
|
||||||
|
// projected into the specified paths, and unlisted keys will not be
|
||||||
|
// present. If a key is specified which is not present in the ConfigMap,
|
||||||
|
// the volume setup will error. Paths must be relative and may not contain
|
||||||
|
// the '..' path or start with '..'.
|
||||||
|
Items []KeyToPath `json:"items,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maps a string key to a path within a volume.
|
||||||
|
type KeyToPath struct {
|
||||||
|
// The key to project.
|
||||||
|
Key string `json:"key"`
|
||||||
|
|
||||||
|
// The relative path of the file to map the key to.
|
||||||
|
// May not be an absolute path.
|
||||||
|
// May not contain the path element '..'.
|
||||||
|
// May not start with the string '..'.
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
// ContainerPort represents a network port in a single container.
|
// ContainerPort represents a network port in a single container.
|
||||||
type ContainerPort struct {
|
type ContainerPort struct {
|
||||||
// If specified, this must be an IANA_SVC_NAME and unique within the pod. Each
|
// If specified, this must be an IANA_SVC_NAME and unique within the pod. Each
|
||||||
|
@ -491,8 +491,20 @@ func validateVolumeSource(source *api.VolumeSource, fldPath *field.Path) field.E
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if source.FlexVolume != nil {
|
if source.FlexVolume != nil {
|
||||||
numVolumes++
|
if numVolumes > 0 {
|
||||||
allErrs = append(allErrs, validateFlexVolumeSource(source.FlexVolume, fldPath.Child("flexVolume"))...)
|
allErrs = append(allErrs, field.Forbidden(fldPath.Child("flexVolume"), "may not specifiy more than 1 volume type"))
|
||||||
|
} else {
|
||||||
|
numVolumes++
|
||||||
|
allErrs = append(allErrs, validateFlexVolumeSource(source.FlexVolume, fldPath.Child("flexVolume"))...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if source.ConfigMap != nil {
|
||||||
|
if numVolumes > 0 {
|
||||||
|
allErrs = append(allErrs, field.Forbidden(fldPath.Child("configMap"), "may not specifiy more than 1 volume type"))
|
||||||
|
} else {
|
||||||
|
numVolumes++
|
||||||
|
allErrs = append(allErrs, validateConfigMapVolumeSource(source.ConfigMap, fldPath.Child("configMap"))...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if source.AzureFile != nil {
|
if source.AzureFile != nil {
|
||||||
numVolumes++
|
numVolumes++
|
||||||
@ -584,6 +596,14 @@ func validateSecretVolumeSource(secretSource *api.SecretVolumeSource, fldPath *f
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateConfigMapVolumeSource(configMapSource *api.ConfigMapVolumeSource, fldPath *field.Path) field.ErrorList {
|
||||||
|
allErrs := field.ErrorList{}
|
||||||
|
if len(configMapSource.Name) == 0 {
|
||||||
|
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
|
||||||
|
}
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
func validatePersistentClaimVolumeSource(claim *api.PersistentVolumeClaimVolumeSource, fldPath *field.Path) field.ErrorList {
|
func validatePersistentClaimVolumeSource(claim *api.PersistentVolumeClaimVolumeSource, fldPath *field.Path) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
if len(claim.ClaimName) == 0 {
|
if len(claim.ClaimName) == 0 {
|
||||||
|
230
pkg/volume/configmap/configmap.go
Normal file
230
pkg/volume/configmap/configmap.go
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package configmap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/types"
|
||||||
|
ioutil "k8s.io/kubernetes/pkg/util/io"
|
||||||
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
|
"k8s.io/kubernetes/pkg/util/strings"
|
||||||
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
|
volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProbeVolumePlugin is the entry point for plugin detection in a package.
|
||||||
|
func ProbeVolumePlugins() []volume.VolumePlugin {
|
||||||
|
return []volume.VolumePlugin{&configMapPlugin{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
configMapPluginName = "kubernetes.io/configmap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// configMapPlugin implements the VolumePlugin interface.
|
||||||
|
type configMapPlugin struct {
|
||||||
|
host volume.VolumeHost
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ volume.VolumePlugin = &configMapPlugin{}
|
||||||
|
|
||||||
|
func (plugin *configMapPlugin) Init(host volume.VolumeHost) error {
|
||||||
|
plugin.host = host
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (plugin *configMapPlugin) Name() string {
|
||||||
|
return configMapPluginName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (plugin *configMapPlugin) CanSupport(spec *volume.Spec) bool {
|
||||||
|
return spec.Volume != nil && spec.Volume.ConfigMap != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (plugin *configMapPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Builder, error) {
|
||||||
|
return &configMapVolumeBuilder{
|
||||||
|
configMapVolume: &configMapVolume{spec.Name(), pod.UID, plugin, plugin.host.GetMounter(), plugin.host.GetWriter(), volume.MetricsNil{}},
|
||||||
|
source: *spec.Volume.ConfigMap,
|
||||||
|
pod: *pod,
|
||||||
|
opts: &opts}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (plugin *configMapPlugin) NewCleaner(volName string, podUID types.UID) (volume.Cleaner, error) {
|
||||||
|
return &configMapVolumeCleaner{&configMapVolume{volName, podUID, plugin, plugin.host.GetMounter(), plugin.host.GetWriter(), volume.MetricsNil{}}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type configMapVolume struct {
|
||||||
|
volName string
|
||||||
|
podUID types.UID
|
||||||
|
plugin *configMapPlugin
|
||||||
|
mounter mount.Interface
|
||||||
|
writer ioutil.Writer
|
||||||
|
volume.MetricsNil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ volume.Volume = &configMapVolume{}
|
||||||
|
|
||||||
|
func (sv *configMapVolume) GetPath() string {
|
||||||
|
return sv.plugin.host.GetPodVolumeDir(sv.podUID, strings.EscapeQualifiedNameForDisk(configMapPluginName), sv.volName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// configMapVolumeBuilder handles retrieving secrets from the API server
|
||||||
|
// and placing them into the volume on the host.
|
||||||
|
type configMapVolumeBuilder struct {
|
||||||
|
*configMapVolume
|
||||||
|
|
||||||
|
source api.ConfigMapVolumeSource
|
||||||
|
pod api.Pod
|
||||||
|
opts *volume.VolumeOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ volume.Builder = &configMapVolumeBuilder{}
|
||||||
|
|
||||||
|
func (sv *configMapVolume) GetAttributes() volume.Attributes {
|
||||||
|
return volume.Attributes{
|
||||||
|
ReadOnly: true,
|
||||||
|
Managed: true,
|
||||||
|
SupportsSELinux: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *configMapVolumeBuilder) getMetaDir() string {
|
||||||
|
return path.Join(b.plugin.host.GetPodPluginDir(b.podUID, strings.EscapeQualifiedNameForDisk(configMapPluginName)), b.volName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the spec for the volume that this plugin wraps.
|
||||||
|
var wrappedVolumeSpec = volume.Spec{
|
||||||
|
Volume: &api.Volume{VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{Medium: api.StorageMediumMemory}}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *configMapVolumeBuilder) SetUp(fsGroup *int64) error {
|
||||||
|
return b.SetUpAt(b.GetPath(), fsGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *configMapVolumeBuilder) SetUpAt(dir string, fsGroup *int64) error {
|
||||||
|
glog.V(3).Infof("Setting up volume %v for pod %v at %v", b.volName, b.pod.UID, dir)
|
||||||
|
|
||||||
|
// Wrap EmptyDir, let it do the setup.
|
||||||
|
wrapped, err := b.plugin.host.NewWrapperBuilder(b.volName, wrappedVolumeSpec, &b.pod, *b.opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := wrapped.SetUpAt(dir, fsGroup); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
kubeClient := b.plugin.host.GetKubeClient()
|
||||||
|
if kubeClient == nil {
|
||||||
|
return fmt.Errorf("Cannot setup configMap volume %v because kube client is not configured", b.volName)
|
||||||
|
}
|
||||||
|
|
||||||
|
configMap, err := kubeClient.Core().ConfigMaps(b.pod.Namespace).Get(b.source.Name)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("Couldn't get configMap %v/%v: %v", b.pod.Namespace, b.source.Name, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
totalBytes := totalBytes(configMap)
|
||||||
|
glog.V(3).Infof("Received configMap %v/%v containing (%v) pieces of data, %v total bytes",
|
||||||
|
b.pod.Namespace,
|
||||||
|
b.source.Name,
|
||||||
|
len(configMap.Data),
|
||||||
|
totalBytes)
|
||||||
|
|
||||||
|
payload, err := makePayload(b.source.Items, configMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
writerContext := fmt.Sprintf("pod %v/%v volume %v", b.pod.Namespace, b.pod.Name, b.volName)
|
||||||
|
writer, err := volumeutil.NewAtomicWriter(dir, writerContext)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("Error creating atomic writer: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writer.Write(payload)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("Error writing payload to dir: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = volume.SetVolumeOwnership(b, fsGroup)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("Error applying volume ownership settings for group: %v", fsGroup)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makePayload(mappings []api.KeyToPath, configMap *api.ConfigMap) (map[string][]byte, error) {
|
||||||
|
payload := make(map[string][]byte, len(configMap.Data))
|
||||||
|
|
||||||
|
if len(mappings) == 0 {
|
||||||
|
for name, data := range configMap.Data {
|
||||||
|
payload[name] = []byte(data)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, ktp := range mappings {
|
||||||
|
content, ok := configMap.Data[ktp.Key]
|
||||||
|
if !ok {
|
||||||
|
glog.Errorf("references non-existent config key")
|
||||||
|
return nil, fmt.Errorf("references non-existent config key")
|
||||||
|
}
|
||||||
|
|
||||||
|
payload[ktp.Path] = []byte(content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func totalBytes(configMap *api.ConfigMap) int {
|
||||||
|
totalSize := 0
|
||||||
|
for _, value := range configMap.Data {
|
||||||
|
totalSize += len(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// configMapVolumeCleaner handles cleaning up configMap volumes.
|
||||||
|
type configMapVolumeCleaner struct {
|
||||||
|
*configMapVolume
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ volume.Cleaner = &configMapVolumeCleaner{}
|
||||||
|
|
||||||
|
func (c *configMapVolumeCleaner) TearDown() error {
|
||||||
|
return c.TearDownAt(c.GetPath())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *configMapVolumeCleaner) TearDownAt(dir string) error {
|
||||||
|
glog.V(3).Infof("Tearing down volume %v for pod %v at %v", c.volName, c.podUID, dir)
|
||||||
|
|
||||||
|
// Wrap EmptyDir, let it do the teardown.
|
||||||
|
wrapped, err := c.plugin.host.NewWrapperCleaner(c.volName, wrappedVolumeSpec, c.podUID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return wrapped.TearDownAt(dir)
|
||||||
|
}
|
380
pkg/volume/configmap/configmap_test.go
Normal file
380
pkg/volume/configmap/configmap_test.go
Normal file
@ -0,0 +1,380 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package configmap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||||
|
"k8s.io/kubernetes/pkg/client/testing/fake"
|
||||||
|
"k8s.io/kubernetes/pkg/types"
|
||||||
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/empty_dir"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMakePayload(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
mappings []api.KeyToPath
|
||||||
|
configMap *api.ConfigMap
|
||||||
|
payload map[string][]byte
|
||||||
|
success bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no overrides",
|
||||||
|
configMap: &api.ConfigMap{
|
||||||
|
Data: map[string]string{
|
||||||
|
"foo": "foo",
|
||||||
|
"bar": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
payload: map[string][]byte{
|
||||||
|
"foo": []byte("foo"),
|
||||||
|
"bar": []byte("bar"),
|
||||||
|
},
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "basic 1",
|
||||||
|
mappings: []api.KeyToPath{
|
||||||
|
{
|
||||||
|
Key: "foo",
|
||||||
|
Path: "path/to/foo.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
configMap: &api.ConfigMap{
|
||||||
|
Data: map[string]string{
|
||||||
|
"foo": "foo",
|
||||||
|
"bar": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
payload: map[string][]byte{
|
||||||
|
"path/to/foo.txt": []byte("foo"),
|
||||||
|
},
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "subdirs",
|
||||||
|
mappings: []api.KeyToPath{
|
||||||
|
{
|
||||||
|
Key: "foo",
|
||||||
|
Path: "path/to/1/2/3/foo.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
configMap: &api.ConfigMap{
|
||||||
|
Data: map[string]string{
|
||||||
|
"foo": "foo",
|
||||||
|
"bar": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
payload: map[string][]byte{
|
||||||
|
"path/to/1/2/3/foo.txt": []byte("foo"),
|
||||||
|
},
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "subdirs 2",
|
||||||
|
mappings: []api.KeyToPath{
|
||||||
|
{
|
||||||
|
Key: "foo",
|
||||||
|
Path: "path/to/1/2/3/foo.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
configMap: &api.ConfigMap{
|
||||||
|
Data: map[string]string{
|
||||||
|
"foo": "foo",
|
||||||
|
"bar": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
payload: map[string][]byte{
|
||||||
|
"path/to/1/2/3/foo.txt": []byte("foo"),
|
||||||
|
},
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "subdirs 3",
|
||||||
|
mappings: []api.KeyToPath{
|
||||||
|
{
|
||||||
|
Key: "foo",
|
||||||
|
Path: "path/to/1/2/3/foo.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "bar",
|
||||||
|
Path: "another/path/to/the/esteemed/bar.bin",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
configMap: &api.ConfigMap{
|
||||||
|
Data: map[string]string{
|
||||||
|
"foo": "foo",
|
||||||
|
"bar": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
payload: map[string][]byte{
|
||||||
|
"path/to/1/2/3/foo.txt": []byte("foo"),
|
||||||
|
"another/path/to/the/esteemed/bar.bin": []byte("bar"),
|
||||||
|
},
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non existent key",
|
||||||
|
mappings: []api.KeyToPath{
|
||||||
|
{
|
||||||
|
Key: "zab",
|
||||||
|
Path: "path/to/foo.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
configMap: &api.ConfigMap{
|
||||||
|
Data: map[string]string{
|
||||||
|
"foo": "foo",
|
||||||
|
"bar": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
success: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
actualPayload, err := makePayload(tc.mappings, tc.configMap)
|
||||||
|
if err != nil && tc.success {
|
||||||
|
t.Errorf("%v: unexpected failure making payload: %v", tc.name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil && !tc.success {
|
||||||
|
t.Errorf("%v: unexpected success making payload", tc.name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tc.success {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if e, a := tc.payload, actualPayload; !reflect.DeepEqual(e, a) {
|
||||||
|
t.Errorf("%v: expected and actual payload do not match", tc.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestHost(t *testing.T, clientset clientset.Interface) (string, volume.VolumeHost) {
|
||||||
|
tempDir, err := ioutil.TempDir("/tmp", "configmap_volume_test.")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("can't make a temp rootdir: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempDir, volume.NewFakeVolumeHost(tempDir, clientset, empty_dir.ProbeVolumePlugins())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCanSupport(t *testing.T) {
|
||||||
|
pluginMgr := volume.VolumePluginMgr{}
|
||||||
|
_, host := newTestHost(t, nil)
|
||||||
|
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
|
||||||
|
|
||||||
|
plugin, err := pluginMgr.FindPluginByName(configMapPluginName)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Can't find the plugin by name")
|
||||||
|
}
|
||||||
|
if plugin.Name() != configMapPluginName {
|
||||||
|
t.Errorf("Wrong name: %s", plugin.Name())
|
||||||
|
}
|
||||||
|
if !plugin.CanSupport(&volume.Spec{Volume: &api.Volume{VolumeSource: api.VolumeSource{ConfigMap: &api.ConfigMapVolumeSource{LocalObjectReference: api.LocalObjectReference{""}}}}}) {
|
||||||
|
t.Errorf("Expected true")
|
||||||
|
}
|
||||||
|
if plugin.CanSupport(&volume.Spec{}) {
|
||||||
|
t.Errorf("Expected false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPlugin(t *testing.T) {
|
||||||
|
var (
|
||||||
|
testPodUID = types.UID("test_pod_uid")
|
||||||
|
testVolumeName = "test_volume_name"
|
||||||
|
testNamespace = "test_configmap_namespace"
|
||||||
|
testName = "test_configmap_name"
|
||||||
|
|
||||||
|
volumeSpec = volumeSpec(testVolumeName, testName)
|
||||||
|
configMap = configMap(testNamespace, testName)
|
||||||
|
client = fake.NewSimpleClientset(&configMap)
|
||||||
|
pluginMgr = volume.VolumePluginMgr{}
|
||||||
|
_, host = newTestHost(t, client)
|
||||||
|
)
|
||||||
|
|
||||||
|
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
|
||||||
|
|
||||||
|
plugin, err := pluginMgr.FindPluginByName(configMapPluginName)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Can't find the plugin by name")
|
||||||
|
}
|
||||||
|
|
||||||
|
pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: testPodUID}}
|
||||||
|
builder, err := plugin.NewBuilder(volume.NewSpecFromVolume(volumeSpec), pod, volume.VolumeOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to make a new Builder: %v", err)
|
||||||
|
}
|
||||||
|
if builder == nil {
|
||||||
|
t.Errorf("Got a nil Builder")
|
||||||
|
}
|
||||||
|
|
||||||
|
volumePath := builder.GetPath()
|
||||||
|
if !strings.HasSuffix(volumePath, fmt.Sprintf("pods/test_pod_uid/volumes/kubernetes.io~configmap/test_volume_name")) {
|
||||||
|
t.Errorf("Got unexpected path: %s", volumePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
fsGroup := int64(1001)
|
||||||
|
err = builder.SetUp(&fsGroup)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to setup volume: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(volumePath); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
t.Errorf("SetUp() failed, volume path not created: %s", volumePath)
|
||||||
|
} else {
|
||||||
|
t.Errorf("SetUp() failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doTestConfigMapDataInVolume(volumePath, configMap, t)
|
||||||
|
doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the case where the plugin's ready file exists, but the volume dir is not a
|
||||||
|
// mountpoint, which is the state the system will be in after reboot. The dir
|
||||||
|
// should be mounter and the configMap data written to it.
|
||||||
|
func TestPluginReboot(t *testing.T) {
|
||||||
|
var (
|
||||||
|
testPodUID = types.UID("test_pod_uid3")
|
||||||
|
testVolumeName = "test_volume_name"
|
||||||
|
testNamespace = "test_configmap_namespace"
|
||||||
|
testName = "test_configmap_name"
|
||||||
|
|
||||||
|
volumeSpec = volumeSpec(testVolumeName, testName)
|
||||||
|
configMap = configMap(testNamespace, testName)
|
||||||
|
client = fake.NewSimpleClientset(&configMap)
|
||||||
|
pluginMgr = volume.VolumePluginMgr{}
|
||||||
|
rootDir, host = newTestHost(t, client)
|
||||||
|
)
|
||||||
|
|
||||||
|
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
|
||||||
|
|
||||||
|
plugin, err := pluginMgr.FindPluginByName(configMapPluginName)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Can't find the plugin by name")
|
||||||
|
}
|
||||||
|
|
||||||
|
pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: testPodUID}}
|
||||||
|
builder, err := plugin.NewBuilder(volume.NewSpecFromVolume(volumeSpec), pod, volume.VolumeOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to make a new Builder: %v", err)
|
||||||
|
}
|
||||||
|
if builder == nil {
|
||||||
|
t.Errorf("Got a nil Builder")
|
||||||
|
}
|
||||||
|
|
||||||
|
podMetadataDir := fmt.Sprintf("%v/pods/test_pod_uid3/plugins/kubernetes.io~configmap/test_volume_name", rootDir)
|
||||||
|
util.SetReady(podMetadataDir)
|
||||||
|
volumePath := builder.GetPath()
|
||||||
|
if !strings.HasSuffix(volumePath, fmt.Sprintf("pods/test_pod_uid3/volumes/kubernetes.io~configmap/test_volume_name")) {
|
||||||
|
t.Errorf("Got unexpected path: %s", volumePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
fsGroup := int64(1001)
|
||||||
|
err = builder.SetUp(&fsGroup)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to setup volume: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(volumePath); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
t.Errorf("SetUp() failed, volume path not created: %s", volumePath)
|
||||||
|
} else {
|
||||||
|
t.Errorf("SetUp() failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doTestConfigMapDataInVolume(volumePath, configMap, t)
|
||||||
|
doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func volumeSpec(volumeName, configMapName string) *api.Volume {
|
||||||
|
return &api.Volume{
|
||||||
|
Name: volumeName,
|
||||||
|
VolumeSource: api.VolumeSource{
|
||||||
|
ConfigMap: &api.ConfigMapVolumeSource{
|
||||||
|
LocalObjectReference: api.LocalObjectReference{
|
||||||
|
Name: configMapName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configMap(namespace, name string) api.ConfigMap {
|
||||||
|
return api.ConfigMap{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Namespace: namespace,
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Data: map[string]string{
|
||||||
|
"data-1": "value-1",
|
||||||
|
"data-2": "value-2",
|
||||||
|
"data-3": "value-3",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func doTestConfigMapDataInVolume(volumePath string, configMap api.ConfigMap, t *testing.T) {
|
||||||
|
for key, value := range configMap.Data {
|
||||||
|
configMapDataHostPath := path.Join(volumePath, key)
|
||||||
|
if _, err := os.Stat(configMapDataHostPath); err != nil {
|
||||||
|
t.Fatalf("SetUp() failed, couldn't find configMap data on disk: %v", configMapDataHostPath)
|
||||||
|
} else {
|
||||||
|
actualValue, err := ioutil.ReadFile(configMapDataHostPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Couldn't read configMap data from: %v", configMapDataHostPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if value != string(actualValue) {
|
||||||
|
t.Errorf("Unexpected value; expected %q, got %q", value, actualValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func doTestCleanAndTeardown(plugin volume.VolumePlugin, podUID types.UID, testVolumeName, volumePath string, t *testing.T) {
|
||||||
|
cleaner, err := plugin.NewCleaner(testVolumeName, podUID)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to make a new Cleaner: %v", err)
|
||||||
|
}
|
||||||
|
if cleaner == nil {
|
||||||
|
t.Errorf("Got a nil Cleaner")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cleaner.TearDown(); err != nil {
|
||||||
|
t.Errorf("Expected success, got: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(volumePath); err == nil {
|
||||||
|
t.Errorf("TearDown() failed, volume path still exists: %s", volumePath)
|
||||||
|
} else if !os.IsNotExist(err) {
|
||||||
|
t.Errorf("SetUp() failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
18
pkg/volume/configmap/doc.go
Normal file
18
pkg/volume/configmap/doc.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package configmap contains the internal representation of configMap volumes.
|
||||||
|
package configmap
|
@ -18,16 +18,258 @@ package e2e
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/util"
|
"k8s.io/kubernetes/pkg/util"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("ConfigMap", func() {
|
var _ = Describe("ConfigMap", func() {
|
||||||
|
|
||||||
f := NewFramework("configmap")
|
f := NewFramework("configmap")
|
||||||
|
|
||||||
|
It("should be consumable from pods in volume [Conformance]", func() {
|
||||||
|
name := "configmap-test-volume-" + string(util.NewUUID())
|
||||||
|
volumeName := "configmap-volume"
|
||||||
|
volumeMountPath := "/etc/configmap-volume"
|
||||||
|
|
||||||
|
configMap := &api.ConfigMap{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Namespace: f.Namespace.Name,
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Data: map[string]string{
|
||||||
|
"data-1": "value-1",
|
||||||
|
"data-2": "value-2",
|
||||||
|
"data-3": "value-3",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
By(fmt.Sprintf("Creating configMap with name %s", configMap.Name))
|
||||||
|
defer func() {
|
||||||
|
By("Cleaning up the configMap")
|
||||||
|
if err := f.Client.ConfigMaps(f.Namespace.Name).Delete(configMap.Name); err != nil {
|
||||||
|
Failf("unable to delete configMap %v: %v", configMap.Name, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
var err error
|
||||||
|
if configMap, err = f.Client.ConfigMaps(f.Namespace.Name).Create(configMap); err != nil {
|
||||||
|
Failf("unable to create test configMap %s: %v", configMap.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pod := &api.Pod{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "pod-configmaps-" + string(util.NewUUID()),
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Volumes: []api.Volume{
|
||||||
|
{
|
||||||
|
Name: volumeName,
|
||||||
|
VolumeSource: api.VolumeSource{
|
||||||
|
ConfigMap: &api.ConfigMapVolumeSource{
|
||||||
|
LocalObjectReference: api.LocalObjectReference{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Containers: []api.Container{
|
||||||
|
{
|
||||||
|
Name: "configmap-volume-test",
|
||||||
|
Image: "gcr.io/google_containers/mounttest:0.6",
|
||||||
|
Args: []string{"--file_content=/etc/configmap-volume/data-1"},
|
||||||
|
VolumeMounts: []api.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: volumeName,
|
||||||
|
MountPath: volumeMountPath,
|
||||||
|
ReadOnly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RestartPolicy: api.RestartPolicyNever,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testContainerOutput("consume configMaps", f.Client, pod, 0, []string{
|
||||||
|
"content of file \"/etc/configmap-volume/data-1\": value-1",
|
||||||
|
}, f.Namespace.Name)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should be consumable from pods in volume with mappings [Conformance]", func() {
|
||||||
|
name := "configmap-test-volume-map-" + string(util.NewUUID())
|
||||||
|
volumeName := "configmap-volume"
|
||||||
|
volumeMountPath := "/etc/configmap-volume"
|
||||||
|
|
||||||
|
configMap := &api.ConfigMap{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Namespace: f.Namespace.Name,
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Data: map[string]string{
|
||||||
|
"data-1": "value-1",
|
||||||
|
"data-2": "value-2",
|
||||||
|
"data-3": "value-3",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
By(fmt.Sprintf("Creating configMap with name %s", configMap.Name))
|
||||||
|
defer func() {
|
||||||
|
By("Cleaning up the configMap")
|
||||||
|
if err := f.Client.ConfigMaps(f.Namespace.Name).Delete(configMap.Name); err != nil {
|
||||||
|
Failf("unable to delete configMap %v: %v", configMap.Name, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
var err error
|
||||||
|
if configMap, err = f.Client.ConfigMaps(f.Namespace.Name).Create(configMap); err != nil {
|
||||||
|
Failf("unable to create test configMap %s: %v", configMap.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pod := &api.Pod{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "pod-configmaps-" + string(util.NewUUID()),
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Volumes: []api.Volume{
|
||||||
|
{
|
||||||
|
Name: volumeName,
|
||||||
|
VolumeSource: api.VolumeSource{
|
||||||
|
ConfigMap: &api.ConfigMapVolumeSource{
|
||||||
|
LocalObjectReference: api.LocalObjectReference{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Items: []api.KeyToPath{
|
||||||
|
{
|
||||||
|
Key: "data-2",
|
||||||
|
Path: "path/to/data-2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Containers: []api.Container{
|
||||||
|
{
|
||||||
|
Name: "configmap-volume-test",
|
||||||
|
Image: "gcr.io/google_containers/mounttest:0.6",
|
||||||
|
Args: []string{"--file_content=/etc/configmap-volume/path/to/data-2"},
|
||||||
|
VolumeMounts: []api.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: volumeName,
|
||||||
|
MountPath: volumeMountPath,
|
||||||
|
ReadOnly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RestartPolicy: api.RestartPolicyNever,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testContainerOutput("consume configMaps", f.Client, pod, 0, []string{
|
||||||
|
"content of file \"/etc/configmap-volume/path/to/data-2\": value-2",
|
||||||
|
}, f.Namespace.Name)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("updates should be reflected in volume [Conformance]", func() {
|
||||||
|
|
||||||
|
// We may have to wait or a full sync period to elapse before the
|
||||||
|
// Kubelet projects the update into the volume and the container picks
|
||||||
|
// it up. This timeout is based on the default Kubelet sync period (1
|
||||||
|
// minute) plus additional time for fudge factor.
|
||||||
|
const podLogTimeout = 90 * time.Second
|
||||||
|
|
||||||
|
name := "configmap-test-upd-" + string(util.NewUUID())
|
||||||
|
volumeName := "configmap-volume"
|
||||||
|
volumeMountPath := "/etc/configmap-volume"
|
||||||
|
containerName := "configmap-volume-test"
|
||||||
|
|
||||||
|
configMap := &api.ConfigMap{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Namespace: f.Namespace.Name,
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Data: map[string]string{
|
||||||
|
"data-1": "value-1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
By(fmt.Sprintf("Creating configMap with name %s", configMap.Name))
|
||||||
|
defer func() {
|
||||||
|
By("Cleaning up the configMap")
|
||||||
|
if err := f.Client.ConfigMaps(f.Namespace.Name).Delete(configMap.Name); err != nil {
|
||||||
|
Failf("unable to delete configMap %v: %v", configMap.Name, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
var err error
|
||||||
|
if configMap, err = f.Client.ConfigMaps(f.Namespace.Name).Create(configMap); err != nil {
|
||||||
|
Failf("unable to create test configMap %s: %v", configMap.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pod := &api.Pod{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "pod-configmaps-" + string(util.NewUUID()),
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Volumes: []api.Volume{
|
||||||
|
{
|
||||||
|
Name: volumeName,
|
||||||
|
VolumeSource: api.VolumeSource{
|
||||||
|
ConfigMap: &api.ConfigMapVolumeSource{
|
||||||
|
LocalObjectReference: api.LocalObjectReference{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Containers: []api.Container{
|
||||||
|
{
|
||||||
|
Name: containerName,
|
||||||
|
Image: "gcr.io/google_containers/mounttest:0.6",
|
||||||
|
Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/configmap-volume/data-1"},
|
||||||
|
VolumeMounts: []api.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: volumeName,
|
||||||
|
MountPath: volumeMountPath,
|
||||||
|
ReadOnly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RestartPolicy: api.RestartPolicyNever,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
By("Deleting the pod")
|
||||||
|
f.Client.Pods(f.Namespace.Name).Delete(pod.Name, api.NewDeleteOptions(0))
|
||||||
|
}()
|
||||||
|
By("Creating the pod")
|
||||||
|
_, err = f.Client.Pods(f.Namespace.Name).Create(pod)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
expectNoError(waitForPodRunningInNamespace(f.Client, pod.Name, f.Namespace.Name))
|
||||||
|
|
||||||
|
pollLogs := func() (string, error) {
|
||||||
|
return getPodLogs(f.Client, f.Namespace.Name, pod.Name, containerName)
|
||||||
|
}
|
||||||
|
|
||||||
|
Eventually(pollLogs, podLogTimeout, poll).Should(ContainSubstring("value-1"))
|
||||||
|
|
||||||
|
By(fmt.Sprintf("Updating configmap %v", configMap.Name))
|
||||||
|
configMap.ResourceVersion = "" // to force update
|
||||||
|
configMap.Data["data-1"] = "value-2"
|
||||||
|
_, err = f.Client.ConfigMaps(f.Namespace.Name).Update(configMap)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
Eventually(pollLogs, podLogTimeout, poll).Should(ContainSubstring("value-2"))
|
||||||
|
})
|
||||||
|
|
||||||
It("should be consumable via environment variable [Conformance]", func() {
|
It("should be consumable via environment variable [Conformance]", func() {
|
||||||
name := "configmap-test-" + string(util.NewUUID())
|
name := "configmap-test-" + string(util.NewUUID())
|
||||||
configMap := &api.ConfigMap{
|
configMap := &api.ConfigMap{
|
||||||
|
@ -27,10 +27,10 @@ import (
|
|||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
// How long to wait for a log pod to be displayed
|
|
||||||
const podLogTimeout = 45 * time.Second
|
|
||||||
|
|
||||||
var _ = Describe("Downward API volume", func() {
|
var _ = Describe("Downward API volume", func() {
|
||||||
|
// How long to wait for a log pod to be displayed
|
||||||
|
const podLogTimeout = 45 * time.Second
|
||||||
|
|
||||||
f := NewFramework("downward-api")
|
f := NewFramework("downward-api")
|
||||||
It("should provide podname only [Conformance]", func() {
|
It("should provide podname only [Conformance]", func() {
|
||||||
podName := "downwardapi-volume-" + string(util.NewUUID())
|
podName := "downwardapi-volume-" + string(util.NewUUID())
|
||||||
|
Loading…
Reference in New Issue
Block a user