Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
25004014
Commit
25004014
authored
May 04, 2015
by
Victor Marmol
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #7703 from pmorie/nsenter-mount
Add NsenterMounter mount implementation
parents
7cf7b093
e5521234
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
223 additions
and
45 deletions
+223
-45
mount_linux.go
pkg/util/mount/mount_linux.go
+70
-45
nsenter_mount.go
pkg/util/mount/nsenter_mount.go
+153
-0
No files found.
pkg/util/mount/mount_linux.go
View file @
25004014
...
@@ -35,22 +35,43 @@ import (
...
@@ -35,22 +35,43 @@ import (
const
(
const
(
// How many times to retry for a consistent read of /proc/mounts.
// How many times to retry for a consistent read of /proc/mounts.
maxListTries
=
3
maxListTries
=
3
// Number of fields per line in
"/proc/mounts",
as per the fstab man page.
// Number of fields per line in
/proc/mounts
as per the fstab man page.
expectedNumFieldsPerLine
=
6
expectedNumFieldsPerLine
=
6
// Location of the mount file to use
procMountsPath
=
"/proc/mounts"
)
)
// Mounter implements mount.Interface for linux platform.
// Mounter provides the default implementation of mount.Interface
// for the linux platform. This implementation assumes that the
// kubelet is running in the host's root mount namespace.
type
Mounter
struct
{}
type
Mounter
struct
{}
var
_
=
Interface
(
&
Mounter
{})
// Mount mounts source to target as fstype with given options. 'source' and 'fstype' must
// Mount mounts source to target as fstype with given options. 'source' and 'fstype' must
// be an emtpy string in case it's not required, e.g. for remount, or for auto filesystem
// be an emtpy string in case it's not required, e.g. for remount, or for auto filesystem
// type, where kernel handles fs type for you. The mount 'options' is a list of options,
// type, where kernel handles fs type for you. The mount 'options' is a list of options,
// currently come from mount(8), e.g. "ro", "remount", "bind", etc. If no more option is
// currently come from mount(8), e.g. "ro", "remount", "bind", etc. If no more option is
// required, call Mount with an empty string list or nil.
// required, call Mount with an empty string list or nil.
func
(
mounter
*
Mounter
)
Mount
(
source
string
,
target
string
,
fstype
string
,
options
[]
string
)
error
{
func
(
mounter
*
Mounter
)
Mount
(
source
string
,
target
string
,
fstype
string
,
options
[]
string
)
error
{
// The remount options to use in case of bind mount, due to the fact that bind mount doesn't
bind
,
bindRemountOpts
:=
isBind
(
options
)
// respect mount options. The list equals:
// options - 'bind' + 'remount' (no duplicate)
if
bind
{
err
:=
doMount
(
source
,
target
,
fstype
,
[]
string
{
"bind"
})
if
err
!=
nil
{
return
err
}
return
doMount
(
source
,
target
,
fstype
,
bindRemountOpts
)
}
else
{
return
doMount
(
source
,
target
,
fstype
,
options
)
}
}
// isBind detects whether a bind mount is being requested and makes the remount options to
// use in case of bind mount, due to the fact that bind mount doesn't respect mount options.
// The list equals:
// options - 'bind' + 'remount' (no duplicate)
func
isBind
(
options
[]
string
)
(
bool
,
[]
string
)
{
bindRemountOpts
:=
[]
string
{
"remount"
}
bindRemountOpts
:=
[]
string
{
"remount"
}
bind
:=
false
bind
:=
false
...
@@ -68,19 +89,24 @@ func (mounter *Mounter) Mount(source string, target string, fstype string, optio
...
@@ -68,19 +89,24 @@ func (mounter *Mounter) Mount(source string, target string, fstype string, optio
}
}
}
}
if
bind
{
return
bind
,
bindRemountOpts
err
:=
doMount
(
source
,
target
,
fstype
,
[]
string
{
"bind"
})
if
err
!=
nil
{
return
err
}
return
doMount
(
source
,
target
,
fstype
,
bindRemountOpts
)
}
else
{
return
doMount
(
source
,
target
,
fstype
,
options
)
}
}
}
// doMount runs the mount command.
func
doMount
(
source
string
,
target
string
,
fstype
string
,
options
[]
string
)
error
{
func
doMount
(
source
string
,
target
string
,
fstype
string
,
options
[]
string
)
error
{
glog
.
V
(
5
)
.
Infof
(
"Mounting %s %s %s %v"
,
source
,
target
,
fstype
,
options
)
glog
.
V
(
5
)
.
Infof
(
"Mounting %s %s %s %v"
,
source
,
target
,
fstype
,
options
)
mountArgs
:=
makeMountArgs
(
source
,
target
,
fstype
,
options
)
command
:=
exec
.
Command
(
"mount"
,
mountArgs
...
)
output
,
err
:=
command
.
CombinedOutput
()
if
err
!=
nil
{
glog
.
Errorf
(
"Mount failed: %v
\n
Mounting arguments: %s %s %s %v
\n
Output: %s
\n
"
,
err
,
source
,
target
,
fstype
,
options
,
string
(
output
))
}
return
err
}
// makeMountArgs makes the arguments to the mount(8) command.
func
makeMountArgs
(
source
,
target
,
fstype
string
,
options
[]
string
)
[]
string
{
// Build mount command as follows:
// Build mount command as follows:
// mount [-t $fstype] [-o $options] [$source] $target
// mount [-t $fstype] [-o $options] [$source] $target
mountArgs
:=
[]
string
{}
mountArgs
:=
[]
string
{}
...
@@ -94,16 +120,11 @@ func doMount(source string, target string, fstype string, options []string) erro
...
@@ -94,16 +120,11 @@ func doMount(source string, target string, fstype string, options []string) erro
mountArgs
=
append
(
mountArgs
,
source
)
mountArgs
=
append
(
mountArgs
,
source
)
}
}
mountArgs
=
append
(
mountArgs
,
target
)
mountArgs
=
append
(
mountArgs
,
target
)
command
:=
exec
.
Command
(
"mount"
,
mountArgs
...
)
output
,
err
:=
command
.
CombinedOutput
()
return
mountArgs
if
err
!=
nil
{
glog
.
Errorf
(
"Mount failed: %v
\n
Mounting arguments: %s %s %s %v
\n
Output: %s
\n
"
,
err
,
source
,
target
,
fstype
,
options
,
string
(
output
))
}
return
err
}
}
// Unmount unmounts t
arget with given options
.
// Unmount unmounts t
he target
.
func
(
mounter
*
Mounter
)
Unmount
(
target
string
)
error
{
func
(
mounter
*
Mounter
)
Unmount
(
target
string
)
error
{
glog
.
V
(
5
)
.
Infof
(
"Unmounting %s %v"
)
glog
.
V
(
5
)
.
Infof
(
"Unmounting %s %v"
)
command
:=
exec
.
Command
(
"umount"
,
target
)
command
:=
exec
.
Command
(
"umount"
,
target
)
...
@@ -116,25 +137,8 @@ func (mounter *Mounter) Unmount(target string) error {
...
@@ -116,25 +137,8 @@ func (mounter *Mounter) Unmount(target string) error {
}
}
// List returns a list of all mounted filesystems.
// List returns a list of all mounted filesystems.
func
(
mounter
*
Mounter
)
List
()
([]
MountPoint
,
error
)
{
func
(
*
Mounter
)
List
()
([]
MountPoint
,
error
)
{
hash1
,
err
:=
readProcMounts
(
nil
)
return
listProcMounts
(
procMountsPath
)
if
err
!=
nil
{
return
nil
,
err
}
for
i
:=
0
;
i
<
maxListTries
;
i
++
{
mps
:=
[]
MountPoint
{}
hash2
,
err
:=
readProcMounts
(
&
mps
)
if
err
!=
nil
{
return
nil
,
err
}
if
hash1
==
hash2
{
// Success
return
mps
,
nil
}
hash1
=
hash2
}
return
nil
,
fmt
.
Errorf
(
"failed to get a consistent snapshot of /proc/mounts after %d tries"
,
maxListTries
)
}
}
// IsMountPoint determines if a directory is a mountpoint, by comparing the device for the
// IsMountPoint determines if a directory is a mountpoint, by comparing the device for the
...
@@ -153,10 +157,31 @@ func (mounter *Mounter) IsMountPoint(file string) (bool, error) {
...
@@ -153,10 +157,31 @@ func (mounter *Mounter) IsMountPoint(file string) (bool, error) {
return
stat
.
Sys
()
.
(
*
syscall
.
Stat_t
)
.
Dev
!=
rootStat
.
Sys
()
.
(
*
syscall
.
Stat_t
)
.
Dev
,
nil
return
stat
.
Sys
()
.
(
*
syscall
.
Stat_t
)
.
Dev
!=
rootStat
.
Sys
()
.
(
*
syscall
.
Stat_t
)
.
Dev
,
nil
}
}
// readProcMounts reads /proc/mounts and produces a hash of the contents. If the out
func
listProcMounts
(
mountFilePath
string
)
([]
MountPoint
,
error
)
{
// argument is not nil, this fills it with MountPoint structs.
hash1
,
err
:=
readProcMounts
(
mountFilePath
,
nil
)
func
readProcMounts
(
out
*
[]
MountPoint
)
(
uint32
,
error
)
{
if
err
!=
nil
{
file
,
err
:=
os
.
Open
(
"/proc/mounts"
)
return
nil
,
err
}
for
i
:=
0
;
i
<
maxListTries
;
i
++
{
mps
:=
[]
MountPoint
{}
hash2
,
err
:=
readProcMounts
(
mountFilePath
,
&
mps
)
if
err
!=
nil
{
return
nil
,
err
}
if
hash1
==
hash2
{
// Success
return
mps
,
nil
}
hash1
=
hash2
}
return
nil
,
fmt
.
Errorf
(
"failed to get a consistent snapshot of %v after %d tries"
,
mountFilePath
,
maxListTries
)
}
// readProcMounts reads the given mountFilePath (normally /proc/mounts) and produces a hash
// of the contents. If the out argument is not nil, this fills it with MountPoint structs.
func
readProcMounts
(
mountFilePath
string
,
out
*
[]
MountPoint
)
(
uint32
,
error
)
{
file
,
err
:=
os
.
Open
(
mountFilePath
)
if
err
!=
nil
{
if
err
!=
nil
{
return
0
,
err
return
0
,
err
}
}
...
...
pkg/util/mount/nsenter_mount.go
0 → 100644
View file @
25004014
// +build linux
/*
Copyright 2014 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
mount
import
(
"path/filepath"
"strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec"
"github.com/golang/glog"
)
// NsenterMounter is part of experimental support for running the kubelet
// in a container. Currently, all docker containers receive their own mount
// namespaces. NsenterMounter works by executing nsenter to run commands in
// the host's mount namespace.
//
// NsenterMounter requires:
//
// 1. Docker >= 1.6 due to the dependency on the slave propagation mode
// of the bind-mount of the kubelet root directory in the container.
// Docker 1.5 used a private propagation mode for bind-mounts, so mounts
// performed in the host's mount namespace do not propagate out to the
// bind-mount in this docker version.
// 2. The host's root filesystem must be available at /rootfs
// 3. The nsenter binary must be at /nsenter in the container's filesystem.
// 4. The Kubelet process must have CAP_SYS_ADMIN (required by nsenter); at
// the present, this effectively means that the kubelet is running in a
// privileged container.
//
// For more information about mount propagation modes, see:
// https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
type
NsenterMounter
struct
{}
// NsenterMounter implements mount.Interface
var
_
=
Interface
(
&
NsenterMounter
{})
const
(
hostRootFsPath
=
"/rootfs"
hostProcMountsPath
=
"/rootfs/proc/mounts"
nsenterPath
=
"/nsenter"
)
// Mount runs mount(8) in the host's root mount namespace. Aside from this
// aspect, Mount has the same semantics as the mounter returned by mount.New()
func
(
*
NsenterMounter
)
Mount
(
source
string
,
target
string
,
fstype
string
,
options
[]
string
)
error
{
bind
,
bindRemountOpts
:=
isBind
(
options
)
if
bind
{
err
:=
doNsenterMount
(
source
,
target
,
fstype
,
[]
string
{
"bind"
})
if
err
!=
nil
{
return
err
}
return
doNsenterMount
(
source
,
target
,
fstype
,
bindRemountOpts
)
}
return
doNsenterMount
(
source
,
target
,
fstype
,
options
)
}
// doNsenterMount nsenter's the host's mount namespace and performs the
// requested mount.
func
doNsenterMount
(
source
,
target
,
fstype
string
,
options
[]
string
)
error
{
glog
.
V
(
5
)
.
Infof
(
"nsenter Mounting %s %s %s %v"
,
source
,
target
,
fstype
,
options
)
args
:=
makeNsenterArgs
(
source
,
target
,
fstype
,
options
)
glog
.
V
(
5
)
.
Infof
(
"Mount command: %v %v"
,
nsenterPath
,
args
)
exec
:=
exec
.
New
()
outputBytes
,
err
:=
exec
.
Command
(
nsenterPath
,
args
...
)
.
CombinedOutput
()
if
len
(
outputBytes
)
!=
0
{
glog
.
V
(
5
)
.
Infof
(
"Output from mount command: %v"
,
string
(
outputBytes
))
}
return
err
}
// makeNsenterArgs makes a list of argument to nsenter in order to do the
// requested mount.
func
makeNsenterArgs
(
source
,
target
,
fstype
string
,
options
[]
string
)
[]
string
{
nsenterArgs
:=
[]
string
{
"--mount=/rootfs/proc/1/ns/mnt"
,
"/usr/bin/mount"
,
}
args
:=
makeMountArgs
(
source
,
target
,
fstype
,
options
)
return
append
(
nsenterArgs
,
args
...
)
}
// Unmount runs umount(8) in the host's mount namespace.
func
(
*
NsenterMounter
)
Unmount
(
target
string
)
error
{
args
:=
[]
string
{
"--mount=/rootfs/proc/1/ns/mnt"
,
"/usr/bin/umount"
,
target
,
}
glog
.
V
(
5
)
.
Infof
(
"Unmount command: %v %v"
,
nsenterPath
,
args
)
exec
:=
exec
.
New
()
outputBytes
,
err
:=
exec
.
Command
(
nsenterPath
,
args
...
)
.
CombinedOutput
()
if
len
(
outputBytes
)
!=
0
{
glog
.
V
(
5
)
.
Infof
(
"Output from mount command: %v"
,
string
(
outputBytes
))
}
return
err
}
// List returns a list of all mounted filesystems in the host's mount namespace.
func
(
*
NsenterMounter
)
List
()
([]
MountPoint
,
error
)
{
return
listProcMounts
(
hostProcMountsPath
)
}
// IsMountPoint determines whether a path is a mountpoint by calling findmnt
// in the host's root mount namespace.
func
(
*
NsenterMounter
)
IsMountPoint
(
file
string
)
(
bool
,
error
)
{
file
,
err
:=
filepath
.
Abs
(
file
)
if
err
!=
nil
{
return
false
,
err
}
args
:=
[]
string
{
"--mount=/rootfs/proc/1/ns/mnt"
,
"/usr/bin/findmnt"
,
"-o"
,
"target"
,
"--noheadings"
,
"--target"
,
file
}
glog
.
V
(
5
)
.
Infof
(
"findmnt command: %v %v"
,
nsenterPath
,
args
)
exec
:=
exec
.
New
()
out
,
err
:=
exec
.
Command
(
nsenterPath
,
args
...
)
.
CombinedOutput
()
if
err
!=
nil
{
// If findmnt didn't run, just claim it's not a mount point.
return
false
,
nil
}
strOut
:=
strings
.
TrimSuffix
(
string
(
out
),
"
\n
"
)
glog
.
V
(
5
)
.
Infof
(
"IsMountPoint findmnt output: %v"
,
strOut
)
if
strOut
==
file
{
return
true
,
nil
}
return
false
,
nil
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment