1
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
35 func ProbeVolumePlugins() []volume.VolumePlugin {
36 return []volume.VolumePlugin{&configMapPlugin{}}
37 }
38
39 const (
40 configMapPluginName = "kubernetes.io/configmap"
41 )
42
43
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
152
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
174 return volume.Spec{
175
176
177
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
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
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
254
255 return volume.SetVolumeOwnership(b, dir, mounterArgs.FsGroup, nil , 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
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
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