...

Source file src/k8s.io/kubernetes/pkg/volume/configmap/configmap.go

Documentation: k8s.io/kubernetes/pkg/volume/configmap

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package configmap
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"k8s.io/klog/v2"
    23  	"k8s.io/mount-utils"
    24  	utilstrings "k8s.io/utils/strings"
    25  
    26  	v1 "k8s.io/api/core/v1"
    27  	"k8s.io/apimachinery/pkg/api/errors"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/types"
    30  	"k8s.io/kubernetes/pkg/volume"
    31  	volumeutil "k8s.io/kubernetes/pkg/volume/util"
    32  )
    33  
    34  // ProbeVolumePlugins is the entry point for plugin detection in a package.
    35  func ProbeVolumePlugins() []volume.VolumePlugin {
    36  	return []volume.VolumePlugin{&configMapPlugin{}}
    37  }
    38  
    39  const (
    40  	configMapPluginName = "kubernetes.io/configmap"
    41  )
    42  
    43  // configMapPlugin implements the VolumePlugin interface.
    44  type configMapPlugin struct {
    45  	host         volume.VolumeHost
    46  	getConfigMap func(namespace, name string) (*v1.ConfigMap, error)
    47  }
    48  
    49  var _ volume.VolumePlugin = &configMapPlugin{}
    50  
    51  func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
    52  	return host.GetPodVolumeDir(uid, utilstrings.EscapeQualifiedName(configMapPluginName), volName)
    53  }
    54  
    55  func (plugin *configMapPlugin) Init(host volume.VolumeHost) error {
    56  	plugin.host = host
    57  	plugin.getConfigMap = host.GetConfigMapFunc()
    58  	return nil
    59  }
    60  
    61  func (plugin *configMapPlugin) GetPluginName() string {
    62  	return configMapPluginName
    63  }
    64  
    65  func (plugin *configMapPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
    66  	volumeSource, _ := getVolumeSource(spec)
    67  	if volumeSource == nil {
    68  		return "", fmt.Errorf("Spec does not reference a ConfigMap volume type")
    69  	}
    70  
    71  	return fmt.Sprintf(
    72  		"%v/%v",
    73  		spec.Name(),
    74  		volumeSource.Name), nil
    75  }
    76  
    77  func (plugin *configMapPlugin) CanSupport(spec *volume.Spec) bool {
    78  	return spec.Volume != nil && spec.Volume.ConfigMap != nil
    79  }
    80  
    81  func (plugin *configMapPlugin) RequiresRemount(spec *volume.Spec) bool {
    82  	return true
    83  }
    84  
    85  func (plugin *configMapPlugin) SupportsMountOption() bool {
    86  	return false
    87  }
    88  
    89  func (plugin *configMapPlugin) SupportsBulkVolumeVerification() bool {
    90  	return false
    91  }
    92  
    93  func (plugin *configMapPlugin) SupportsSELinuxContextMount(spec *volume.Spec) (bool, error) {
    94  	return false, nil
    95  }
    96  
    97  func (plugin *configMapPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, opts volume.VolumeOptions) (volume.Mounter, error) {
    98  	return &configMapVolumeMounter{
    99  		configMapVolume: &configMapVolume{
   100  			spec.Name(),
   101  			pod.UID,
   102  			plugin,
   103  			plugin.host.GetMounter(plugin.GetPluginName()),
   104  			volume.NewCachedMetrics(volume.NewMetricsDu(getPath(pod.UID, spec.Name(), plugin.host))),
   105  		},
   106  		source:       *spec.Volume.ConfigMap,
   107  		pod:          *pod,
   108  		opts:         &opts,
   109  		getConfigMap: plugin.getConfigMap,
   110  	}, nil
   111  }
   112  
   113  func (plugin *configMapPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
   114  	return &configMapVolumeUnmounter{
   115  		&configMapVolume{
   116  			volName,
   117  			podUID,
   118  			plugin,
   119  			plugin.host.GetMounter(plugin.GetPluginName()),
   120  			volume.NewCachedMetrics(volume.NewMetricsDu(getPath(podUID, volName, plugin.host))),
   121  		},
   122  	}, nil
   123  }
   124  
   125  func (plugin *configMapPlugin) ConstructVolumeSpec(volumeName, mountPath string) (volume.ReconstructedVolume, error) {
   126  	configMapVolume := &v1.Volume{
   127  		Name: volumeName,
   128  		VolumeSource: v1.VolumeSource{
   129  			ConfigMap: &v1.ConfigMapVolumeSource{},
   130  		},
   131  	}
   132  	return volume.ReconstructedVolume{
   133  		Spec: volume.NewSpecFromVolume(configMapVolume),
   134  	}, nil
   135  }
   136  
   137  type configMapVolume struct {
   138  	volName string
   139  	podUID  types.UID
   140  	plugin  *configMapPlugin
   141  	mounter mount.Interface
   142  	volume.MetricsProvider
   143  }
   144  
   145  var _ volume.Volume = &configMapVolume{}
   146  
   147  func (sv *configMapVolume) GetPath() string {
   148  	return sv.plugin.host.GetPodVolumeDir(sv.podUID, utilstrings.EscapeQualifiedName(configMapPluginName), sv.volName)
   149  }
   150  
   151  // configMapVolumeMounter handles retrieving secrets from the API server
   152  // and placing them into the volume on the host.
   153  type configMapVolumeMounter struct {
   154  	*configMapVolume
   155  
   156  	source       v1.ConfigMapVolumeSource
   157  	pod          v1.Pod
   158  	opts         *volume.VolumeOptions
   159  	getConfigMap func(namespace, name string) (*v1.ConfigMap, error)
   160  }
   161  
   162  var _ volume.Mounter = &configMapVolumeMounter{}
   163  
   164  func (sv *configMapVolume) GetAttributes() volume.Attributes {
   165  	return volume.Attributes{
   166  		ReadOnly:       true,
   167  		Managed:        true,
   168  		SELinuxRelabel: true,
   169  	}
   170  }
   171  
   172  func wrappedVolumeSpec() volume.Spec {
   173  	// This is the spec for the volume that this plugin wraps.
   174  	return volume.Spec{
   175  		// This should be on a tmpfs instead of the local disk; the problem is
   176  		// charging the memory for the tmpfs to the right cgroup.  We should make
   177  		// this a tmpfs when we can do the accounting correctly.
   178  		Volume: &v1.Volume{VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}},
   179  	}
   180  }
   181  
   182  func (b *configMapVolumeMounter) SetUp(mounterArgs volume.MounterArgs) error {
   183  	return b.SetUpAt(b.GetPath(), mounterArgs)
   184  }
   185  
   186  func (b *configMapVolumeMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
   187  	klog.V(3).Infof("Setting up volume %v for pod %v at %v", b.volName, b.pod.UID, dir)
   188  
   189  	// Wrap EmptyDir, let it do the setup.
   190  	wrapped, err := b.plugin.host.NewWrapperMounter(b.volName, wrappedVolumeSpec(), &b.pod, *b.opts)
   191  	if err != nil {
   192  		return err
   193  	}
   194  
   195  	optional := b.source.Optional != nil && *b.source.Optional
   196  	configMap, err := b.getConfigMap(b.pod.Namespace, b.source.Name)
   197  	if err != nil {
   198  		if !(errors.IsNotFound(err) && optional) {
   199  			klog.Errorf("Couldn't get configMap %v/%v: %v", b.pod.Namespace, b.source.Name, err)
   200  			return err
   201  		}
   202  		configMap = &v1.ConfigMap{
   203  			ObjectMeta: metav1.ObjectMeta{
   204  				Namespace: b.pod.Namespace,
   205  				Name:      b.source.Name,
   206  			},
   207  		}
   208  	}
   209  
   210  	totalBytes := totalBytes(configMap)
   211  	klog.V(3).Infof("Received configMap %v/%v containing (%v) pieces of data, %v total bytes",
   212  		b.pod.Namespace,
   213  		b.source.Name,
   214  		len(configMap.Data)+len(configMap.BinaryData),
   215  		totalBytes)
   216  
   217  	payload, err := MakePayload(b.source.Items, configMap, b.source.DefaultMode, optional)
   218  	if err != nil {
   219  		return err
   220  	}
   221  
   222  	setupSuccess := false
   223  	if err := wrapped.SetUpAt(dir, mounterArgs); err != nil {
   224  		return err
   225  	}
   226  	if err := volumeutil.MakeNestedMountpoints(b.volName, dir, b.pod); err != nil {
   227  		return err
   228  	}
   229  
   230  	defer func() {
   231  		// Clean up directories if setup fails
   232  		if !setupSuccess {
   233  			unmounter, unmountCreateErr := b.plugin.NewUnmounter(b.volName, b.podUID)
   234  			if unmountCreateErr != nil {
   235  				klog.Errorf("error cleaning up mount %s after failure. Create unmounter failed with %v", b.volName, unmountCreateErr)
   236  				return
   237  			}
   238  			tearDownErr := unmounter.TearDown()
   239  			if tearDownErr != nil {
   240  				klog.Errorf("Error tearing down volume %s with : %v", b.volName, tearDownErr)
   241  			}
   242  		}
   243  	}()
   244  
   245  	writerContext := fmt.Sprintf("pod %v/%v volume %v", b.pod.Namespace, b.pod.Name, b.volName)
   246  	writer, err := volumeutil.NewAtomicWriter(dir, writerContext)
   247  	if err != nil {
   248  		klog.Errorf("Error creating atomic writer: %v", err)
   249  		return err
   250  	}
   251  
   252  	setPerms := func(_ string) error {
   253  		// This may be the first time writing and new files get created outside the timestamp subdirectory:
   254  		// change the permissions on the whole volume and not only in the timestamp directory.
   255  		return volume.SetVolumeOwnership(b, dir, mounterArgs.FsGroup, nil /*fsGroupChangePolicy*/, volumeutil.FSGroupCompleteHook(b.plugin, nil))
   256  	}
   257  	err = writer.Write(payload, setPerms)
   258  	if err != nil {
   259  		klog.Errorf("Error writing payload to dir: %v", err)
   260  		return err
   261  	}
   262  
   263  	setupSuccess = true
   264  	return nil
   265  }
   266  
   267  // MakePayload function is exported so that it can be called from the projection volume driver
   268  func MakePayload(mappings []v1.KeyToPath, configMap *v1.ConfigMap, defaultMode *int32, optional bool) (map[string]volumeutil.FileProjection, error) {
   269  	if defaultMode == nil {
   270  		return nil, fmt.Errorf("no defaultMode used, not even the default value for it")
   271  	}
   272  
   273  	payload := make(map[string]volumeutil.FileProjection, (len(configMap.Data) + len(configMap.BinaryData)))
   274  	var fileProjection volumeutil.FileProjection
   275  
   276  	if len(mappings) == 0 {
   277  		for name, data := range configMap.Data {
   278  			fileProjection.Data = []byte(data)
   279  			fileProjection.Mode = *defaultMode
   280  			payload[name] = fileProjection
   281  		}
   282  		for name, data := range configMap.BinaryData {
   283  			fileProjection.Data = data
   284  			fileProjection.Mode = *defaultMode
   285  			payload[name] = fileProjection
   286  		}
   287  	} else {
   288  		for _, ktp := range mappings {
   289  			if stringData, ok := configMap.Data[ktp.Key]; ok {
   290  				fileProjection.Data = []byte(stringData)
   291  			} else if binaryData, ok := configMap.BinaryData[ktp.Key]; ok {
   292  				fileProjection.Data = binaryData
   293  			} else {
   294  				if optional {
   295  					continue
   296  				}
   297  				return nil, fmt.Errorf("configmap references non-existent config key: %s", ktp.Key)
   298  			}
   299  
   300  			if ktp.Mode != nil {
   301  				fileProjection.Mode = *ktp.Mode
   302  			} else {
   303  				fileProjection.Mode = *defaultMode
   304  			}
   305  			payload[ktp.Path] = fileProjection
   306  		}
   307  	}
   308  
   309  	return payload, nil
   310  }
   311  
   312  func totalBytes(configMap *v1.ConfigMap) int {
   313  	totalSize := 0
   314  	for _, value := range configMap.Data {
   315  		totalSize += len(value)
   316  	}
   317  	for _, value := range configMap.BinaryData {
   318  		totalSize += len(value)
   319  	}
   320  
   321  	return totalSize
   322  }
   323  
   324  // configMapVolumeUnmounter handles cleaning up configMap volumes.
   325  type configMapVolumeUnmounter struct {
   326  	*configMapVolume
   327  }
   328  
   329  var _ volume.Unmounter = &configMapVolumeUnmounter{}
   330  
   331  func (c *configMapVolumeUnmounter) TearDown() error {
   332  	return c.TearDownAt(c.GetPath())
   333  }
   334  
   335  func (c *configMapVolumeUnmounter) TearDownAt(dir string) error {
   336  	return volumeutil.UnmountViaEmptyDir(dir, c.plugin.host, c.volName, wrappedVolumeSpec(), c.podUID)
   337  }
   338  
   339  func getVolumeSource(spec *volume.Spec) (*v1.ConfigMapVolumeSource, bool) {
   340  	var readOnly bool
   341  	var volumeSource *v1.ConfigMapVolumeSource
   342  
   343  	if spec.Volume != nil && spec.Volume.ConfigMap != nil {
   344  		volumeSource = spec.Volume.ConfigMap
   345  		readOnly = spec.ReadOnly
   346  	}
   347  
   348  	return volumeSource, readOnly
   349  }
   350  

View as plain text