rkt: Support alternate stage1's via annotation

This provides a basic implementation for setting a stage1 on a per-pod
basis via an annotation.

It's possible this feature should be gated behind additional knobs, such
as a kubelet flag to filter allowed stage1s, or a check akin to what
priviliged gets in the apiserver.
Currently, it checks `AllowPrivileged`, as a means to let people disable
this feature, though overloading it as stage1 and privileged isn't
ideal.
This commit is contained in:
Euan Kemp
2016-04-26 15:20:06 -07:00
committed by Euan Kemp
parent dc236d926d
commit 136da158c5
3 changed files with 197 additions and 20 deletions

View File

@@ -36,8 +36,10 @@ import (
kubetesting "k8s.io/kubernetes/pkg/kubelet/container/testing"
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
"k8s.io/kubernetes/pkg/kubelet/rkt/mock_os"
"k8s.io/kubernetes/pkg/kubelet/rkt/mock_rkt"
"k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util/errors"
utilexec "k8s.io/kubernetes/pkg/util/exec"
utiltesting "k8s.io/kubernetes/pkg/util/testing"
)
@@ -1194,6 +1196,11 @@ func TestGenerateRunCommand(t *testing.T) {
for i, tt := range tests {
testCaseHint := fmt.Sprintf("test case #%d", i)
rkt.runtimeHelper = &fakeRuntimeHelper{tt.dnsServers, tt.dnsSearches, tt.hostName, "", tt.err}
rkt.execer = &utilexec.FakeExec{CommandScript: []utilexec.FakeCommandAction{func(cmd string, args ...string) utilexec.Cmd {
return utilexec.InitFakeCmd(&utilexec.FakeCmd{}, cmd, args...)
}}}
// a command should be created of this form, but the returned command shouldn't be called (asserted by having no expectations on it)
result, err := rkt.generateRunCommand(tt.pod, tt.uuid, tt.netnsName)
assert.Equal(t, tt.err, err, testCaseHint)
@@ -1617,3 +1624,137 @@ func TestGarbageCollect(t *testing.T) {
getter.pods = make(map[types.UID]*api.Pod)
}
}
type annotationsByName []appctypes.Annotation
func (a annotationsByName) Len() int { return len(a) }
func (a annotationsByName) Less(x, y int) bool { return a[x].Name < a[y].Name }
func (a annotationsByName) Swap(x, y int) { a[x], a[y] = a[y], a[x] }
func TestMakePodManifestAnnotations(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockVolumeGetter := mock_rkt.NewMockVolumeGetter(ctrl)
fr := newFakeRktInterface()
fs := newFakeSystemd()
r := &Runtime{apisvc: fr, systemd: fs, volumeGetter: mockVolumeGetter}
testCases := []struct {
in *api.Pod
out *appcschema.PodManifest
outerr error
}{
{
in: &api.Pod{
ObjectMeta: api.ObjectMeta{
UID: "uid-1",
Name: "name-1",
Namespace: "namespace-1",
Annotations: map[string]string{
k8sRktStage1NameAnno: "stage1-override-img",
},
},
},
out: &appcschema.PodManifest{
Annotations: []appctypes.Annotation{
{
Name: appctypes.ACIdentifier(k8sRktStage1NameAnno),
Value: "stage1-override-img",
},
{
Name: appctypes.ACIdentifier(k8sRktUIDAnno),
Value: "uid-1",
},
{
Name: appctypes.ACIdentifier(k8sRktNameAnno),
Value: "name-1",
},
{
Name: appctypes.ACIdentifier(k8sRktKubeletAnno),
Value: "true",
},
{
Name: appctypes.ACIdentifier(k8sRktNamespaceAnno),
Value: "namespace-1",
},
{
Name: appctypes.ACIdentifier(k8sRktRestartCountAnno),
Value: "0",
},
},
},
},
}
for i, testCase := range testCases {
hint := fmt.Sprintf("case #%d", i)
mockVolumeGetter.EXPECT().GetVolumes(gomock.Any()).Return(kubecontainer.VolumeMap{}, true)
result, err := r.makePodManifest(testCase.in, []api.Secret{})
assert.Equal(t, err, testCase.outerr, hint)
if err == nil {
sort.Sort(annotationsByName(result.Annotations))
sort.Sort(annotationsByName(testCase.out.Annotations))
assert.Equal(t, result.Annotations, testCase.out.Annotations, hint)
}
}
}
func TestPreparePodArgs(t *testing.T) {
r := &Runtime{
config: &Config{},
}
testCases := []struct {
manifest appcschema.PodManifest
stage1Config string
cmd []string
}{
{
appcschema.PodManifest{
Annotations: appctypes.Annotations{
{
Name: k8sRktStage1NameAnno,
Value: "stage1-image",
},
},
},
"",
[]string{"prepare", "--quiet", "--pod-manifest", "file", "--stage1-name=stage1-image"},
},
{
appcschema.PodManifest{
Annotations: appctypes.Annotations{
{
Name: k8sRktStage1NameAnno,
Value: "stage1-image",
},
},
},
"stage1-path",
[]string{"prepare", "--quiet", "--pod-manifest", "file", "--stage1-name=stage1-image"},
},
{
appcschema.PodManifest{
Annotations: appctypes.Annotations{},
},
"stage1-path",
[]string{"prepare", "--quiet", "--pod-manifest", "file", "--stage1-path=stage1-path"},
},
{
appcschema.PodManifest{
Annotations: appctypes.Annotations{},
},
"",
[]string{"prepare", "--quiet", "--pod-manifest", "file"},
},
}
for i, testCase := range testCases {
r.config.Stage1Image = testCase.stage1Config
cmd := r.preparePodArgs(&testCase.manifest, "file")
assert.Equal(t, testCase.cmd, cmd, fmt.Sprintf("Test case #%d", i))
}
}