1
16
17 package downwardapi
18
19 import (
20 "fmt"
21 "path/filepath"
22
23 v1 "k8s.io/api/core/v1"
24 "k8s.io/apimachinery/pkg/types"
25 utilerrors "k8s.io/apimachinery/pkg/util/errors"
26 "k8s.io/klog/v2"
27 "k8s.io/kubernetes/pkg/api/v1/resource"
28 "k8s.io/kubernetes/pkg/fieldpath"
29 "k8s.io/kubernetes/pkg/volume"
30 volumeutil "k8s.io/kubernetes/pkg/volume/util"
31 utilstrings "k8s.io/utils/strings"
32 )
33
34
35 func ProbeVolumePlugins() []volume.VolumePlugin {
36 return []volume.VolumePlugin{&downwardAPIPlugin{}}
37 }
38
39 const (
40 downwardAPIPluginName = "kubernetes.io/downward-api"
41 )
42
43
44 type downwardAPIPlugin struct {
45 host volume.VolumeHost
46 }
47
48 var _ volume.VolumePlugin = &downwardAPIPlugin{}
49
50 func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
51 return host.GetPodVolumeDir(uid, utilstrings.EscapeQualifiedName(downwardAPIPluginName), volName)
52 }
53
54 func wrappedVolumeSpec() volume.Spec {
55 return volume.Spec{
56 Volume: &v1.Volume{VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{Medium: v1.StorageMediumMemory}}},
57 }
58 }
59
60 func (plugin *downwardAPIPlugin) Init(host volume.VolumeHost) error {
61 plugin.host = host
62 return nil
63 }
64
65 func (plugin *downwardAPIPlugin) GetPluginName() string {
66 return downwardAPIPluginName
67 }
68
69 func (plugin *downwardAPIPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
70 volumeSource, _ := getVolumeSource(spec)
71 if volumeSource == nil {
72 return "", fmt.Errorf("Spec does not reference a DownwardAPI volume type")
73 }
74
75
76 return spec.Name(), nil
77 }
78
79 func (plugin *downwardAPIPlugin) CanSupport(spec *volume.Spec) bool {
80 return spec.Volume != nil && spec.Volume.DownwardAPI != nil
81 }
82
83 func (plugin *downwardAPIPlugin) RequiresRemount(spec *volume.Spec) bool {
84 return true
85 }
86
87 func (plugin *downwardAPIPlugin) SupportsMountOption() bool {
88 return false
89 }
90
91 func (plugin *downwardAPIPlugin) SupportsBulkVolumeVerification() bool {
92 return false
93 }
94
95 func (plugin *downwardAPIPlugin) SupportsSELinuxContextMount(spec *volume.Spec) (bool, error) {
96 return false, nil
97 }
98
99 func (plugin *downwardAPIPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, opts volume.VolumeOptions) (volume.Mounter, error) {
100 v := &downwardAPIVolume{
101 volName: spec.Name(),
102 items: spec.Volume.DownwardAPI.Items,
103 pod: pod,
104 podUID: pod.UID,
105 plugin: plugin,
106 MetricsProvider: volume.NewCachedMetrics(volume.NewMetricsDu(getPath(pod.UID, spec.Name(), plugin.host))),
107 }
108 return &downwardAPIVolumeMounter{
109 downwardAPIVolume: v,
110 source: *spec.Volume.DownwardAPI,
111 opts: &opts,
112 }, nil
113 }
114
115 func (plugin *downwardAPIPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
116 return &downwardAPIVolumeUnmounter{
117 &downwardAPIVolume{
118 volName: volName,
119 podUID: podUID,
120 plugin: plugin,
121 MetricsProvider: volume.NewCachedMetrics(volume.NewMetricsDu(getPath(podUID, volName, plugin.host))),
122 },
123 }, nil
124 }
125
126 func (plugin *downwardAPIPlugin) ConstructVolumeSpec(volumeName, mountPath string) (volume.ReconstructedVolume, error) {
127 downwardAPIVolume := &v1.Volume{
128 Name: volumeName,
129 VolumeSource: v1.VolumeSource{
130 DownwardAPI: &v1.DownwardAPIVolumeSource{},
131 },
132 }
133 return volume.ReconstructedVolume{
134 Spec: volume.NewSpecFromVolume(downwardAPIVolume),
135 }, nil
136 }
137
138
139 type downwardAPIVolume struct {
140 volName string
141 items []v1.DownwardAPIVolumeFile
142 pod *v1.Pod
143 podUID types.UID
144 plugin *downwardAPIPlugin
145 volume.MetricsProvider
146 }
147
148
149
150 type downwardAPIVolumeMounter struct {
151 *downwardAPIVolume
152 source v1.DownwardAPIVolumeSource
153 opts *volume.VolumeOptions
154 }
155
156
157 var _ volume.Mounter = &downwardAPIVolumeMounter{}
158
159
160 func (d *downwardAPIVolume) GetAttributes() volume.Attributes {
161 return volume.Attributes{
162 ReadOnly: true,
163 Managed: true,
164 SELinuxRelabel: true,
165 }
166 }
167
168
169
170
171
172 func (b *downwardAPIVolumeMounter) SetUp(mounterArgs volume.MounterArgs) error {
173 return b.SetUpAt(b.GetPath(), mounterArgs)
174 }
175
176 func (b *downwardAPIVolumeMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
177 klog.V(3).Infof("Setting up a downwardAPI volume %v for pod %v/%v at %v", b.volName, b.pod.Namespace, b.pod.Name, dir)
178
179 wrapped, err := b.plugin.host.NewWrapperMounter(b.volName, wrappedVolumeSpec(), b.pod, *b.opts)
180 if err != nil {
181 klog.Errorf("Couldn't setup downwardAPI volume %v for pod %v/%v: %s", b.volName, b.pod.Namespace, b.pod.Name, err.Error())
182 return err
183 }
184
185 data, err := CollectData(b.source.Items, b.pod, b.plugin.host, b.source.DefaultMode)
186 if err != nil {
187 klog.Errorf("Error preparing data for downwardAPI volume %v for pod %v/%v: %s", b.volName, b.pod.Namespace, b.pod.Name, err.Error())
188 return err
189 }
190
191 setupSuccess := false
192 if err := wrapped.SetUpAt(dir, mounterArgs); err != nil {
193 klog.Errorf("Unable to setup downwardAPI volume %v for pod %v/%v: %s", b.volName, b.pod.Namespace, b.pod.Name, err.Error())
194 return err
195 }
196
197 if err := volumeutil.MakeNestedMountpoints(b.volName, dir, *b.pod); err != nil {
198 return err
199 }
200
201 defer func() {
202
203 if !setupSuccess {
204 unmounter, unmountCreateErr := b.plugin.NewUnmounter(b.volName, b.podUID)
205 if unmountCreateErr != nil {
206 klog.Errorf("error cleaning up mount %s after failure. Create unmounter failed with %v", b.volName, unmountCreateErr)
207 return
208 }
209 tearDownErr := unmounter.TearDown()
210 if tearDownErr != nil {
211 klog.Errorf("error tearing down volume %s with : %v", b.volName, tearDownErr)
212 }
213 }
214 }()
215
216 writerContext := fmt.Sprintf("pod %v/%v volume %v", b.pod.Namespace, b.pod.Name, b.volName)
217 writer, err := volumeutil.NewAtomicWriter(dir, writerContext)
218 if err != nil {
219 klog.Errorf("Error creating atomic writer: %v", err)
220 return err
221 }
222
223 setPerms := func(_ string) error {
224
225
226 return volume.SetVolumeOwnership(b, dir, mounterArgs.FsGroup, nil , volumeutil.FSGroupCompleteHook(b.plugin, nil))
227 }
228 err = writer.Write(data, setPerms)
229 if err != nil {
230 klog.Errorf("Error writing payload to dir: %v", err)
231 return err
232 }
233
234 setupSuccess = true
235 return nil
236 }
237
238
239
240
241
242
243 func CollectData(items []v1.DownwardAPIVolumeFile, pod *v1.Pod, host volume.VolumeHost, defaultMode *int32) (map[string]volumeutil.FileProjection, error) {
244 if defaultMode == nil {
245 return nil, fmt.Errorf("no defaultMode used, not even the default value for it")
246 }
247
248 errlist := []error{}
249 data := make(map[string]volumeutil.FileProjection)
250 for _, fileInfo := range items {
251 var fileProjection volumeutil.FileProjection
252 fPath := filepath.Clean(fileInfo.Path)
253 if fileInfo.Mode != nil {
254 fileProjection.Mode = *fileInfo.Mode
255 } else {
256 fileProjection.Mode = *defaultMode
257 }
258 if fileInfo.FieldRef != nil {
259
260 if values, err := fieldpath.ExtractFieldPathAsString(pod, fileInfo.FieldRef.FieldPath); err != nil {
261 klog.Errorf("Unable to extract field %s: %s", fileInfo.FieldRef.FieldPath, err.Error())
262 errlist = append(errlist, err)
263 } else {
264 fileProjection.Data = []byte(values)
265 }
266 } else if fileInfo.ResourceFieldRef != nil {
267 containerName := fileInfo.ResourceFieldRef.ContainerName
268 nodeAllocatable, err := host.GetNodeAllocatable()
269 if err != nil {
270 errlist = append(errlist, err)
271 } else if values, err := resource.ExtractResourceValueByContainerNameAndNodeAllocatable(fileInfo.ResourceFieldRef, pod, containerName, nodeAllocatable); err != nil {
272 klog.Errorf("Unable to extract field %s: %s", fileInfo.ResourceFieldRef.Resource, err.Error())
273 errlist = append(errlist, err)
274 } else {
275 fileProjection.Data = []byte(values)
276 }
277 }
278
279 data[fPath] = fileProjection
280 }
281 return data, utilerrors.NewAggregate(errlist)
282 }
283
284 func (d *downwardAPIVolume) GetPath() string {
285 return d.plugin.host.GetPodVolumeDir(d.podUID, utilstrings.EscapeQualifiedName(downwardAPIPluginName), d.volName)
286 }
287
288
289 type downwardAPIVolumeUnmounter struct {
290 *downwardAPIVolume
291 }
292
293
294 var _ volume.Unmounter = &downwardAPIVolumeUnmounter{}
295
296 func (c *downwardAPIVolumeUnmounter) TearDown() error {
297 return c.TearDownAt(c.GetPath())
298 }
299
300 func (c *downwardAPIVolumeUnmounter) TearDownAt(dir string) error {
301 return volumeutil.UnmountViaEmptyDir(dir, c.plugin.host, c.volName, wrappedVolumeSpec(), c.podUID)
302 }
303
304 func getVolumeSource(spec *volume.Spec) (*v1.DownwardAPIVolumeSource, bool) {
305 var readOnly bool
306 var volumeSource *v1.DownwardAPIVolumeSource
307
308 if spec.Volume != nil && spec.Volume.DownwardAPI != nil {
309 volumeSource = spec.Volume.DownwardAPI
310 readOnly = spec.ReadOnly
311 }
312
313 return volumeSource, readOnly
314 }
315
View as plain text