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
5dff0496
Commit
5dff0496
authored
Jul 29, 2015
by
Mike Danese
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #9384 from pmorie/emptydir-nonroot
Support emptydir volumes for containers running as non-root
parents
4ddfa5d5
5394aa97
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
563 additions
and
77 deletions
+563
-77
Dockerfile
contrib/for-tests/mount-tester-user/Dockerfile
+16
-0
Makefile
contrib/for-tests/mount-tester-user/Makefile
+9
-0
Makefile
contrib/for-tests/mount-tester/Makefile
+1
-1
mt.go
contrib/for-tests/mount-tester/mt.go
+49
-10
util.go
pkg/securitycontext/util.go
+26
-1
util_test.go
pkg/securitycontext/util_test.go
+85
-0
chcon_runner.go
pkg/volume/empty_dir/chcon_runner.go
+27
-0
chcon_runner_linux.go
pkg/volume/empty_dir/chcon_runner_linux.go
+34
-0
chcon_runner_unsupported.go
pkg/volume/empty_dir/chcon_runner_unsupported.go
+26
-0
empty_dir.go
pkg/volume/empty_dir/empty_dir.go
+109
-27
empty_dir_linux.go
pkg/volume/empty_dir/empty_dir_linux.go
+6
-0
empty_dir_test.go
pkg/volume/empty_dir/empty_dir_test.go
+0
-0
empty_dir_unsupported.go
pkg/volume/empty_dir/empty_dir_unsupported.go
+7
-1
empty_dir.go
test/e2e/empty_dir.go
+168
-37
No files found.
contrib/for-tests/mount-tester-user/Dockerfile
0 → 100644
View file @
5dff0496
# Copyright 2015 Google Inc. 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.
FROM
gcr.io/google-containers/mounttest:0.3
USER
1001
contrib/for-tests/mount-tester-user/Makefile
0 → 100644
View file @
5dff0496
all
:
push
TAG
=
0.1
image
:
sudo
docker build
-t
gcr.io/google_containers/mounttest-user:
$(TAG)
.
push
:
image
gcloud docker push gcr.io/google_containers/mounttest-user:
$(TAG)
contrib/for-tests/mount-tester/Makefile
View file @
5dff0496
all
:
push
all
:
push
TAG
=
0.
2
TAG
=
0.
3
mt
:
mt.go
mt
:
mt.go
CGO_ENABLED
=
0
GOOS
=
linux go build
-a
-installsuffix
cgo
-ldflags
'-w'
./mt.go
CGO_ENABLED
=
0
GOOS
=
linux go build
-a
-installsuffix
cgo
-ldflags
'-w'
./mt.go
...
...
contrib/for-tests/mount-tester/mt.go
View file @
5dff0496
...
@@ -25,17 +25,23 @@ import (
...
@@ -25,17 +25,23 @@ import (
)
)
var
(
var
(
fsTypePath
=
""
fsTypePath
=
""
fileModePath
=
""
fileModePath
=
""
readFileContentPath
=
""
filePermPath
=
""
readWriteNewFilePath
=
""
readFileContentPath
=
""
newFilePath0644
=
""
newFilePath0666
=
""
newFilePath0777
=
""
)
)
func
init
()
{
func
init
()
{
flag
.
StringVar
(
&
fsTypePath
,
"fs_type"
,
""
,
"Path to print the fs type for"
)
flag
.
StringVar
(
&
fsTypePath
,
"fs_type"
,
""
,
"Path to print the fs type for"
)
flag
.
StringVar
(
&
fileModePath
,
"file_mode"
,
""
,
"Path to print the filemode of"
)
flag
.
StringVar
(
&
fileModePath
,
"file_mode"
,
""
,
"Path to print the mode bits of"
)
flag
.
StringVar
(
&
filePermPath
,
"file_perm"
,
""
,
"Path to print the perms of"
)
flag
.
StringVar
(
&
readFileContentPath
,
"file_content"
,
""
,
"Path to read the file content from"
)
flag
.
StringVar
(
&
readFileContentPath
,
"file_content"
,
""
,
"Path to read the file content from"
)
flag
.
StringVar
(
&
readWriteNewFilePath
,
"rw_new_file"
,
""
,
"Path to write to and read from"
)
flag
.
StringVar
(
&
newFilePath0644
,
"new_file_0644"
,
""
,
"Path to write to and read from with perm 0644"
)
flag
.
StringVar
(
&
newFilePath0666
,
"new_file_0666"
,
""
,
"Path to write to and read from with perm 0666"
)
flag
.
StringVar
(
&
newFilePath0777
,
"new_file_0777"
,
""
,
"Path to write to and read from with perm 0777"
)
}
}
// This program performs some tests on the filesystem as dictated by the
// This program performs some tests on the filesystem as dictated by the
...
@@ -48,6 +54,9 @@ func main() {
...
@@ -48,6 +54,9 @@ func main() {
errs
=
[]
error
{}
errs
=
[]
error
{}
)
)
// Clear the umask so we can set any mode bits we want.
syscall
.
Umask
(
0000
)
// NOTE: the ordering of execution of the various command line
// NOTE: the ordering of execution of the various command line
// flags is intentional and allows a single command to:
// flags is intentional and allows a single command to:
//
//
...
@@ -62,7 +71,17 @@ func main() {
...
@@ -62,7 +71,17 @@ func main() {
errs
=
append
(
errs
,
err
)
errs
=
append
(
errs
,
err
)
}
}
err
=
readWriteNewFile
(
readWriteNewFilePath
)
err
=
readWriteNewFile
(
newFilePath0644
,
0644
)
if
err
!=
nil
{
errs
=
append
(
errs
,
err
)
}
err
=
readWriteNewFile
(
newFilePath0666
,
0666
)
if
err
!=
nil
{
errs
=
append
(
errs
,
err
)
}
err
=
readWriteNewFile
(
newFilePath0777
,
0777
)
if
err
!=
nil
{
if
err
!=
nil
{
errs
=
append
(
errs
,
err
)
errs
=
append
(
errs
,
err
)
}
}
...
@@ -72,6 +91,11 @@ func main() {
...
@@ -72,6 +91,11 @@ func main() {
errs
=
append
(
errs
,
err
)
errs
=
append
(
errs
,
err
)
}
}
err
=
filePerm
(
filePermPath
)
if
err
!=
nil
{
errs
=
append
(
errs
,
err
)
}
err
=
readFileContent
(
readFileContentPath
)
err
=
readFileContent
(
readFileContentPath
)
if
err
!=
nil
{
if
err
!=
nil
{
errs
=
append
(
errs
,
err
)
errs
=
append
(
errs
,
err
)
...
@@ -94,7 +118,7 @@ func fsType(path string) error {
...
@@ -94,7 +118,7 @@ func fsType(path string) error {
buf
:=
syscall
.
Statfs_t
{}
buf
:=
syscall
.
Statfs_t
{}
if
err
:=
syscall
.
Statfs
(
path
,
&
buf
);
err
!=
nil
{
if
err
:=
syscall
.
Statfs
(
path
,
&
buf
);
err
!=
nil
{
fmt
.
Printf
(
"error from statfs(%q): %v"
,
path
,
err
)
fmt
.
Printf
(
"error from statfs(%q): %v
\n
"
,
path
,
err
)
return
err
return
err
}
}
...
@@ -122,6 +146,21 @@ func fileMode(path string) error {
...
@@ -122,6 +146,21 @@ func fileMode(path string) error {
return
nil
return
nil
}
}
func
filePerm
(
path
string
)
error
{
if
path
==
""
{
return
nil
}
fileinfo
,
err
:=
os
.
Lstat
(
path
)
if
err
!=
nil
{
fmt
.
Printf
(
"error from Lstat(%q): %v
\n
"
,
path
,
err
)
return
err
}
fmt
.
Printf
(
"perms of file %q: %v
\n
"
,
path
,
fileinfo
.
Mode
()
.
Perm
())
return
nil
}
func
readFileContent
(
path
string
)
error
{
func
readFileContent
(
path
string
)
error
{
if
path
==
""
{
if
path
==
""
{
return
nil
return
nil
...
@@ -138,13 +177,13 @@ func readFileContent(path string) error {
...
@@ -138,13 +177,13 @@ func readFileContent(path string) error {
return
nil
return
nil
}
}
func
readWriteNewFile
(
path
string
)
error
{
func
readWriteNewFile
(
path
string
,
perm
os
.
FileMode
)
error
{
if
path
==
""
{
if
path
==
""
{
return
nil
return
nil
}
}
content
:=
"mount-tester new file
\n
"
content
:=
"mount-tester new file
\n
"
err
:=
ioutil
.
WriteFile
(
path
,
[]
byte
(
content
),
0644
)
err
:=
ioutil
.
WriteFile
(
path
,
[]
byte
(
content
),
perm
)
if
err
!=
nil
{
if
err
!=
nil
{
fmt
.
Printf
(
"error writing new file %q: %v
\n
"
,
path
,
err
)
fmt
.
Printf
(
"error writing new file %q: %v
\n
"
,
path
,
err
)
return
err
return
err
...
...
pkg/securitycontext/util.go
View file @
5dff0496
...
@@ -16,7 +16,12 @@ limitations under the License.
...
@@ -16,7 +16,12 @@ limitations under the License.
package
securitycontext
package
securitycontext
import
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
import
(
"fmt"
"strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
)
// HasPrivilegedRequest returns the value of SecurityContext.Privileged, taking into account
// HasPrivilegedRequest returns the value of SecurityContext.Privileged, taking into account
// the possibility of nils
// the possibility of nils
...
@@ -41,3 +46,23 @@ func HasCapabilitiesRequest(container *api.Container) bool {
...
@@ -41,3 +46,23 @@ func HasCapabilitiesRequest(container *api.Container) bool {
}
}
return
len
(
container
.
SecurityContext
.
Capabilities
.
Add
)
>
0
||
len
(
container
.
SecurityContext
.
Capabilities
.
Drop
)
>
0
return
len
(
container
.
SecurityContext
.
Capabilities
.
Add
)
>
0
||
len
(
container
.
SecurityContext
.
Capabilities
.
Drop
)
>
0
}
}
const
expectedSELinuxContextFields
=
4
// ParseSELinuxOptions parses a string containing a full SELinux context
// (user, role, type, and level) into an SELinuxOptions object. If the
// context is malformed, an error is returned.
func
ParseSELinuxOptions
(
context
string
)
(
*
api
.
SELinuxOptions
,
error
)
{
fields
:=
strings
.
SplitN
(
context
,
":"
,
expectedSELinuxContextFields
)
if
len
(
fields
)
!=
expectedSELinuxContextFields
{
return
nil
,
fmt
.
Errorf
(
"expected %v fields in selinuxcontext; got %v (context: %v)"
,
expectedSELinuxContextFields
,
len
(
fields
),
context
)
}
return
&
api
.
SELinuxOptions
{
User
:
fields
[
0
],
Role
:
fields
[
1
],
Type
:
fields
[
2
],
Level
:
fields
[
3
],
},
nil
}
pkg/securitycontext/util_test.go
0 → 100644
View file @
5dff0496
/*
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
securitycontext
import
(
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
)
func
TestParseSELinuxOptions
(
t
*
testing
.
T
)
{
cases
:=
[]
struct
{
name
string
input
string
expected
*
api
.
SELinuxOptions
}{
{
name
:
"simple"
,
input
:
"user_t:role_t:type_t:s0"
,
expected
:
&
api
.
SELinuxOptions
{
User
:
"user_t"
,
Role
:
"role_t"
,
Type
:
"type_t"
,
Level
:
"s0"
,
},
},
{
name
:
"simple + categories"
,
input
:
"user_t:role_t:type_t:s0:c0"
,
expected
:
&
api
.
SELinuxOptions
{
User
:
"user_t"
,
Role
:
"role_t"
,
Type
:
"type_t"
,
Level
:
"s0:c0"
,
},
},
{
name
:
"not enough fields"
,
input
:
"type_t:s0:c0"
,
},
}
for
_
,
tc
:=
range
cases
{
result
,
err
:=
ParseSELinuxOptions
(
tc
.
input
)
if
err
!=
nil
{
if
tc
.
expected
==
nil
{
continue
}
else
{
t
.
Errorf
(
"%v: unexpected error: %v"
,
tc
.
name
,
err
)
}
}
compareContexts
(
tc
.
name
,
tc
.
expected
,
result
,
t
)
}
}
func
compareContexts
(
name
string
,
ex
,
ac
*
api
.
SELinuxOptions
,
t
*
testing
.
T
)
{
if
e
,
a
:=
ex
.
User
,
ac
.
User
;
e
!=
a
{
t
.
Errorf
(
"%v: expected user: %v, got: %v"
,
name
,
e
,
a
)
}
if
e
,
a
:=
ex
.
Role
,
ac
.
Role
;
e
!=
a
{
t
.
Errorf
(
"%v: expected role: %v, got: %v"
,
name
,
e
,
a
)
}
if
e
,
a
:=
ex
.
Type
,
ac
.
Type
;
e
!=
a
{
t
.
Errorf
(
"%v: expected type: %v, got: %v"
,
name
,
e
,
a
)
}
if
e
,
a
:=
ex
.
Level
,
ac
.
Level
;
e
!=
a
{
t
.
Errorf
(
"%v: expected level: %v, got: %v"
,
name
,
e
,
a
)
}
}
pkg/volume/empty_dir/chcon_runner.go
0 → 100644
View file @
5dff0496
/*
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
empty_dir
// chconRunner knows how to chcon a directory.
type
chconRunner
interface
{
SetContext
(
dir
,
context
string
)
error
}
// newChconRunner returns a new chconRunner.
func
newChconRunner
()
chconRunner
{
return
&
realChconRunner
{}
}
pkg/volume/empty_dir/chcon_runner_linux.go
0 → 100644
View file @
5dff0496
// +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
empty_dir
import
(
"github.com/docker/libcontainer/selinux"
)
type
realChconRunner
struct
{}
func
(
_
*
realChconRunner
)
SetContext
(
dir
,
context
string
)
error
{
// If SELinux is not enabled, return an empty string
if
!
selinux
.
SelinuxEnabled
()
{
return
nil
}
return
selinux
.
Setfilecon
(
dir
,
context
)
}
pkg/volume/empty_dir/chcon_runner_unsupported.go
0 → 100644
View file @
5dff0496
// +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
empty_dir
type
realChconRunner
struct
{}
func
(
_
*
realChconRunner
)
SetContext
(
dir
,
context
string
)
error
{
// NOP
return
nil
}
pkg/volume/empty_dir/empty_dir.go
View file @
5dff0496
...
@@ -19,15 +19,24 @@ package empty_dir
...
@@ -19,15 +19,24 @@ package empty_dir
import
(
import
(
"fmt"
"fmt"
"os"
"os"
"path"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/mount"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/mount"
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
volumeutil
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume/util"
"github.com/golang/glog"
"github.com/golang/glog"
)
)
// TODO: in the near future, this will be changed to be more restrictive
// and the group will be set to allow containers to use emptyDir volumes
// from the group attribute.
//
// https://github.com/GoogleCloudPlatform/kubernetes/issues/2630
const
perm
os
.
FileMode
=
0777
// This is the primary entrypoint for volume plugins.
// This is the primary entrypoint for volume plugins.
func
ProbeVolumePlugins
()
[]
volume
.
VolumePlugin
{
func
ProbeVolumePlugins
()
[]
volume
.
VolumePlugin
{
return
[]
volume
.
VolumePlugin
{
return
[]
volume
.
VolumePlugin
{
...
@@ -61,22 +70,23 @@ func (plugin *emptyDirPlugin) CanSupport(spec *volume.Spec) bool {
...
@@ -61,22 +70,23 @@ func (plugin *emptyDirPlugin) CanSupport(spec *volume.Spec) bool {
}
}
func
(
plugin
*
emptyDirPlugin
)
NewBuilder
(
spec
*
volume
.
Spec
,
pod
*
api
.
Pod
,
opts
volume
.
VolumeOptions
,
mounter
mount
.
Interface
)
(
volume
.
Builder
,
error
)
{
func
(
plugin
*
emptyDirPlugin
)
NewBuilder
(
spec
*
volume
.
Spec
,
pod
*
api
.
Pod
,
opts
volume
.
VolumeOptions
,
mounter
mount
.
Interface
)
(
volume
.
Builder
,
error
)
{
return
plugin
.
newBuilderInternal
(
spec
,
pod
,
mounter
,
&
realMountDetector
{
mounter
},
opts
)
return
plugin
.
newBuilderInternal
(
spec
,
pod
,
mounter
,
&
realMountDetector
{
mounter
},
opts
,
newChconRunner
()
)
}
}
func
(
plugin
*
emptyDirPlugin
)
newBuilderInternal
(
spec
*
volume
.
Spec
,
pod
*
api
.
Pod
,
mounter
mount
.
Interface
,
mountDetector
mountDetector
,
opts
volume
.
VolumeOptions
)
(
volume
.
Builder
,
error
)
{
func
(
plugin
*
emptyDirPlugin
)
newBuilderInternal
(
spec
*
volume
.
Spec
,
pod
*
api
.
Pod
,
mounter
mount
.
Interface
,
mountDetector
mountDetector
,
opts
volume
.
VolumeOptions
,
chconRunner
chconRunner
)
(
volume
.
Builder
,
error
)
{
medium
:=
api
.
StorageMediumDefault
medium
:=
api
.
StorageMediumDefault
if
spec
.
VolumeSource
.
EmptyDir
!=
nil
{
// Support a non-specified source as EmptyDir.
if
spec
.
VolumeSource
.
EmptyDir
!=
nil
{
// Support a non-specified source as EmptyDir.
medium
=
spec
.
VolumeSource
.
EmptyDir
.
Medium
medium
=
spec
.
VolumeSource
.
EmptyDir
.
Medium
}
}
return
&
emptyDir
{
return
&
emptyDir
{
pod
UID
:
pod
.
UID
,
pod
:
pod
,
volName
:
spec
.
Name
,
volName
:
spec
.
Name
,
medium
:
medium
,
medium
:
medium
,
mounter
:
mounter
,
mounter
:
mounter
,
mountDetector
:
mountDetector
,
mountDetector
:
mountDetector
,
plugin
:
plugin
,
plugin
:
plugin
,
rootContext
:
opts
.
RootContext
,
rootContext
:
opts
.
RootContext
,
chconRunner
:
chconRunner
,
},
nil
},
nil
}
}
...
@@ -87,7 +97,7 @@ func (plugin *emptyDirPlugin) NewCleaner(volName string, podUID types.UID, mount
...
@@ -87,7 +97,7 @@ func (plugin *emptyDirPlugin) NewCleaner(volName string, podUID types.UID, mount
func
(
plugin
*
emptyDirPlugin
)
newCleanerInternal
(
volName
string
,
podUID
types
.
UID
,
mounter
mount
.
Interface
,
mountDetector
mountDetector
)
(
volume
.
Cleaner
,
error
)
{
func
(
plugin
*
emptyDirPlugin
)
newCleanerInternal
(
volName
string
,
podUID
types
.
UID
,
mounter
mount
.
Interface
,
mountDetector
mountDetector
)
(
volume
.
Cleaner
,
error
)
{
ed
:=
&
emptyDir
{
ed
:=
&
emptyDir
{
pod
UID
:
podUID
,
pod
:
&
api
.
Pod
{
ObjectMeta
:
api
.
ObjectMeta
{
UID
:
podUID
}}
,
volName
:
volName
,
volName
:
volName
,
medium
:
api
.
StorageMediumDefault
,
// might be changed later
medium
:
api
.
StorageMediumDefault
,
// might be changed later
mounter
:
mounter
,
mounter
:
mounter
,
...
@@ -117,13 +127,14 @@ const (
...
@@ -117,13 +127,14 @@ const (
// EmptyDir volumes are temporary directories exposed to the pod.
// EmptyDir volumes are temporary directories exposed to the pod.
// These do not persist beyond the lifetime of a pod.
// These do not persist beyond the lifetime of a pod.
type
emptyDir
struct
{
type
emptyDir
struct
{
pod
UID
types
.
UID
pod
*
api
.
Pod
volName
string
volName
string
medium
api
.
StorageMedium
medium
api
.
StorageMedium
mounter
mount
.
Interface
mounter
mount
.
Interface
mountDetector
mountDetector
mountDetector
mountDetector
plugin
*
emptyDirPlugin
plugin
*
emptyDirPlugin
rootContext
string
rootContext
string
chconRunner
chconRunner
}
}
// SetUp creates new directory.
// SetUp creates new directory.
...
@@ -133,29 +144,58 @@ func (ed *emptyDir) SetUp() error {
...
@@ -133,29 +144,58 @@ func (ed *emptyDir) SetUp() error {
// SetUpAt creates new directory.
// SetUpAt creates new directory.
func
(
ed
*
emptyDir
)
SetUpAt
(
dir
string
)
error
{
func
(
ed
*
emptyDir
)
SetUpAt
(
dir
string
)
error
{
isMnt
,
err
:=
ed
.
mounter
.
IsMountPoint
(
dir
)
// Getting an os.IsNotExist err from is a contingency; the directory
// may not exist yet, in which case, setup should run.
if
err
!=
nil
&&
!
os
.
IsNotExist
(
err
)
{
return
err
}
// If the plugin readiness file is present for this volume, and the
// storage medium is the default, then the volume is ready. If the
// medium is memory, and a mountpoint is present, then the volume is
// ready.
if
volumeutil
.
IsReady
(
ed
.
getMetaDir
())
{
if
ed
.
medium
==
api
.
StorageMediumMemory
&&
isMnt
{
return
nil
}
else
if
ed
.
medium
==
api
.
StorageMediumDefault
{
return
nil
}
}
// Determine the effective SELinuxOptions to use for this volume.
securityContext
:=
""
if
selinuxEnabled
()
{
securityContext
=
ed
.
rootContext
}
switch
ed
.
medium
{
switch
ed
.
medium
{
case
api
.
StorageMediumDefault
:
case
api
.
StorageMediumDefault
:
return
ed
.
setupDefault
(
dir
)
err
=
ed
.
setupDir
(
dir
,
securityContext
)
case
api
.
StorageMediumMemory
:
case
api
.
StorageMediumMemory
:
return
ed
.
setupTmpfs
(
dir
)
err
=
ed
.
setupTmpfs
(
dir
,
securityContext
)
default
:
default
:
return
fmt
.
Errorf
(
"unknown storage medium %q"
,
ed
.
medium
)
err
=
fmt
.
Errorf
(
"unknown storage medium %q"
,
ed
.
medium
)
}
if
err
==
nil
{
volumeutil
.
SetReady
(
ed
.
getMetaDir
())
}
}
return
err
}
}
func
(
ed
*
emptyDir
)
IsReadOnly
()
bool
{
func
(
ed
*
emptyDir
)
IsReadOnly
()
bool
{
return
false
return
false
}
}
func
(
ed
*
emptyDir
)
setupDefault
(
dir
string
)
error
{
// setupTmpfs creates a tmpfs mount at the specified directory with the
return
os
.
MkdirAll
(
dir
,
0750
)
// specified SELinux context.
}
func
(
ed
*
emptyDir
)
setupTmpfs
(
dir
string
,
selinuxContext
string
)
error
{
func
(
ed
*
emptyDir
)
setupTmpfs
(
dir
string
)
error
{
if
ed
.
mounter
==
nil
{
if
ed
.
mounter
==
nil
{
return
fmt
.
Errorf
(
"memory storage requested, but mounter is nil"
)
return
fmt
.
Errorf
(
"memory storage requested, but mounter is nil"
)
}
}
if
err
:=
os
.
MkdirAll
(
dir
,
0750
);
err
!=
nil
{
if
err
:=
ed
.
setupDir
(
dir
,
selinuxContext
);
err
!=
nil
{
return
err
return
err
}
}
// Make SetUp idempotent.
// Make SetUp idempotent.
...
@@ -170,28 +210,66 @@ func (ed *emptyDir) setupTmpfs(dir string) error {
...
@@ -170,28 +210,66 @@ func (ed *emptyDir) setupTmpfs(dir string) error {
}
}
// By default a tmpfs mount will receive a different SELinux context
// By default a tmpfs mount will receive a different SELinux context
// from that of the Kubelet root directory which is not readable from
// which is not readable from the SELinux context of a docker container.
// the SELinux context of a docker container.
var
opts
[]
string
//
if
selinuxContext
!=
""
{
// getTmpfsMountOptions gets the mount option to set the context of
opts
=
[]
string
{
fmt
.
Sprintf
(
"rootcontext=
\"
%v
\"
"
,
selinuxContext
)}
// the tmpfs mount so that it can be read from the SELinux context of
}
else
{
// the container.
opts
=
[]
string
{}
opts
:=
ed
.
getTmpfsMountOptions
()
}
glog
.
V
(
3
)
.
Infof
(
"pod %v: mounting tmpfs for volume %v with opts %v"
,
ed
.
podUID
,
ed
.
volName
,
opts
)
glog
.
V
(
3
)
.
Infof
(
"pod %v: mounting tmpfs for volume %v with opts %v"
,
ed
.
pod
.
UID
,
ed
.
volName
,
opts
)
return
ed
.
mounter
.
Mount
(
"tmpfs"
,
dir
,
"tmpfs"
,
opts
)
return
ed
.
mounter
.
Mount
(
"tmpfs"
,
dir
,
"tmpfs"
,
opts
)
}
}
func
(
ed
*
emptyDir
)
getTmpfsMountOptions
()
[]
string
{
// setupDir creates the directory with the specified SELinux context and
if
ed
.
rootContext
==
""
{
// the default permissions specified by the perm constant.
return
[]
string
{
""
}
func
(
ed
*
emptyDir
)
setupDir
(
dir
,
selinuxContext
string
)
error
{
// Create the directory if it doesn't already exist.
if
err
:=
os
.
MkdirAll
(
dir
,
perm
);
err
!=
nil
{
return
err
}
}
return
[]
string
{
fmt
.
Sprintf
(
"rootcontext=
\"
%v
\"
"
,
ed
.
rootContext
)}
// stat the directory to read permission bits
fileinfo
,
err
:=
os
.
Lstat
(
dir
)
if
err
!=
nil
{
return
err
}
if
fileinfo
.
Mode
()
.
Perm
()
!=
perm
.
Perm
()
{
// If the permissions on the created directory are wrong, the
// kubelet is probably running with a umask set. In order to
// avoid clearing the umask for the entire process or locking
// the thread, clearing the umask, creating the dir, restoring
// the umask, and unlocking the thread, we do a chmod to set
// the specific bits we need.
err
:=
os
.
Chmod
(
dir
,
perm
)
if
err
!=
nil
{
return
err
}
fileinfo
,
err
=
os
.
Lstat
(
dir
)
if
err
!=
nil
{
return
err
}
if
fileinfo
.
Mode
()
.
Perm
()
!=
perm
.
Perm
()
{
glog
.
Errorf
(
"Expected directory %q permissions to be: %s; got: %s"
,
dir
,
perm
.
Perm
(),
fileinfo
.
Mode
()
.
Perm
())
}
}
// Set the context on the directory, if appropriate
if
selinuxContext
!=
""
{
glog
.
V
(
3
)
.
Infof
(
"Setting SELinux context for %v to %v"
,
dir
,
selinuxContext
)
return
ed
.
chconRunner
.
SetContext
(
dir
,
selinuxContext
)
}
return
nil
}
}
func
(
ed
*
emptyDir
)
GetPath
()
string
{
func
(
ed
*
emptyDir
)
GetPath
()
string
{
name
:=
emptyDirPluginName
name
:=
emptyDirPluginName
return
ed
.
plugin
.
host
.
GetPodVolumeDir
(
ed
.
podUID
,
util
.
EscapeQualifiedNameForDisk
(
name
),
ed
.
volName
)
return
ed
.
plugin
.
host
.
GetPodVolumeDir
(
ed
.
pod
.
UID
,
util
.
EscapeQualifiedNameForDisk
(
name
),
ed
.
volName
)
}
}
// TearDown simply discards everything in the directory.
// TearDown simply discards everything in the directory.
...
@@ -238,3 +316,7 @@ func (ed *emptyDir) teardownTmpfs(dir string) error {
...
@@ -238,3 +316,7 @@ func (ed *emptyDir) teardownTmpfs(dir string) error {
}
}
return
nil
return
nil
}
}
func
(
ed
*
emptyDir
)
getMetaDir
()
string
{
return
path
.
Join
(
ed
.
plugin
.
host
.
GetPodPluginDir
(
ed
.
pod
.
UID
,
util
.
EscapeQualifiedNameForDisk
(
emptyDirPluginName
)),
ed
.
volName
)
}
pkg/volume/empty_dir/empty_dir_linux.go
View file @
5dff0496
...
@@ -23,6 +23,7 @@ import (
...
@@ -23,6 +23,7 @@ import (
"syscall"
"syscall"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/mount"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/mount"
"github.com/docker/libcontainer/selinux"
"github.com/golang/glog"
"github.com/golang/glog"
)
)
...
@@ -51,3 +52,8 @@ func (m *realMountDetector) GetMountMedium(path string) (storageMedium, bool, er
...
@@ -51,3 +52,8 @@ func (m *realMountDetector) GetMountMedium(path string) (storageMedium, bool, er
}
}
return
mediumUnknown
,
isMnt
,
nil
return
mediumUnknown
,
isMnt
,
nil
}
}
// selinuxEnabled determines whether SELinux is enabled.
func
selinuxEnabled
()
bool
{
return
selinux
.
SelinuxEnabled
()
}
pkg/volume/empty_dir/empty_dir_test.go
View file @
5dff0496
This diff is collapsed.
Click to expand it.
pkg/volume/empty_dir/empty_dir_unsupported.go
View file @
5dff0496
...
@@ -18,7 +18,9 @@ limitations under the License.
...
@@ -18,7 +18,9 @@ limitations under the License.
package
empty_dir
package
empty_dir
import
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/mount"
import
(
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/mount"
)
// realMountDetector pretends to implement mediumer.
// realMountDetector pretends to implement mediumer.
type
realMountDetector
struct
{
type
realMountDetector
struct
{
...
@@ -28,3 +30,7 @@ type realMountDetector struct {
...
@@ -28,3 +30,7 @@ type realMountDetector struct {
func
(
m
*
realMountDetector
)
GetMountMedium
(
path
string
)
(
storageMedium
,
bool
,
error
)
{
func
(
m
*
realMountDetector
)
GetMountMedium
(
path
string
)
(
storageMedium
,
bool
,
error
)
{
return
mediumUnknown
,
false
,
nil
return
mediumUnknown
,
false
,
nil
}
}
func
selinuxEnabled
()
bool
{
return
false
}
test/e2e/empty_dir.go
View file @
5dff0496
...
@@ -27,51 +27,182 @@ import (
...
@@ -27,51 +27,182 @@ import (
.
"github.com/onsi/ginkgo"
.
"github.com/onsi/ginkgo"
)
)
const
(
testImageRootUid
=
"gcr.io/google_containers/mounttest:0.3"
testImageNonRootUid
=
"gcr.io/google_containers/mounttest-user:0.1"
)
var
_
=
Describe
(
"EmptyDir volumes"
,
func
()
{
var
_
=
Describe
(
"EmptyDir volumes"
,
func
()
{
f
:=
NewFramework
(
"emptydir"
)
f
:=
NewFramework
(
"emptydir"
)
It
(
"should have the correct mode"
,
func
()
{
It
(
"volume on tmpfs should have the correct mode"
,
func
()
{
volumePath
:=
"/test-volume"
doTestVolumeMode
(
f
,
testImageRootUid
,
api
.
StorageMediumMemory
)
source
:=
&
api
.
EmptyDirVolumeSource
{
})
Medium
:
api
.
StorageMediumMemory
,
}
It
(
"should support (root,0644,tmpfs)"
,
func
()
{
pod
:=
testPodWithVolume
(
volumePath
,
source
)
doTest0644
(
f
,
testImageRootUid
,
api
.
StorageMediumMemory
)
})
pod
.
Spec
.
Containers
[
0
]
.
Args
=
[]
string
{
fmt
.
Sprintf
(
"--fs_type=%v"
,
volumePath
),
It
(
"should support (root,0666,tmpfs)"
,
func
()
{
fmt
.
Sprintf
(
"--file_mode=%v"
,
volumePath
),
doTest0666
(
f
,
testImageRootUid
,
api
.
StorageMediumMemory
)
}
})
f
.
TestContainerOutput
(
"emptydir r/w on tmpfs"
,
pod
,
0
,
[]
string
{
"mount type of
\"
/test-volume
\"
: tmpfs"
,
It
(
"should support (root,0777,tmpfs)"
,
func
()
{
"mode of file
\"
/test-volume
\"
: dtrwxrwxrwx"
,
// we expect the sticky bit (mode flag t) to be set for the dir
doTest0777
(
f
,
testImageRootUid
,
api
.
StorageMediumMemory
)
})
})
It
(
"should support (non-root,0644,tmpfs)"
,
func
()
{
doTest0644
(
f
,
testImageNonRootUid
,
api
.
StorageMediumMemory
)
})
It
(
"should support (non-root,0666,tmpfs)"
,
func
()
{
doTest0666
(
f
,
testImageNonRootUid
,
api
.
StorageMediumMemory
)
})
It
(
"should support (non-root,0777,tmpfs)"
,
func
()
{
doTest0777
(
f
,
testImageNonRootUid
,
api
.
StorageMediumMemory
)
})
It
(
"volume on default medium should have the correct mode"
,
func
()
{
doTestVolumeMode
(
f
,
testImageRootUid
,
api
.
StorageMediumDefault
)
})
It
(
"should support (root,0644,default)"
,
func
()
{
doTest0644
(
f
,
testImageRootUid
,
api
.
StorageMediumDefault
)
})
It
(
"should support (root,0666,default)"
,
func
()
{
doTest0666
(
f
,
testImageRootUid
,
api
.
StorageMediumDefault
)
})
It
(
"should support (root,0777,default)"
,
func
()
{
doTest0777
(
f
,
testImageRootUid
,
api
.
StorageMediumDefault
)
})
})
It
(
"should support r/w"
,
func
()
{
It
(
"should support (non-root,0644,default)"
,
func
()
{
volumePath
:=
"/test-volume"
doTest0644
(
f
,
testImageNonRootUid
,
api
.
StorageMediumDefault
)
filePath
:=
path
.
Join
(
volumePath
,
"test-file"
)
})
source
:=
&
api
.
EmptyDirVolumeSource
{
Medium
:
api
.
StorageMediumMemory
,
It
(
"should support (non-root,0666,default)"
,
func
()
{
}
doTest0666
(
f
,
testImageNonRootUid
,
api
.
StorageMediumDefault
)
pod
:=
testPodWithVolume
(
volumePath
,
source
)
})
pod
.
Spec
.
Containers
[
0
]
.
Args
=
[]
string
{
It
(
"should support (non-root,0777,default)"
,
func
()
{
fmt
.
Sprintf
(
"--fs_type=%v"
,
volumePath
),
doTest0777
(
f
,
testImageNonRootUid
,
api
.
StorageMediumDefault
)
fmt
.
Sprintf
(
"--rw_new_file=%v"
,
filePath
),
fmt
.
Sprintf
(
"--file_mode=%v"
,
filePath
),
}
f
.
TestContainerOutput
(
"emptydir r/w on tmpfs"
,
pod
,
0
,
[]
string
{
"mount type of
\"
/test-volume
\"
: tmpfs"
,
"mode of file
\"
/test-volume/test-file
\"
: -rw-r--r--"
,
"content of file
\"
/test-volume/test-file
\"
: mount-tester new file"
,
})
})
})
})
})
const
containerName
=
"test-container"
const
(
const
volumeName
=
"test-volume"
containerName
=
"test-container"
volumeName
=
"test-volume"
)
func
doTestVolumeMode
(
f
*
Framework
,
image
string
,
medium
api
.
StorageMedium
)
{
var
(
volumePath
=
"/test-volume"
source
=
&
api
.
EmptyDirVolumeSource
{
Medium
:
medium
}
pod
=
testPodWithVolume
(
testImageRootUid
,
volumePath
,
source
)
)
pod
.
Spec
.
Containers
[
0
]
.
Args
=
[]
string
{
fmt
.
Sprintf
(
"--fs_type=%v"
,
volumePath
),
fmt
.
Sprintf
(
"--file_perm=%v"
,
volumePath
),
}
msg
:=
fmt
.
Sprintf
(
"emptydir volume type on %v"
,
formatMedium
(
medium
))
out
:=
[]
string
{
"perms of file
\"
/test-volume
\"
: -rwxrwxrwx"
,
}
if
medium
==
api
.
StorageMediumMemory
{
out
=
append
(
out
,
"mount type of
\"
/test-volume
\"
: tmpfs"
)
}
f
.
TestContainerOutput
(
msg
,
pod
,
0
,
out
)
}
func
doTest0644
(
f
*
Framework
,
image
string
,
medium
api
.
StorageMedium
)
{
var
(
volumePath
=
"/test-volume"
filePath
=
path
.
Join
(
volumePath
,
"test-file"
)
source
=
&
api
.
EmptyDirVolumeSource
{
Medium
:
medium
}
pod
=
testPodWithVolume
(
image
,
volumePath
,
source
)
)
pod
.
Spec
.
Containers
[
0
]
.
Args
=
[]
string
{
fmt
.
Sprintf
(
"--fs_type=%v"
,
volumePath
),
fmt
.
Sprintf
(
"--new_file_0644=%v"
,
filePath
),
fmt
.
Sprintf
(
"--file_perm=%v"
,
filePath
),
}
msg
:=
fmt
.
Sprintf
(
"emptydir 0644 on %v"
,
formatMedium
(
medium
))
out
:=
[]
string
{
"perms of file
\"
/test-volume/test-file
\"
: -rw-r--r--"
,
"content of file
\"
/test-volume/test-file
\"
: mount-tester new file"
,
}
if
medium
==
api
.
StorageMediumMemory
{
out
=
append
(
out
,
"mount type of
\"
/test-volume
\"
: tmpfs"
)
}
f
.
TestContainerOutput
(
msg
,
pod
,
0
,
out
)
}
func
doTest0666
(
f
*
Framework
,
image
string
,
medium
api
.
StorageMedium
)
{
var
(
volumePath
=
"/test-volume"
filePath
=
path
.
Join
(
volumePath
,
"test-file"
)
source
=
&
api
.
EmptyDirVolumeSource
{
Medium
:
medium
}
pod
=
testPodWithVolume
(
image
,
volumePath
,
source
)
)
pod
.
Spec
.
Containers
[
0
]
.
Args
=
[]
string
{
fmt
.
Sprintf
(
"--fs_type=%v"
,
volumePath
),
fmt
.
Sprintf
(
"--new_file_0666=%v"
,
filePath
),
fmt
.
Sprintf
(
"--file_perm=%v"
,
filePath
),
}
msg
:=
fmt
.
Sprintf
(
"emptydir 0666 on %v"
,
formatMedium
(
medium
))
out
:=
[]
string
{
"perms of file
\"
/test-volume/test-file
\"
: -rw-rw-rw-"
,
"content of file
\"
/test-volume/test-file
\"
: mount-tester new file"
,
}
if
medium
==
api
.
StorageMediumMemory
{
out
=
append
(
out
,
"mount type of
\"
/test-volume
\"
: tmpfs"
)
}
f
.
TestContainerOutput
(
msg
,
pod
,
0
,
out
)
}
func
doTest0777
(
f
*
Framework
,
image
string
,
medium
api
.
StorageMedium
)
{
var
(
volumePath
=
"/test-volume"
filePath
=
path
.
Join
(
volumePath
,
"test-file"
)
source
=
&
api
.
EmptyDirVolumeSource
{
Medium
:
medium
}
pod
=
testPodWithVolume
(
image
,
volumePath
,
source
)
)
pod
.
Spec
.
Containers
[
0
]
.
Args
=
[]
string
{
fmt
.
Sprintf
(
"--fs_type=%v"
,
volumePath
),
fmt
.
Sprintf
(
"--new_file_0777=%v"
,
filePath
),
fmt
.
Sprintf
(
"--file_perm=%v"
,
filePath
),
}
msg
:=
fmt
.
Sprintf
(
"emptydir 0777 on %v"
,
formatMedium
(
medium
))
out
:=
[]
string
{
"perms of file
\"
/test-volume/test-file
\"
: -rwxrwxrwx"
,
"content of file
\"
/test-volume/test-file
\"
: mount-tester new file"
,
}
if
medium
==
api
.
StorageMediumMemory
{
out
=
append
(
out
,
"mount type of
\"
/test-volume
\"
: tmpfs"
)
}
f
.
TestContainerOutput
(
msg
,
pod
,
0
,
out
)
}
func
formatMedium
(
medium
api
.
StorageMedium
)
string
{
if
medium
==
api
.
StorageMediumMemory
{
return
"tmpfs"
}
return
"node default medium"
}
func
testPodWithVolume
(
path
string
,
source
*
api
.
EmptyDirVolumeSource
)
*
api
.
Pod
{
func
testPodWithVolume
(
image
,
path
string
,
source
*
api
.
EmptyDirVolumeSource
)
*
api
.
Pod
{
podName
:=
"pod-"
+
string
(
util
.
NewUUID
())
podName
:=
"pod-"
+
string
(
util
.
NewUUID
())
return
&
api
.
Pod
{
return
&
api
.
Pod
{
...
@@ -86,7 +217,7 @@ func testPodWithVolume(path string, source *api.EmptyDirVolumeSource) *api.Pod {
...
@@ -86,7 +217,7 @@ func testPodWithVolume(path string, source *api.EmptyDirVolumeSource) *api.Pod {
Containers
:
[]
api
.
Container
{
Containers
:
[]
api
.
Container
{
{
{
Name
:
containerName
,
Name
:
containerName
,
Image
:
"gcr.io/google_containers/mounttest:0.2"
,
Image
:
image
,
VolumeMounts
:
[]
api
.
VolumeMount
{
VolumeMounts
:
[]
api
.
VolumeMount
{
{
{
Name
:
volumeName
,
Name
:
volumeName
,
...
...
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