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
a42ff94c
Commit
a42ff94c
authored
Feb 17, 2015
by
Paul Morie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add secret volume plugin and e2e test
parent
afefa85b
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
539 additions
and
24 deletions
+539
-24
types.go
pkg/api/types.go
+13
-5
types.go
pkg/api/v1beta1/types.go
+9
-1
types.go
pkg/api/v1beta2/types.go
+9
-2
types.go
pkg/api/v1beta3/types.go
+9
-3
validation.go
pkg/api/validation/validation.go
+6
-1
validation_test.go
pkg/api/validation/validation_test.go
+4
-1
plugins.go
pkg/kubelet/server/plugins.go
+2
-0
server.go
pkg/kubelet/server/server.go
+2
-0
empty_dir_test.go
pkg/kubelet/volume/empty_dir/empty_dir_test.go
+4
-4
gce_pd_test.go
pkg/kubelet/volume/gce_pd/gce_pd_test.go
+3
-3
git_repo_test.go
pkg/kubelet/volume/git_repo/git_repo_test.go
+1
-1
host_path_test.go
pkg/kubelet/volume/host_path/host_path_test.go
+2
-2
plugins.go
pkg/kubelet/volume/plugins.go
+4
-0
secret.go
pkg/kubelet/volume/secret/secret.go
+133
-0
secret_test.go
pkg/kubelet/volume/secret/secret_test.go
+160
-0
testing.go
pkg/kubelet/volume/testing.go
+7
-1
volumes.go
pkg/kubelet/volumes.go
+5
-0
secrets.go
test/e2e/secrets.go
+166
-0
No files found.
pkg/api/types.go
View file @
a42ff94c
...
@@ -230,7 +230,10 @@ type GitRepo struct {
...
@@ -230,7 +230,10 @@ type GitRepo struct {
// TODO: Consider credentials here.
// TODO: Consider credentials here.
}
}
// Adapts a Secret into a VolumeSource
// Adapts a Secret into a VolumeSource.
//
// The contents of the target Secret's Data field will be presented in a volume
// as files using the keys in the Data field as the file names.
type
SecretSource
struct
{
type
SecretSource
struct
{
// Reference to a Secret
// Reference to a Secret
Target
ObjectReference
`json:"target"`
Target
ObjectReference
`json:"target"`
...
@@ -1318,15 +1321,22 @@ type ResourceQuotaList struct {
...
@@ -1318,15 +1321,22 @@ type ResourceQuotaList struct {
Items
[]
ResourceQuota
`json:"items"`
Items
[]
ResourceQuota
`json:"items"`
}
}
// Secret holds secret data of a certain type
// Secret holds secret data of a certain type. The total bytes of the values in
// the Data field must be less than MaxSecretSize bytes.
type
Secret
struct
{
type
Secret
struct
{
TypeMeta
`json:",inline"`
TypeMeta
`json:",inline"`
ObjectMeta
`json:"metadata,omitempty"`
ObjectMeta
`json:"metadata,omitempty"`
// Data contains the secret data. Each key must be a valid DNS_SUBDOMAIN.
// The serialized form of the secret data is a base64 encoded string.
Data
map
[
string
][]
byte
`json:"data,omitempty"`
Data
map
[
string
][]
byte
`json:"data,omitempty"`
Type
SecretType
`json:"type,omitempty"`
// Used to facilitate programatic handling of secret data.
Type
SecretType
`json:"type,omitempty"`
}
}
const
MaxSecretSize
=
1
*
1024
*
1024
type
SecretType
string
type
SecretType
string
const
(
const
(
...
@@ -1339,5 +1349,3 @@ type SecretList struct {
...
@@ -1339,5 +1349,3 @@ type SecretList struct {
Items
[]
Secret
`json:"items"`
Items
[]
Secret
`json:"items"`
}
}
const
MaxSecretSize
=
1
*
1024
*
1024
pkg/api/v1beta1/types.go
View file @
a42ff94c
...
@@ -1100,13 +1100,21 @@ type ResourceQuotaList struct {
...
@@ -1100,13 +1100,21 @@ type ResourceQuotaList struct {
Items
[]
ResourceQuota
`json:"items"`
Items
[]
ResourceQuota
`json:"items"`
}
}
// Secret holds secret data of a certain type. The total bytes of the values in
// the Data field must be less than MaxSecretSize bytes.
type
Secret
struct
{
type
Secret
struct
{
TypeMeta
`json:",inline"`
TypeMeta
`json:",inline"`
// Data contains the secret data. Each key must be a valid DNS_SUBDOMAIN.
// The serialized form of the secret data is a base64 encoded string.
Data
map
[
string
][]
byte
`json:"data,omitempty"`
Data
map
[
string
][]
byte
`json:"data,omitempty"`
Type
SecretType
`json:"type,omitempty"`
// Used to facilitate programatic handling of secret data.
Type
SecretType
`json:"type,omitempty"`
}
}
const
MaxSecretSize
=
1
*
1024
*
1024
type
SecretType
string
type
SecretType
string
const
(
const
(
...
...
pkg/api/v1beta2/types.go
View file @
a42ff94c
...
@@ -1103,14 +1103,21 @@ type ResourceQuotaList struct {
...
@@ -1103,14 +1103,21 @@ type ResourceQuotaList struct {
Items
[]
ResourceQuota
`json:"items"`
Items
[]
ResourceQuota
`json:"items"`
}
}
// Secret holds secret data of a certain type
// Secret holds secret data of a certain type. The total bytes of the values in
// the Data field must be less than MaxSecretSize bytes.
type
Secret
struct
{
type
Secret
struct
{
TypeMeta
`json:",inline"`
TypeMeta
`json:",inline"`
// Data contains the secret data. Each key must be a valid DNS_SUBDOMAIN.
Data
map
[
string
][]
byte
`json:"data,omitempty"`
Data
map
[
string
][]
byte
`json:"data,omitempty"`
Type
SecretType
`json:"type,omitempty"`
// Used to facilitate programatic handling of secret data.
// The serialized form of the secret data is a base64 encoded string.
Type
SecretType
`json:"type,omitempty"`
}
}
const
MaxSecretSize
=
1
*
1024
*
1024
type
SecretType
string
type
SecretType
string
const
(
const
(
...
...
pkg/api/v1beta3/types.go
View file @
a42ff94c
...
@@ -1243,16 +1243,22 @@ type ResourceQuotaList struct {
...
@@ -1243,16 +1243,22 @@ type ResourceQuotaList struct {
Items
[]
ResourceQuota
`json:"items"`
Items
[]
ResourceQuota
`json:"items"`
}
}
// Secret holds
mappings between paths and secret data
// Secret holds
secret data of a certain type. The total bytes of the values in
//
TODO: shouldn't "Secret" be a plural?
//
the Data field must be less than MaxSecretSize bytes.
type
Secret
struct
{
type
Secret
struct
{
TypeMeta
`json:",inline"`
TypeMeta
`json:",inline"`
ObjectMeta
`json:"metadata,omitempty"`
ObjectMeta
`json:"metadata,omitempty"`
// Data contains the secret data. Each key must be a valid DNS_SUBDOMAIN.
// The serialized form of the secret data is a base64 encoded string.
Data
map
[
string
][]
byte
`json:"data,omitempty"`
Data
map
[
string
][]
byte
`json:"data,omitempty"`
Type
SecretType
`json:"type,omitempty"`
// Used to facilitate programatic handling of secret data.
Type
SecretType
`json:"type,omitempty"`
}
}
const
MaxSecretSize
=
1
*
1024
*
1024
type
SecretType
string
type
SecretType
string
const
(
const
(
...
...
pkg/api/validation/validation.go
View file @
a42ff94c
...
@@ -853,9 +853,14 @@ func ValidateSecret(secret *api.Secret) errs.ValidationErrorList {
...
@@ -853,9 +853,14 @@ func ValidateSecret(secret *api.Secret) errs.ValidationErrorList {
}
}
totalSize
:=
0
totalSize
:=
0
for
_
,
value
:=
range
secret
.
Data
{
for
key
,
value
:=
range
secret
.
Data
{
if
!
util
.
IsDNSSubdomain
(
key
)
{
allErrs
=
append
(
allErrs
,
errs
.
NewFieldInvalid
(
fmt
.
Sprintf
(
"data[%v]"
,
key
),
key
,
cIdentifierErrorMsg
))
}
totalSize
+=
len
(
value
)
totalSize
+=
len
(
value
)
}
}
if
totalSize
>
api
.
MaxSecretSize
{
if
totalSize
>
api
.
MaxSecretSize
{
allErrs
=
append
(
allErrs
,
errs
.
NewFieldForbidden
(
"data"
,
"Maximum secret size exceeded"
))
allErrs
=
append
(
allErrs
,
errs
.
NewFieldForbidden
(
"data"
,
"Maximum secret size exceeded"
))
}
}
...
...
pkg/api/validation/validation_test.go
View file @
a42ff94c
...
@@ -2497,7 +2497,7 @@ func TestValidateSecret(t *testing.T) {
...
@@ -2497,7 +2497,7 @@ func TestValidateSecret(t *testing.T) {
return
api
.
Secret
{
return
api
.
Secret
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"foo"
,
Namespace
:
"bar"
},
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"foo"
,
Namespace
:
"bar"
},
Data
:
map
[
string
][]
byte
{
Data
:
map
[
string
][]
byte
{
"
foo
"
:
[]
byte
(
"bar"
),
"
data-1
"
:
[]
byte
(
"bar"
),
},
},
}
}
}
}
...
@@ -2508,6 +2508,7 @@ func TestValidateSecret(t *testing.T) {
...
@@ -2508,6 +2508,7 @@ func TestValidateSecret(t *testing.T) {
emptyNs
=
validSecret
()
emptyNs
=
validSecret
()
invalidNs
=
validSecret
()
invalidNs
=
validSecret
()
overMaxSize
=
validSecret
()
overMaxSize
=
validSecret
()
invalidKey
=
validSecret
()
)
)
emptyName
.
Name
=
""
emptyName
.
Name
=
""
...
@@ -2517,6 +2518,7 @@ func TestValidateSecret(t *testing.T) {
...
@@ -2517,6 +2518,7 @@ func TestValidateSecret(t *testing.T) {
overMaxSize
.
Data
=
map
[
string
][]
byte
{
overMaxSize
.
Data
=
map
[
string
][]
byte
{
"over"
:
make
([]
byte
,
api
.
MaxSecretSize
+
1
),
"over"
:
make
([]
byte
,
api
.
MaxSecretSize
+
1
),
}
}
invalidKey
.
Data
[
"a..b"
]
=
[]
byte
(
"whoops"
)
tests
:=
map
[
string
]
struct
{
tests
:=
map
[
string
]
struct
{
secret
api
.
Secret
secret
api
.
Secret
...
@@ -2528,6 +2530,7 @@ func TestValidateSecret(t *testing.T) {
...
@@ -2528,6 +2530,7 @@ func TestValidateSecret(t *testing.T) {
"empty namespace"
:
{
emptyNs
,
false
},
"empty namespace"
:
{
emptyNs
,
false
},
"invalid namespace"
:
{
invalidNs
,
false
},
"invalid namespace"
:
{
invalidNs
,
false
},
"over max size"
:
{
overMaxSize
,
false
},
"over max size"
:
{
overMaxSize
,
false
},
"invalid key"
:
{
invalidKey
,
false
},
}
}
for
name
,
tc
:=
range
tests
{
for
name
,
tc
:=
range
tests
{
...
...
pkg/kubelet/server/plugins.go
View file @
a42ff94c
...
@@ -26,6 +26,7 @@ import (
...
@@ -26,6 +26,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/volume/gce_pd"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/volume/gce_pd"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/volume/git_repo"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/volume/git_repo"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/volume/host_path"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/volume/host_path"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/volume/secret"
)
)
// ProbeVolumePlugins collects all volume plugins into an easy to use list.
// ProbeVolumePlugins collects all volume plugins into an easy to use list.
...
@@ -39,6 +40,7 @@ func ProbeVolumePlugins() []volume.Plugin {
...
@@ -39,6 +40,7 @@ func ProbeVolumePlugins() []volume.Plugin {
allPlugins
=
append
(
allPlugins
,
gce_pd
.
ProbeVolumePlugins
()
...
)
allPlugins
=
append
(
allPlugins
,
gce_pd
.
ProbeVolumePlugins
()
...
)
allPlugins
=
append
(
allPlugins
,
git_repo
.
ProbeVolumePlugins
()
...
)
allPlugins
=
append
(
allPlugins
,
git_repo
.
ProbeVolumePlugins
()
...
)
allPlugins
=
append
(
allPlugins
,
host_path
.
ProbeVolumePlugins
()
...
)
allPlugins
=
append
(
allPlugins
,
host_path
.
ProbeVolumePlugins
()
...
)
allPlugins
=
append
(
allPlugins
,
secret
.
ProbeVolumePlugins
()
...
)
return
allPlugins
return
allPlugins
}
}
pkg/kubelet/server/server.go
View file @
a42ff94c
...
@@ -179,6 +179,8 @@ func (s *KubeletServer) Run(_ []string) error {
...
@@ -179,6 +179,8 @@ func (s *KubeletServer) Run(_ []string) error {
glog
.
Warningf
(
"No API client: %v"
,
err
)
glog
.
Warningf
(
"No API client: %v"
,
err
)
}
}
glog
.
Infof
(
"Using root directory: %v"
,
s
.
RootDirectory
)
credentialprovider
.
SetPreferredDockercfgPath
(
s
.
RootDirectory
)
credentialprovider
.
SetPreferredDockercfgPath
(
s
.
RootDirectory
)
kcfg
:=
KubeletConfig
{
kcfg
:=
KubeletConfig
{
...
...
pkg/kubelet/volume/empty_dir/empty_dir_test.go
View file @
a42ff94c
...
@@ -27,7 +27,7 @@ import (
...
@@ -27,7 +27,7 @@ import (
func
TestCanSupport
(
t
*
testing
.
T
)
{
func
TestCanSupport
(
t
*
testing
.
T
)
{
plugMgr
:=
volume
.
PluginMgr
{}
plugMgr
:=
volume
.
PluginMgr
{}
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
&
volume
.
FakeHost
{
"/tmp/fake"
})
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
&
volume
.
FakeHost
{
"/tmp/fake"
,
nil
})
plug
,
err
:=
plugMgr
.
FindPluginByName
(
"kubernetes.io/empty-dir"
)
plug
,
err
:=
plugMgr
.
FindPluginByName
(
"kubernetes.io/empty-dir"
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -46,7 +46,7 @@ func TestCanSupport(t *testing.T) {
...
@@ -46,7 +46,7 @@ func TestCanSupport(t *testing.T) {
func
TestPlugin
(
t
*
testing
.
T
)
{
func
TestPlugin
(
t
*
testing
.
T
)
{
plugMgr
:=
volume
.
PluginMgr
{}
plugMgr
:=
volume
.
PluginMgr
{}
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
&
volume
.
FakeHost
{
"/tmp/fake"
})
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
&
volume
.
FakeHost
{
"/tmp/fake"
,
nil
})
plug
,
err
:=
plugMgr
.
FindPluginByName
(
"kubernetes.io/empty-dir"
)
plug
,
err
:=
plugMgr
.
FindPluginByName
(
"kubernetes.io/empty-dir"
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -100,7 +100,7 @@ func TestPlugin(t *testing.T) {
...
@@ -100,7 +100,7 @@ func TestPlugin(t *testing.T) {
func
TestPluginBackCompat
(
t
*
testing
.
T
)
{
func
TestPluginBackCompat
(
t
*
testing
.
T
)
{
plugMgr
:=
volume
.
PluginMgr
{}
plugMgr
:=
volume
.
PluginMgr
{}
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
&
volume
.
FakeHost
{
"/tmp/fake"
})
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
&
volume
.
FakeHost
{
"/tmp/fake"
,
nil
})
plug
,
err
:=
plugMgr
.
FindPluginByName
(
"kubernetes.io/empty-dir"
)
plug
,
err
:=
plugMgr
.
FindPluginByName
(
"kubernetes.io/empty-dir"
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -125,7 +125,7 @@ func TestPluginBackCompat(t *testing.T) {
...
@@ -125,7 +125,7 @@ func TestPluginBackCompat(t *testing.T) {
func
TestPluginLegacy
(
t
*
testing
.
T
)
{
func
TestPluginLegacy
(
t
*
testing
.
T
)
{
plugMgr
:=
volume
.
PluginMgr
{}
plugMgr
:=
volume
.
PluginMgr
{}
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
&
volume
.
FakeHost
{
"/tmp/fake"
})
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
&
volume
.
FakeHost
{
"/tmp/fake"
,
nil
})
plug
,
err
:=
plugMgr
.
FindPluginByName
(
"empty"
)
plug
,
err
:=
plugMgr
.
FindPluginByName
(
"empty"
)
if
err
!=
nil
{
if
err
!=
nil
{
...
...
pkg/kubelet/volume/gce_pd/gce_pd_test.go
View file @
a42ff94c
...
@@ -28,7 +28,7 @@ import (
...
@@ -28,7 +28,7 @@ import (
func
TestCanSupport
(
t
*
testing
.
T
)
{
func
TestCanSupport
(
t
*
testing
.
T
)
{
plugMgr
:=
volume
.
PluginMgr
{}
plugMgr
:=
volume
.
PluginMgr
{}
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
&
volume
.
FakeHost
{
"/tmp/fake"
})
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
&
volume
.
FakeHost
{
"/tmp/fake"
,
nil
})
plug
,
err
:=
plugMgr
.
FindPluginByName
(
"kubernetes.io/gce-pd"
)
plug
,
err
:=
plugMgr
.
FindPluginByName
(
"kubernetes.io/gce-pd"
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -80,7 +80,7 @@ func (fake *fakeMounter) List() ([]mount.MountPoint, error) {
...
@@ -80,7 +80,7 @@ func (fake *fakeMounter) List() ([]mount.MountPoint, error) {
func
TestPlugin
(
t
*
testing
.
T
)
{
func
TestPlugin
(
t
*
testing
.
T
)
{
plugMgr
:=
volume
.
PluginMgr
{}
plugMgr
:=
volume
.
PluginMgr
{}
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
&
volume
.
FakeHost
{
"/tmp/fake"
})
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
&
volume
.
FakeHost
{
"/tmp/fake"
,
nil
})
plug
,
err
:=
plugMgr
.
FindPluginByName
(
"kubernetes.io/gce-pd"
)
plug
,
err
:=
plugMgr
.
FindPluginByName
(
"kubernetes.io/gce-pd"
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -146,7 +146,7 @@ func TestPlugin(t *testing.T) {
...
@@ -146,7 +146,7 @@ func TestPlugin(t *testing.T) {
func
TestPluginLegacy
(
t
*
testing
.
T
)
{
func
TestPluginLegacy
(
t
*
testing
.
T
)
{
plugMgr
:=
volume
.
PluginMgr
{}
plugMgr
:=
volume
.
PluginMgr
{}
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
&
volume
.
FakeHost
{
"/tmp/fake"
})
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
&
volume
.
FakeHost
{
"/tmp/fake"
,
nil
})
plug
,
err
:=
plugMgr
.
FindPluginByName
(
"gce-pd"
)
plug
,
err
:=
plugMgr
.
FindPluginByName
(
"gce-pd"
)
if
err
!=
nil
{
if
err
!=
nil
{
...
...
pkg/kubelet/volume/git_repo/git_repo_test.go
View file @
a42ff94c
...
@@ -35,7 +35,7 @@ func newTestHost(t *testing.T) volume.Host {
...
@@ -35,7 +35,7 @@ func newTestHost(t *testing.T) volume.Host {
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"can't make a temp rootdir: %v"
,
err
)
t
.
Fatalf
(
"can't make a temp rootdir: %v"
,
err
)
}
}
return
&
volume
.
FakeHost
{
tempDir
}
return
&
volume
.
FakeHost
{
tempDir
,
nil
}
}
}
func
TestCanSupport
(
t
*
testing
.
T
)
{
func
TestCanSupport
(
t
*
testing
.
T
)
{
...
...
pkg/kubelet/volume/host_path/host_path_test.go
View file @
a42ff94c
...
@@ -26,7 +26,7 @@ import (
...
@@ -26,7 +26,7 @@ import (
func
TestCanSupport
(
t
*
testing
.
T
)
{
func
TestCanSupport
(
t
*
testing
.
T
)
{
plugMgr
:=
volume
.
PluginMgr
{}
plugMgr
:=
volume
.
PluginMgr
{}
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
&
volume
.
FakeHost
{
"fake"
})
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
&
volume
.
FakeHost
{
"fake"
,
nil
})
plug
,
err
:=
plugMgr
.
FindPluginByName
(
"kubernetes.io/host-path"
)
plug
,
err
:=
plugMgr
.
FindPluginByName
(
"kubernetes.io/host-path"
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -45,7 +45,7 @@ func TestCanSupport(t *testing.T) {
...
@@ -45,7 +45,7 @@ func TestCanSupport(t *testing.T) {
func
TestPlugin
(
t
*
testing
.
T
)
{
func
TestPlugin
(
t
*
testing
.
T
)
{
plugMgr
:=
volume
.
PluginMgr
{}
plugMgr
:=
volume
.
PluginMgr
{}
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
&
volume
.
FakeHost
{
"fake"
})
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
&
volume
.
FakeHost
{
"fake"
,
nil
})
plug
,
err
:=
plugMgr
.
FindPluginByName
(
"kubernetes.io/host-path"
)
plug
,
err
:=
plugMgr
.
FindPluginByName
(
"kubernetes.io/host-path"
)
if
err
!=
nil
{
if
err
!=
nil
{
...
...
pkg/kubelet/volume/plugins.go
View file @
a42ff94c
...
@@ -22,6 +22,7 @@ import (
...
@@ -22,6 +22,7 @@ import (
"sync"
"sync"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"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/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
...
@@ -76,6 +77,9 @@ type Host interface {
...
@@ -76,6 +77,9 @@ type Host interface {
// does not exist, the result of this call might not exist. This
// does not exist, the result of this call might not exist. This
// directory might not actually exist on disk yet.
// directory might not actually exist on disk yet.
GetPodPluginDir
(
podUID
types
.
UID
,
pluginName
string
)
string
GetPodPluginDir
(
podUID
types
.
UID
,
pluginName
string
)
string
// GetKubeClient returns a client interface
GetKubeClient
()
client
.
Interface
}
}
// PluginMgr tracks registered plugins.
// PluginMgr tracks registered plugins.
...
...
pkg/kubelet/volume/secret/secret.go
0 → 100644
View file @
a42ff94c
/*
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.
*/
package
secret
import
(
"fmt"
"io/ioutil"
"os"
"path"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/volume"
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
"github.com/golang/glog"
)
// ProbeVolumePlugin is the entry point for plugin detection in a package.
func
ProbeVolumePlugins
()
[]
volume
.
Plugin
{
return
[]
volume
.
Plugin
{
&
secretPlugin
{}}
}
const
(
secretPluginName
=
"kubernetes.io/secret"
)
// secretPlugin implements the VolumePlugin interface.
type
secretPlugin
struct
{
host
volume
.
Host
}
func
(
plugin
*
secretPlugin
)
Init
(
host
volume
.
Host
)
{
plugin
.
host
=
host
}
func
(
plugin
*
secretPlugin
)
Name
()
string
{
return
secretPluginName
}
func
(
plugin
*
secretPlugin
)
CanSupport
(
spec
*
api
.
Volume
)
bool
{
if
spec
.
Source
.
Secret
!=
nil
{
return
true
}
return
false
}
func
(
plugin
*
secretPlugin
)
NewBuilder
(
spec
*
api
.
Volume
,
podUID
types
.
UID
)
(
volume
.
Builder
,
error
)
{
return
plugin
.
newBuilderInternal
(
spec
,
podUID
)
}
func
(
plugin
*
secretPlugin
)
newBuilderInternal
(
spec
*
api
.
Volume
,
podUID
types
.
UID
)
(
volume
.
Builder
,
error
)
{
return
&
secretVolume
{
spec
.
Name
,
podUID
,
plugin
,
&
spec
.
Source
.
Secret
.
Target
},
nil
}
func
(
plugin
*
secretPlugin
)
NewCleaner
(
volName
string
,
podUID
types
.
UID
)
(
volume
.
Cleaner
,
error
)
{
return
plugin
.
newCleanerInternal
(
volName
,
podUID
)
}
func
(
plugin
*
secretPlugin
)
newCleanerInternal
(
volName
string
,
podUID
types
.
UID
)
(
volume
.
Cleaner
,
error
)
{
return
&
secretVolume
{
volName
,
podUID
,
plugin
,
nil
},
nil
}
// secretVolume handles retrieving secrets from the API server
// and placing them into the volume on the host.
type
secretVolume
struct
{
volName
string
podUID
types
.
UID
plugin
*
secretPlugin
secretRef
*
api
.
ObjectReference
}
func
(
sv
*
secretVolume
)
SetUp
()
error
{
// TODO: explore tmpfs for secret volumes
hostPath
:=
sv
.
GetPath
()
glog
.
V
(
3
)
.
Infof
(
"Setting up volume %v for pod %v at %v"
,
sv
.
volName
,
sv
.
podUID
,
hostPath
)
err
:=
os
.
MkdirAll
(
hostPath
,
0777
)
if
err
!=
nil
{
return
err
}
kubeClient
:=
sv
.
plugin
.
host
.
GetKubeClient
()
if
kubeClient
==
nil
{
return
fmt
.
Errorf
(
"Cannot setup secret volume %v because kube client is not configured"
,
sv
)
}
secret
,
err
:=
kubeClient
.
Secrets
(
sv
.
secretRef
.
Namespace
)
.
Get
(
sv
.
secretRef
.
Name
)
if
err
!=
nil
{
glog
.
Errorf
(
"Couldn't get secret %v/%v"
,
sv
.
secretRef
.
Namespace
,
sv
.
secretRef
.
Name
)
return
err
}
for
name
,
data
:=
range
secret
.
Data
{
hostFilePath
:=
path
.
Join
(
hostPath
,
name
)
err
:=
ioutil
.
WriteFile
(
hostFilePath
,
data
,
0777
)
if
err
!=
nil
{
glog
.
Errorf
(
"Error writing secret data to host path: %v, %v"
,
hostFilePath
,
err
)
return
err
}
}
return
nil
}
func
(
sv
*
secretVolume
)
GetPath
()
string
{
return
sv
.
plugin
.
host
.
GetPodVolumeDir
(
sv
.
podUID
,
volume
.
EscapePluginName
(
secretPluginName
),
sv
.
volName
)
}
func
(
sv
*
secretVolume
)
TearDown
()
error
{
glog
.
V
(
3
)
.
Infof
(
"Tearing down volume %v for pod %v at %v"
,
sv
.
volName
,
sv
.
podUID
,
sv
.
GetPath
())
tmpDir
,
err
:=
volume
.
RenameDirectory
(
sv
.
GetPath
(),
sv
.
volName
+
".deleting~"
)
if
err
!=
nil
{
return
err
}
err
=
os
.
RemoveAll
(
tmpDir
)
if
err
!=
nil
{
return
err
}
return
nil
}
pkg/kubelet/volume/secret/secret_test.go
0 → 100644
View file @
a42ff94c
/*
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.
*/
package
secret
import
(
"fmt"
"io/ioutil"
"os"
"path"
"strings"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/volume"
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
)
func
newTestHost
(
t
*
testing
.
T
,
fakeKubeClient
client
.
Interface
)
volume
.
Host
{
tempDir
,
err
:=
ioutil
.
TempDir
(
"/tmp"
,
"secret_volume_test."
)
if
err
!=
nil
{
t
.
Fatalf
(
"can't make a temp rootdir: %v"
,
err
)
}
return
&
volume
.
FakeHost
{
tempDir
,
fakeKubeClient
}
}
func
TestCanSupport
(
t
*
testing
.
T
)
{
pluginMgr
:=
volume
.
PluginMgr
{}
pluginMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
newTestHost
(
t
,
nil
))
plugin
,
err
:=
pluginMgr
.
FindPluginByName
(
secretPluginName
)
if
err
!=
nil
{
t
.
Errorf
(
"Can't find the plugin by name"
)
}
if
plugin
.
Name
()
!=
secretPluginName
{
t
.
Errorf
(
"Wrong name: %s"
,
plugin
.
Name
())
}
if
!
plugin
.
CanSupport
(
&
api
.
Volume
{
Source
:
api
.
VolumeSource
{
Secret
:
&
api
.
SecretSource
{
Target
:
api
.
ObjectReference
{}}}})
{
t
.
Errorf
(
"Expected true"
)
}
}
func
TestPlugin
(
t
*
testing
.
T
)
{
var
(
testPodUID
=
"test_pod_uid"
testVolumeName
=
"test_volume_name"
testNamespace
=
"test_secret_namespace"
testName
=
"test_secret_name"
)
volumeSpec
:=
&
api
.
Volume
{
Name
:
testVolumeName
,
Source
:
api
.
VolumeSource
{
Secret
:
&
api
.
SecretSource
{
Target
:
api
.
ObjectReference
{
Namespace
:
testNamespace
,
Name
:
testName
,
},
},
},
}
secret
:=
api
.
Secret
{
ObjectMeta
:
api
.
ObjectMeta
{
Namespace
:
testNamespace
,
Name
:
testName
,
},
Data
:
map
[
string
][]
byte
{
"data-1"
:
[]
byte
(
"value-1"
),
"data-2"
:
[]
byte
(
"value-2"
),
"data-3"
:
[]
byte
(
"value-3"
),
},
}
client
:=
&
client
.
Fake
{
Secret
:
secret
,
}
pluginMgr
:=
volume
.
PluginMgr
{}
pluginMgr
.
InitPlugins
(
ProbeVolumePlugins
(),
newTestHost
(
t
,
client
))
plugin
,
err
:=
pluginMgr
.
FindPluginByName
(
secretPluginName
)
if
err
!=
nil
{
t
.
Errorf
(
"Can't find the plugin by name"
)
}
builder
,
err
:=
plugin
.
NewBuilder
(
volumeSpec
,
types
.
UID
(
testPodUID
))
if
err
!=
nil
{
t
.
Errorf
(
"Failed to make a new Builder: %v"
,
err
)
}
if
builder
==
nil
{
t
.
Errorf
(
"Got a nil Builder: %v"
)
}
volumePath
:=
builder
.
GetPath
()
if
!
strings
.
HasSuffix
(
volumePath
,
fmt
.
Sprintf
(
"pods/test_pod_uid/volumes/kubernetes.io~secret/test_volume_name"
))
{
t
.
Errorf
(
"Got unexpected path: %s"
,
volumePath
)
}
err
=
builder
.
SetUp
()
if
err
!=
nil
{
t
.
Errorf
(
"Failed to setup volume: %v"
,
err
)
}
if
_
,
err
:=
os
.
Stat
(
volumePath
);
err
!=
nil
{
if
os
.
IsNotExist
(
err
)
{
t
.
Errorf
(
"SetUp() failed, volume path not created: %s"
,
volumePath
)
}
else
{
t
.
Errorf
(
"SetUp() failed: %v"
,
err
)
}
}
for
key
,
value
:=
range
secret
.
Data
{
secretDataHostPath
:=
path
.
Join
(
volumePath
,
key
)
if
_
,
err
:=
os
.
Stat
(
secretDataHostPath
);
err
!=
nil
{
t
.
Fatalf
(
"SetUp() failed, couldn't find secret data on disk: %v"
,
secretDataHostPath
)
}
else
{
actualSecretBytes
,
err
:=
ioutil
.
ReadFile
(
secretDataHostPath
)
if
err
!=
nil
{
t
.
Fatalf
(
"Couldn't read secret data from: %v"
,
secretDataHostPath
)
}
actualSecretValue
:=
string
(
actualSecretBytes
)
if
string
(
value
)
!=
actualSecretValue
{
t
.
Errorf
(
"Unexpected value; expected %q, got %q"
,
value
,
actualSecretValue
)
}
}
}
cleaner
,
err
:=
plugin
.
NewCleaner
(
testVolumeName
,
types
.
UID
(
testPodUID
))
if
err
!=
nil
{
t
.
Errorf
(
"Failed to make a new Cleaner: %v"
,
err
)
}
if
cleaner
==
nil
{
t
.
Errorf
(
"Got a nil Cleaner: %v"
)
}
if
err
:=
cleaner
.
TearDown
();
err
!=
nil
{
t
.
Errorf
(
"Expected success, got: %v"
,
err
)
}
if
_
,
err
:=
os
.
Stat
(
volumePath
);
err
==
nil
{
t
.
Errorf
(
"TearDown() failed, volume path still exists: %s"
,
volumePath
)
}
else
if
!
os
.
IsNotExist
(
err
)
{
t
.
Errorf
(
"SetUp() failed: %v"
,
err
)
}
}
pkg/kubelet/volume/testing.go
View file @
a42ff94c
...
@@ -21,12 +21,14 @@ import (
...
@@ -21,12 +21,14 @@ import (
"path"
"path"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
)
)
// FakeHost is useful for testing volume plugins.
// FakeHost is useful for testing volume plugins.
type
FakeHost
struct
{
type
FakeHost
struct
{
RootDir
string
RootDir
string
KubeClient
client
.
Interface
}
}
func
(
f
*
FakeHost
)
GetPluginDir
(
podUID
string
)
string
{
func
(
f
*
FakeHost
)
GetPluginDir
(
podUID
string
)
string
{
...
@@ -41,6 +43,10 @@ func (f *FakeHost) GetPodPluginDir(podUID types.UID, pluginName string) string {
...
@@ -41,6 +43,10 @@ func (f *FakeHost) GetPodPluginDir(podUID types.UID, pluginName string) string {
return
path
.
Join
(
f
.
RootDir
,
"pods"
,
string
(
podUID
),
"plugins"
,
pluginName
)
return
path
.
Join
(
f
.
RootDir
,
"pods"
,
string
(
podUID
),
"plugins"
,
pluginName
)
}
}
func
(
f
*
FakeHost
)
GetKubeClient
()
client
.
Interface
{
return
f
.
KubeClient
}
// FakePlugin is useful for for testing. It tries to be a fully compliant
// FakePlugin is useful for for testing. It tries to be a fully compliant
// plugin, but all it does is make empty directories.
// plugin, but all it does is make empty directories.
// Use as:
// Use as:
...
...
pkg/kubelet/volumes.go
View file @
a42ff94c
...
@@ -22,6 +22,7 @@ import (
...
@@ -22,6 +22,7 @@ import (
"path"
"path"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/volume"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/volume"
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
"github.com/davecgh/go-spew/spew"
"github.com/davecgh/go-spew/spew"
...
@@ -48,6 +49,10 @@ func (vh *volumeHost) GetPodPluginDir(podUID types.UID, pluginName string) strin
...
@@ -48,6 +49,10 @@ func (vh *volumeHost) GetPodPluginDir(podUID types.UID, pluginName string) strin
return
vh
.
kubelet
.
getPodPluginDir
(
podUID
,
pluginName
)
return
vh
.
kubelet
.
getPodPluginDir
(
podUID
,
pluginName
)
}
}
func
(
vh
*
volumeHost
)
GetKubeClient
()
client
.
Interface
{
return
vh
.
kubelet
.
kubeClient
}
func
(
kl
*
Kubelet
)
newVolumeBuilderFromPlugins
(
spec
*
api
.
Volume
,
podUID
types
.
UID
)
volume
.
Builder
{
func
(
kl
*
Kubelet
)
newVolumeBuilderFromPlugins
(
spec
*
api
.
Volume
,
podUID
types
.
UID
)
volume
.
Builder
{
plugin
,
err
:=
kl
.
volumePluginMgr
.
FindPluginBySpec
(
spec
)
plugin
,
err
:=
kl
.
volumePluginMgr
.
FindPluginBySpec
(
spec
)
if
err
!=
nil
{
if
err
!=
nil
{
...
...
test/e2e/secrets.go
0 → 100644
View file @
a42ff94c
/*
Copyright 2014 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.
*/
package
e2e
import
(
"fmt"
"strings"
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
.
"github.com/onsi/ginkgo"
.
"github.com/onsi/gomega"
)
var
_
=
Describe
(
"Secrets"
,
func
()
{
var
c
*
client
.
Client
BeforeEach
(
func
()
{
var
err
error
c
,
err
=
loadClient
()
Expect
(
err
)
.
NotTo
(
HaveOccurred
())
})
It
(
"should be consumable from pods"
,
func
()
{
ns
:=
api
.
NamespaceDefault
name
:=
"secret-test-"
+
string
(
util
.
NewUUID
())
volumeName
:=
"secret-volume"
volumeMountPath
:=
"/etc/secret-volume"
secret
:=
&
api
.
Secret
{
ObjectMeta
:
api
.
ObjectMeta
{
Namespace
:
ns
,
Name
:
name
,
},
Data
:
map
[
string
][]
byte
{
"data-1"
:
[]
byte
(
"value-1
\n
"
),
"data-2"
:
[]
byte
(
"value-2
\n
"
),
"data-3"
:
[]
byte
(
"value-3
\n
"
),
},
}
secret
,
err
:=
c
.
Secrets
(
ns
)
.
Create
(
secret
)
By
(
fmt
.
Sprintf
(
"Creating secret with name %s"
,
secret
.
Name
))
if
err
!=
nil
{
Fail
(
fmt
.
Sprintf
(
"unable to create test secret %s: %v"
,
secret
.
Name
,
err
))
}
// Clean up secret
defer
func
()
{
By
(
"Cleaning up the secret"
)
if
err
=
c
.
Secrets
(
ns
)
.
Delete
(
secret
.
Name
);
err
!=
nil
{
Fail
(
fmt
.
Sprintf
(
"unable to delete secret %v: %v"
,
secret
.
Name
,
err
))
}
}()
By
(
fmt
.
Sprintf
(
"Creating a pod to consume secret %v"
,
secret
.
Name
))
// Make a client pod that verifies that it has the service environment variables.
clientName
:=
"client-secrets-"
+
string
(
util
.
NewUUID
())
clientPod
:=
&
api
.
Pod
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
clientName
,
},
Spec
:
api
.
PodSpec
{
Volumes
:
[]
api
.
Volume
{
{
Name
:
volumeName
,
Source
:
api
.
VolumeSource
{
Secret
:
&
api
.
SecretSource
{
Target
:
api
.
ObjectReference
{
Kind
:
"Secret"
,
Namespace
:
ns
,
Name
:
name
,
},
},
},
},
},
Containers
:
[]
api
.
Container
{
{
Name
:
"catcont"
,
Image
:
"busybox"
,
Command
:
[]
string
{
"sh"
,
"-c"
,
"cat /etc/secret-volume/data-1; sleep 600"
},
VolumeMounts
:
[]
api
.
VolumeMount
{
{
Name
:
volumeName
,
MountPath
:
volumeMountPath
,
ReadOnly
:
true
,
},
},
},
},
RestartPolicy
:
api
.
RestartPolicy
{
Never
:
&
api
.
RestartPolicyNever
{},
},
},
}
_
,
err
=
c
.
Pods
(
ns
)
.
Create
(
clientPod
)
if
err
!=
nil
{
Fail
(
fmt
.
Sprintf
(
"Failed to create pod: %v"
,
err
))
}
defer
func
()
{
c
.
Pods
(
ns
)
.
Delete
(
clientPod
.
Name
)
}()
// Wait for client pod to complete.
expectNoError
(
waitForPodRunning
(
c
,
clientPod
.
Name
,
60
*
time
.
Second
))
// Grab its logs. Get host first.
clientPodStatus
,
err
:=
c
.
Pods
(
ns
)
.
Get
(
clientPod
.
Name
)
if
err
!=
nil
{
Fail
(
fmt
.
Sprintf
(
"Failed to get clientPod to know host: %v"
,
err
))
}
By
(
fmt
.
Sprintf
(
"Trying to get logs from host %s pod %s container %s: %v"
,
clientPodStatus
.
Status
.
Host
,
clientPodStatus
.
Name
,
clientPodStatus
.
Spec
.
Containers
[
0
]
.
Name
,
err
))
var
logs
[]
byte
start
:=
time
.
Now
()
// Sometimes the actual containers take a second to get started, try to get logs for 60s
for
time
.
Now
()
.
Sub
(
start
)
<
(
60
*
time
.
Second
)
{
logs
,
err
=
c
.
Get
()
.
Prefix
(
"proxy"
)
.
Resource
(
"minions"
)
.
Name
(
clientPodStatus
.
Status
.
Host
)
.
Suffix
(
"containerLogs"
,
ns
,
clientPodStatus
.
Name
,
clientPodStatus
.
Spec
.
Containers
[
0
]
.
Name
)
.
Do
()
.
Raw
()
fmt
.
Sprintf
(
"clientPod logs:%v
\n
"
,
string
(
logs
))
By
(
fmt
.
Sprintf
(
"clientPod logs:%v
\n
"
,
string
(
logs
)))
if
strings
.
Contains
(
string
(
logs
),
"Internal Error"
)
{
By
(
fmt
.
Sprintf
(
"Failed to get logs from host %s pod %s container %s: %v"
,
clientPodStatus
.
Status
.
Host
,
clientPodStatus
.
Name
,
clientPodStatus
.
Spec
.
Containers
[
0
]
.
Name
,
string
(
logs
)))
time
.
Sleep
(
5
*
time
.
Second
)
continue
}
break
}
toFind
:=
[]
string
{
"value-1"
,
}
for
_
,
m
:=
range
toFind
{
Expect
(
string
(
logs
))
.
To
(
ContainSubstring
(
m
),
"%q in secret data"
,
m
)
}
// We could try a wget the service from the client pod. But services.sh e2e test covers that pretty well.
})
})
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