1
16
17 package nfs
18
19 import (
20 "fmt"
21 "os"
22 "time"
23
24 netutil "k8s.io/utils/net"
25
26 "k8s.io/klog/v2"
27 "k8s.io/mount-utils"
28 utilstrings "k8s.io/utils/strings"
29
30 v1 "k8s.io/api/core/v1"
31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32 "k8s.io/apimachinery/pkg/types"
33 "k8s.io/kubernetes/pkg/volume"
34 "k8s.io/kubernetes/pkg/volume/util"
35 "k8s.io/kubernetes/pkg/volume/util/recyclerclient"
36 )
37
38 func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
39 return host.GetPodVolumeDir(uid, utilstrings.EscapeQualifiedName(nfsPluginName), volName)
40 }
41
42
43
44
45
46
47 func ProbeVolumePlugins(volumeConfig volume.VolumeConfig) []volume.VolumePlugin {
48 return []volume.VolumePlugin{
49 &nfsPlugin{
50 host: nil,
51 config: volumeConfig,
52 },
53 }
54 }
55
56 type nfsPlugin struct {
57 host volume.VolumeHost
58 config volume.VolumeConfig
59 }
60
61 var _ volume.VolumePlugin = &nfsPlugin{}
62 var _ volume.PersistentVolumePlugin = &nfsPlugin{}
63 var _ volume.RecyclableVolumePlugin = &nfsPlugin{}
64
65 const (
66 nfsPluginName = "kubernetes.io/nfs"
67 unMountTimeout = time.Minute
68 )
69
70 func (plugin *nfsPlugin) Init(host volume.VolumeHost) error {
71 plugin.host = host
72 return nil
73 }
74
75 func (plugin *nfsPlugin) GetPluginName() string {
76 return nfsPluginName
77 }
78
79 func (plugin *nfsPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
80 volumeSource, _, err := getVolumeSource(spec)
81 if err != nil {
82 return "", err
83 }
84
85 return fmt.Sprintf(
86 "%v/%v",
87 volumeSource.Server,
88 volumeSource.Path), nil
89 }
90
91 func (plugin *nfsPlugin) CanSupport(spec *volume.Spec) bool {
92 return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.NFS != nil) ||
93 (spec.Volume != nil && spec.Volume.NFS != nil)
94 }
95
96 func (plugin *nfsPlugin) RequiresRemount(spec *volume.Spec) bool {
97 return false
98 }
99
100 func (plugin *nfsPlugin) SupportsMountOption() bool {
101 return true
102 }
103
104 func (plugin *nfsPlugin) SupportsBulkVolumeVerification() bool {
105 return false
106 }
107
108 func (plugin *nfsPlugin) SupportsSELinuxContextMount(spec *volume.Spec) (bool, error) {
109 return false, nil
110 }
111
112 func (plugin *nfsPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
113 return []v1.PersistentVolumeAccessMode{
114 v1.ReadWriteOnce,
115 v1.ReadOnlyMany,
116 v1.ReadWriteMany,
117 }
118 }
119
120 func (plugin *nfsPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
121 return plugin.newMounterInternal(spec, pod, plugin.host.GetMounter(plugin.GetPluginName()))
122 }
123
124 func (plugin *nfsPlugin) newMounterInternal(spec *volume.Spec, pod *v1.Pod, mounter mount.Interface) (volume.Mounter, error) {
125 source, readOnly, err := getVolumeSource(spec)
126 if err != nil {
127 return nil, err
128 }
129 return &nfsMounter{
130 nfs: &nfs{
131 volName: spec.Name(),
132 mounter: mounter,
133 pod: pod,
134 plugin: plugin,
135 MetricsProvider: volume.NewMetricsStatFS(getPath(pod.UID, spec.Name(), plugin.host)),
136 },
137 server: getServerFromSource(source),
138 exportPath: source.Path,
139 readOnly: readOnly,
140 mountOptions: util.MountOptionFromSpec(spec),
141 }, nil
142 }
143
144 func (plugin *nfsPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
145 return plugin.newUnmounterInternal(volName, podUID, plugin.host.GetMounter(plugin.GetPluginName()))
146 }
147
148 func (plugin *nfsPlugin) newUnmounterInternal(volName string, podUID types.UID, mounter mount.Interface) (volume.Unmounter, error) {
149 return &nfsUnmounter{&nfs{
150 volName: volName,
151 mounter: mounter,
152 pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: podUID}},
153 plugin: plugin,
154 MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, volName, plugin.host)),
155 }}, nil
156 }
157
158
159
160 func (plugin *nfsPlugin) Recycle(pvName string, spec *volume.Spec, eventRecorder recyclerclient.RecycleEventRecorder) error {
161 if spec.PersistentVolume == nil || spec.PersistentVolume.Spec.NFS == nil {
162 return fmt.Errorf("spec.PersistentVolumeSource.NFS is nil")
163 }
164
165 pod := plugin.config.RecyclerPodTemplate
166 timeout := util.CalculateTimeoutForVolume(plugin.config.RecyclerMinimumTimeout, plugin.config.RecyclerTimeoutIncrement, spec.PersistentVolume)
167
168 pod.Spec.ActiveDeadlineSeconds = &timeout
169 pod.GenerateName = "pv-recycler-nfs-"
170 pod.Spec.Volumes[0].VolumeSource = v1.VolumeSource{
171 NFS: &v1.NFSVolumeSource{
172 Server: spec.PersistentVolume.Spec.NFS.Server,
173 Path: spec.PersistentVolume.Spec.NFS.Path,
174 },
175 }
176 return recyclerclient.RecycleVolumeByWatchingPodUntilCompletion(pvName, pod, plugin.host.GetKubeClient(), eventRecorder)
177 }
178
179 func (plugin *nfsPlugin) ConstructVolumeSpec(volumeName, mountPath string) (volume.ReconstructedVolume, error) {
180 nfsVolume := &v1.Volume{
181 Name: volumeName,
182 VolumeSource: v1.VolumeSource{
183 NFS: &v1.NFSVolumeSource{
184 Path: volumeName,
185 },
186 },
187 }
188 return volume.ReconstructedVolume{
189 Spec: volume.NewSpecFromVolume(nfsVolume),
190 }, nil
191 }
192
193
194 type nfs struct {
195 volName string
196 pod *v1.Pod
197 mounter mount.Interface
198 plugin *nfsPlugin
199 volume.MetricsProvider
200 }
201
202 func (nfsVolume *nfs) GetPath() string {
203 name := nfsPluginName
204 return nfsVolume.plugin.host.GetPodVolumeDir(nfsVolume.pod.UID, utilstrings.EscapeQualifiedName(name), nfsVolume.volName)
205 }
206
207 type nfsMounter struct {
208 *nfs
209 server string
210 exportPath string
211 readOnly bool
212 mountOptions []string
213 }
214
215 var _ volume.Mounter = &nfsMounter{}
216
217 func (nfsMounter *nfsMounter) GetAttributes() volume.Attributes {
218 return volume.Attributes{
219 ReadOnly: nfsMounter.readOnly,
220 Managed: false,
221 SELinuxRelabel: false,
222 }
223 }
224
225
226 func (nfsMounter *nfsMounter) SetUp(mounterArgs volume.MounterArgs) error {
227 return nfsMounter.SetUpAt(nfsMounter.GetPath(), mounterArgs)
228 }
229
230 func (nfsMounter *nfsMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
231 notMnt, err := mount.IsNotMountPoint(nfsMounter.mounter, dir)
232 klog.V(4).Infof("NFS mount set up: %s %v %v", dir, !notMnt, err)
233 if err != nil && !os.IsNotExist(err) {
234 return err
235 }
236 if !notMnt {
237 return nil
238 }
239 if err := os.MkdirAll(dir, 0750); err != nil {
240 return err
241 }
242 source := fmt.Sprintf("%s:%s", nfsMounter.server, nfsMounter.exportPath)
243 options := []string{}
244 if nfsMounter.readOnly {
245 options = append(options, "ro")
246 }
247 mountOptions := util.JoinMountOptions(nfsMounter.mountOptions, options)
248 err = nfsMounter.mounter.MountSensitiveWithoutSystemd(source, dir, "nfs", mountOptions, nil)
249 if err != nil {
250 notMnt, mntErr := mount.IsNotMountPoint(nfsMounter.mounter, dir)
251 if mntErr != nil {
252 klog.Errorf("IsNotMountPoint check failed: %v", mntErr)
253 return err
254 }
255 if !notMnt {
256 if mntErr = nfsMounter.mounter.Unmount(dir); mntErr != nil {
257 klog.Errorf("Failed to unmount: %v", mntErr)
258 return err
259 }
260 notMnt, mntErr := mount.IsNotMountPoint(nfsMounter.mounter, dir)
261 if mntErr != nil {
262 klog.Errorf("IsNotMountPoint check failed: %v", mntErr)
263 return err
264 }
265 if !notMnt {
266
267 klog.Errorf("%s is still mounted, despite call to unmount(). Will try again next sync loop.", dir)
268 return err
269 }
270 }
271 os.Remove(dir)
272 return err
273 }
274 return nil
275 }
276
277 var _ volume.Unmounter = &nfsUnmounter{}
278
279 type nfsUnmounter struct {
280 *nfs
281 }
282
283 func (c *nfsUnmounter) TearDown() error {
284 return c.TearDownAt(c.GetPath())
285 }
286
287 func (c *nfsUnmounter) TearDownAt(dir string) error {
288
289
290
291 forceUnmounter, ok := c.mounter.(mount.MounterForceUnmounter)
292 if ok {
293 klog.V(4).Infof("Using force unmounter interface")
294 return mount.CleanupMountWithForce(dir, forceUnmounter, true , unMountTimeout)
295 }
296 return mount.CleanupMountPoint(dir, c.mounter, true )
297 }
298
299 func getVolumeSource(spec *volume.Spec) (*v1.NFSVolumeSource, bool, error) {
300 if spec.Volume != nil && spec.Volume.NFS != nil {
301 return spec.Volume.NFS, spec.Volume.NFS.ReadOnly, nil
302 } else if spec.PersistentVolume != nil &&
303 spec.PersistentVolume.Spec.NFS != nil {
304 return spec.PersistentVolume.Spec.NFS, spec.ReadOnly, nil
305 }
306
307 return nil, false, fmt.Errorf("Spec does not reference a NFS volume type")
308 }
309
310 func getServerFromSource(source *v1.NFSVolumeSource) string {
311 if netutil.IsIPv6String(source.Server) {
312 return fmt.Sprintf("[%s]", source.Server)
313 }
314 return source.Server
315 }
316
View as plain text