123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938 |
- /*
- Copyright 2015 The Kubernetes Authors.
- 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 rkt
- import (
- "encoding/json"
- "fmt"
- "net"
- "os"
- "path/filepath"
- "sort"
- "testing"
- "time"
- appcschema "github.com/appc/spec/schema"
- appctypes "github.com/appc/spec/schema/types"
- rktapi "github.com/coreos/rkt/api/v1alpha"
- "github.com/golang/mock/gomock"
- "github.com/stretchr/testify/assert"
- "k8s.io/kubernetes/pkg/api"
- "k8s.io/kubernetes/pkg/api/resource"
- kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
- containertesting "k8s.io/kubernetes/pkg/kubelet/container/testing"
- kubetesting "k8s.io/kubernetes/pkg/kubelet/container/testing"
- "k8s.io/kubernetes/pkg/kubelet/lifecycle"
- "k8s.io/kubernetes/pkg/kubelet/network"
- "k8s.io/kubernetes/pkg/kubelet/network/kubenet"
- "k8s.io/kubernetes/pkg/kubelet/network/mock_network"
- "k8s.io/kubernetes/pkg/kubelet/rkt/mock_os"
- "k8s.io/kubernetes/pkg/kubelet/types"
- kubetypes "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"
- )
- func mustMarshalPodManifest(man *appcschema.PodManifest) []byte {
- manblob, err := json.Marshal(man)
- if err != nil {
- panic(err)
- }
- return manblob
- }
- func mustMarshalImageManifest(man *appcschema.ImageManifest) []byte {
- manblob, err := json.Marshal(man)
- if err != nil {
- panic(err)
- }
- return manblob
- }
- func mustRktHash(hash string) *appctypes.Hash {
- h, err := appctypes.NewHash(hash)
- if err != nil {
- panic(err)
- }
- return h
- }
- func makeRktPod(rktPodState rktapi.PodState,
- rktPodID, podUID, podName, podNamespace string, podCreatedAt, podStartedAt int64,
- podRestartCount string, appNames, imgIDs, imgNames,
- containerHashes []string, appStates []rktapi.AppState,
- exitcodes []int32, ips map[string]string) *rktapi.Pod {
- podManifest := &appcschema.PodManifest{
- ACKind: appcschema.PodManifestKind,
- ACVersion: appcschema.AppContainerVersion,
- Annotations: appctypes.Annotations{
- appctypes.Annotation{
- Name: *appctypes.MustACIdentifier(k8sRktKubeletAnno),
- Value: k8sRktKubeletAnnoValue,
- },
- appctypes.Annotation{
- Name: *appctypes.MustACIdentifier(types.KubernetesPodUIDLabel),
- Value: podUID,
- },
- appctypes.Annotation{
- Name: *appctypes.MustACIdentifier(types.KubernetesPodNameLabel),
- Value: podName,
- },
- appctypes.Annotation{
- Name: *appctypes.MustACIdentifier(types.KubernetesPodNamespaceLabel),
- Value: podNamespace,
- },
- appctypes.Annotation{
- Name: *appctypes.MustACIdentifier(k8sRktRestartCountAnno),
- Value: podRestartCount,
- },
- },
- }
- appNum := len(appNames)
- if appNum != len(imgNames) ||
- appNum != len(imgIDs) ||
- appNum != len(containerHashes) ||
- appNum != len(appStates) {
- panic("inconsistent app number")
- }
- apps := make([]*rktapi.App, appNum)
- for i := range appNames {
- apps[i] = &rktapi.App{
- Name: appNames[i],
- State: appStates[i],
- Image: &rktapi.Image{
- Id: imgIDs[i],
- Name: imgNames[i],
- Version: "latest",
- Manifest: mustMarshalImageManifest(
- &appcschema.ImageManifest{
- ACKind: appcschema.ImageManifestKind,
- ACVersion: appcschema.AppContainerVersion,
- Name: *appctypes.MustACIdentifier(imgNames[i]),
- Annotations: appctypes.Annotations{
- appctypes.Annotation{
- Name: *appctypes.MustACIdentifier(k8sRktContainerHashAnno),
- Value: containerHashes[i],
- },
- },
- },
- ),
- },
- ExitCode: exitcodes[i],
- }
- podManifest.Apps = append(podManifest.Apps, appcschema.RuntimeApp{
- Name: *appctypes.MustACName(appNames[i]),
- Image: appcschema.RuntimeImage{ID: *mustRktHash("sha512-foo")},
- Annotations: appctypes.Annotations{
- appctypes.Annotation{
- Name: *appctypes.MustACIdentifier(k8sRktContainerHashAnno),
- Value: containerHashes[i],
- },
- },
- })
- }
- var networks []*rktapi.Network
- for name, ip := range ips {
- networks = append(networks, &rktapi.Network{Name: name, Ipv4: ip})
- }
- return &rktapi.Pod{
- Id: rktPodID,
- State: rktPodState,
- Apps: apps,
- Manifest: mustMarshalPodManifest(podManifest),
- StartedAt: podStartedAt,
- CreatedAt: podCreatedAt,
- Networks: networks,
- }
- }
- func TestCheckVersion(t *testing.T) {
- fr := newFakeRktInterface()
- fs := newFakeSystemd()
- r := &Runtime{apisvc: fr, systemd: fs}
- fr.info = rktapi.Info{
- RktVersion: "1.2.3+git",
- AppcVersion: "1.2.4+git",
- ApiVersion: "1.2.6-alpha",
- }
- fs.version = "100"
- tests := []struct {
- minimumRktBinVersion string
- recommendedRktBinVersion string
- minimumRktApiVersion string
- minimumSystemdVersion string
- err error
- calledGetInfo bool
- calledSystemVersion bool
- }{
- // Good versions.
- {
- "1.2.3",
- "1.2.3",
- "1.2.5",
- "99",
- nil,
- true,
- true,
- },
- // Good versions.
- {
- "1.2.3+git",
- "1.2.3+git",
- "1.2.6-alpha",
- "100",
- nil,
- true,
- true,
- },
- // Requires greater binary version.
- {
- "1.2.4",
- "1.2.4",
- "1.2.6-alpha",
- "100",
- fmt.Errorf("rkt: binary version is too old(%v), requires at least %v", fr.info.RktVersion, "1.2.4"),
- true,
- true,
- },
- // Requires greater API version.
- {
- "1.2.3",
- "1.2.3",
- "1.2.6",
- "100",
- fmt.Errorf("rkt: API version is too old(%v), requires at least %v", fr.info.ApiVersion, "1.2.6"),
- true,
- true,
- },
- // Requires greater API version.
- {
- "1.2.3",
- "1.2.3",
- "1.2.7",
- "100",
- fmt.Errorf("rkt: API version is too old(%v), requires at least %v", fr.info.ApiVersion, "1.2.7"),
- true,
- true,
- },
- // Requires greater systemd version.
- {
- "1.2.3",
- "1.2.3",
- "1.2.7",
- "101",
- fmt.Errorf("rkt: systemd version(%v) is too old, requires at least %v", fs.version, "101"),
- false,
- true,
- },
- }
- for i, tt := range tests {
- testCaseHint := fmt.Sprintf("test case #%d", i)
- err := r.checkVersion(tt.minimumRktBinVersion, tt.recommendedRktBinVersion, tt.minimumRktApiVersion, tt.minimumSystemdVersion)
- assert.Equal(t, tt.err, err, testCaseHint)
- if tt.calledGetInfo {
- assert.Equal(t, fr.called, []string{"GetInfo"}, testCaseHint)
- }
- if tt.calledSystemVersion {
- assert.Equal(t, fs.called, []string{"Version"}, testCaseHint)
- }
- if err == nil {
- assert.Equal(t, fr.info.RktVersion, r.versions.binVersion.String(), testCaseHint)
- assert.Equal(t, fr.info.ApiVersion, r.versions.apiVersion.String(), testCaseHint)
- }
- fr.CleanCalls()
- fs.CleanCalls()
- }
- }
- func TestListImages(t *testing.T) {
- fr := newFakeRktInterface()
- fs := newFakeSystemd()
- r := &Runtime{apisvc: fr, systemd: fs}
- tests := []struct {
- images []*rktapi.Image
- expected []kubecontainer.Image
- }{
- {nil, []kubecontainer.Image{}},
- {
- []*rktapi.Image{
- {
- Id: "sha512-a2fb8f390702",
- Name: "quay.io/coreos/alpine-sh",
- Version: "latest",
- },
- },
- []kubecontainer.Image{
- {
- ID: "sha512-a2fb8f390702",
- RepoTags: []string{"quay.io/coreos/alpine-sh:latest"},
- },
- },
- },
- {
- []*rktapi.Image{
- {
- Id: "sha512-a2fb8f390702",
- Name: "quay.io/coreos/alpine-sh",
- Version: "latest",
- Size: 400,
- },
- {
- Id: "sha512-c6b597f42816",
- Name: "coreos.com/rkt/stage1-coreos",
- Version: "0.10.0",
- Size: 400,
- },
- },
- []kubecontainer.Image{
- {
- ID: "sha512-a2fb8f390702",
- RepoTags: []string{"quay.io/coreos/alpine-sh:latest"},
- Size: 400,
- },
- {
- ID: "sha512-c6b597f42816",
- RepoTags: []string{"coreos.com/rkt/stage1-coreos:0.10.0"},
- Size: 400,
- },
- },
- },
- }
- for i, tt := range tests {
- fr.images = tt.images
- images, err := r.ListImages()
- if err != nil {
- t.Errorf("%v", err)
- }
- assert.Equal(t, tt.expected, images)
- assert.Equal(t, fr.called, []string{"ListImages"}, fmt.Sprintf("test case %d: unexpected called list", i))
- fr.CleanCalls()
- }
- }
- func TestGetPods(t *testing.T) {
- fr := newFakeRktInterface()
- fs := newFakeSystemd()
- r := &Runtime{apisvc: fr, systemd: fs}
- ns := func(seconds int64) int64 {
- return seconds * 1e9
- }
- tests := []struct {
- pods []*rktapi.Pod
- result []*kubecontainer.Pod
- }{
- // No pods.
- {},
- // One pod.
- {
- []*rktapi.Pod{
- makeRktPod(rktapi.PodState_POD_STATE_RUNNING,
- "uuid-4002", "42", "guestbook", "default",
- ns(10), ns(10), "7",
- []string{"app-1", "app-2"},
- []string{"img-id-1", "img-id-2"},
- []string{"img-name-1", "img-name-2"},
- []string{"1001", "1002"},
- []rktapi.AppState{rktapi.AppState_APP_STATE_RUNNING, rktapi.AppState_APP_STATE_EXITED},
- []int32{0, 0},
- nil,
- ),
- },
- []*kubecontainer.Pod{
- {
- ID: "42",
- Name: "guestbook",
- Namespace: "default",
- Containers: []*kubecontainer.Container{
- {
- ID: kubecontainer.BuildContainerID("rkt", "uuid-4002:app-1"),
- Name: "app-1",
- Image: "img-name-1:latest",
- ImageID: "img-id-1",
- Hash: 1001,
- State: "running",
- },
- {
- ID: kubecontainer.BuildContainerID("rkt", "uuid-4002:app-2"),
- Name: "app-2",
- Image: "img-name-2:latest",
- ImageID: "img-id-2",
- Hash: 1002,
- State: "exited",
- },
- },
- },
- },
- },
- // Multiple pods.
- {
- []*rktapi.Pod{
- makeRktPod(rktapi.PodState_POD_STATE_RUNNING,
- "uuid-4002", "42", "guestbook", "default",
- ns(10), ns(20), "7",
- []string{"app-1", "app-2"},
- []string{"img-id-1", "img-id-2"},
- []string{"img-name-1", "img-name-2"},
- []string{"1001", "1002"},
- []rktapi.AppState{rktapi.AppState_APP_STATE_RUNNING, rktapi.AppState_APP_STATE_EXITED},
- []int32{0, 0},
- nil,
- ),
- makeRktPod(rktapi.PodState_POD_STATE_EXITED,
- "uuid-4003", "43", "guestbook", "default",
- ns(30), ns(40), "7",
- []string{"app-11", "app-22"},
- []string{"img-id-11", "img-id-22"},
- []string{"img-name-11", "img-name-22"},
- []string{"10011", "10022"},
- []rktapi.AppState{rktapi.AppState_APP_STATE_EXITED, rktapi.AppState_APP_STATE_EXITED},
- []int32{0, 0},
- nil,
- ),
- makeRktPod(rktapi.PodState_POD_STATE_EXITED,
- "uuid-4004", "43", "guestbook", "default",
- ns(50), ns(60), "8",
- []string{"app-11", "app-22"},
- []string{"img-id-11", "img-id-22"},
- []string{"img-name-11", "img-name-22"},
- []string{"10011", "10022"},
- []rktapi.AppState{rktapi.AppState_APP_STATE_RUNNING, rktapi.AppState_APP_STATE_RUNNING},
- []int32{0, 0},
- nil,
- ),
- },
- []*kubecontainer.Pod{
- {
- ID: "42",
- Name: "guestbook",
- Namespace: "default",
- Containers: []*kubecontainer.Container{
- {
- ID: kubecontainer.BuildContainerID("rkt", "uuid-4002:app-1"),
- Name: "app-1",
- Image: "img-name-1:latest",
- ImageID: "img-id-1",
- Hash: 1001,
- State: "running",
- },
- {
- ID: kubecontainer.BuildContainerID("rkt", "uuid-4002:app-2"),
- Name: "app-2",
- Image: "img-name-2:latest",
- ImageID: "img-id-2",
- Hash: 1002,
- State: "exited",
- },
- },
- },
- {
- ID: "43",
- Name: "guestbook",
- Namespace: "default",
- Containers: []*kubecontainer.Container{
- {
- ID: kubecontainer.BuildContainerID("rkt", "uuid-4003:app-11"),
- Name: "app-11",
- Image: "img-name-11:latest",
- ImageID: "img-id-11",
- Hash: 10011,
- State: "exited",
- },
- {
- ID: kubecontainer.BuildContainerID("rkt", "uuid-4003:app-22"),
- Name: "app-22",
- Image: "img-name-22:latest",
- ImageID: "img-id-22",
- Hash: 10022,
- State: "exited",
- },
- {
- ID: kubecontainer.BuildContainerID("rkt", "uuid-4004:app-11"),
- Name: "app-11",
- Image: "img-name-11:latest",
- ImageID: "img-id-11",
- Hash: 10011,
- State: "running",
- },
- {
- ID: kubecontainer.BuildContainerID("rkt", "uuid-4004:app-22"),
- Name: "app-22",
- Image: "img-name-22:latest",
- ImageID: "img-id-22",
- Hash: 10022,
- State: "running",
- },
- },
- },
- },
- },
- }
- for i, tt := range tests {
- testCaseHint := fmt.Sprintf("test case #%d", i)
- fr.pods = tt.pods
- pods, err := r.GetPods(true)
- if err != nil {
- t.Errorf("test case #%d: unexpected error: %v", i, err)
- }
- assert.Equal(t, tt.result, pods, testCaseHint)
- assert.Equal(t, []string{"ListPods"}, fr.called, fmt.Sprintf("test case %d: unexpected called list", i))
- fr.CleanCalls()
- }
- }
- func TestGetPodsFilters(t *testing.T) {
- fr := newFakeRktInterface()
- fs := newFakeSystemd()
- r := &Runtime{apisvc: fr, systemd: fs}
- for _, test := range []struct {
- All bool
- ExpectedFilters []*rktapi.PodFilter
- }{
- {
- true,
- []*rktapi.PodFilter{
- {
- Annotations: []*rktapi.KeyValue{
- {
- Key: k8sRktKubeletAnno,
- Value: k8sRktKubeletAnnoValue,
- },
- },
- },
- },
- },
- {
- false,
- []*rktapi.PodFilter{
- {
- States: []rktapi.PodState{rktapi.PodState_POD_STATE_RUNNING},
- Annotations: []*rktapi.KeyValue{
- {
- Key: k8sRktKubeletAnno,
- Value: k8sRktKubeletAnnoValue,
- },
- },
- },
- },
- },
- } {
- _, err := r.GetPods(test.All)
- if err != nil {
- t.Errorf("%v", err)
- }
- assert.Equal(t, test.ExpectedFilters, fr.podFilters, "filters didn't match when all=%b", test.All)
- }
- }
- func TestGetPodStatus(t *testing.T) {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- fr := newFakeRktInterface()
- fs := newFakeSystemd()
- fnp := mock_network.NewMockNetworkPlugin(ctrl)
- fos := &containertesting.FakeOS{}
- frh := &fakeRuntimeHelper{}
- r := &Runtime{
- apisvc: fr,
- systemd: fs,
- runtimeHelper: frh,
- os: fos,
- networkPlugin: fnp,
- }
- ns := func(seconds int64) int64 {
- return seconds * 1e9
- }
- tests := []struct {
- networkPluginName string
- pods []*rktapi.Pod
- result *kubecontainer.PodStatus
- }{
- // # case 0, No pods.
- {
- kubenet.KubenetPluginName,
- nil,
- &kubecontainer.PodStatus{ID: "42", Name: "guestbook", Namespace: "default"},
- },
- // # case 1, One pod.
- {
- kubenet.KubenetPluginName,
- []*rktapi.Pod{
- makeRktPod(rktapi.PodState_POD_STATE_RUNNING,
- "uuid-4002", "42", "guestbook", "default",
- ns(10), ns(20), "7",
- []string{"app-1", "app-2"},
- []string{"img-id-1", "img-id-2"},
- []string{"img-name-1", "img-name-2"},
- []string{"1001", "1002"},
- []rktapi.AppState{rktapi.AppState_APP_STATE_RUNNING, rktapi.AppState_APP_STATE_EXITED},
- []int32{0, 0},
- nil,
- ),
- },
- &kubecontainer.PodStatus{
- ID: "42",
- Name: "guestbook",
- Namespace: "default",
- IP: "10.10.10.42",
- ContainerStatuses: []*kubecontainer.ContainerStatus{
- {
- ID: kubecontainer.BuildContainerID("rkt", "uuid-4002:app-1"),
- Name: "app-1",
- State: kubecontainer.ContainerStateRunning,
- CreatedAt: time.Unix(10, 0),
- StartedAt: time.Unix(20, 0),
- FinishedAt: time.Unix(0, 30),
- Image: "img-name-1:latest",
- ImageID: "rkt://img-id-1",
- Hash: 1001,
- RestartCount: 7,
- },
- {
- ID: kubecontainer.BuildContainerID("rkt", "uuid-4002:app-2"),
- Name: "app-2",
- State: kubecontainer.ContainerStateExited,
- CreatedAt: time.Unix(10, 0),
- StartedAt: time.Unix(20, 0),
- FinishedAt: time.Unix(0, 30),
- Image: "img-name-2:latest",
- ImageID: "rkt://img-id-2",
- Hash: 1002,
- RestartCount: 7,
- Reason: "Completed",
- },
- },
- },
- },
- // # case 2, One pod with no-op network plugin name.
- {
- network.DefaultPluginName,
- []*rktapi.Pod{
- makeRktPod(rktapi.PodState_POD_STATE_RUNNING,
- "uuid-4002", "42", "guestbook", "default",
- ns(10), ns(20), "7",
- []string{"app-1", "app-2"},
- []string{"img-id-1", "img-id-2"},
- []string{"img-name-1", "img-name-2"},
- []string{"1001", "1002"},
- []rktapi.AppState{rktapi.AppState_APP_STATE_RUNNING, rktapi.AppState_APP_STATE_EXITED},
- []int32{0, 0},
- map[string]string{defaultNetworkName: "10.10.10.22"},
- ),
- },
- &kubecontainer.PodStatus{
- ID: "42",
- Name: "guestbook",
- Namespace: "default",
- IP: "10.10.10.22",
- ContainerStatuses: []*kubecontainer.ContainerStatus{
- {
- ID: kubecontainer.BuildContainerID("rkt", "uuid-4002:app-1"),
- Name: "app-1",
- State: kubecontainer.ContainerStateRunning,
- CreatedAt: time.Unix(10, 0),
- StartedAt: time.Unix(20, 0),
- FinishedAt: time.Unix(0, 30),
- Image: "img-name-1:latest",
- ImageID: "rkt://img-id-1",
- Hash: 1001,
- RestartCount: 7,
- },
- {
- ID: kubecontainer.BuildContainerID("rkt", "uuid-4002:app-2"),
- Name: "app-2",
- State: kubecontainer.ContainerStateExited,
- CreatedAt: time.Unix(10, 0),
- StartedAt: time.Unix(20, 0),
- FinishedAt: time.Unix(0, 30),
- Image: "img-name-2:latest",
- ImageID: "rkt://img-id-2",
- Hash: 1002,
- RestartCount: 7,
- Reason: "Completed",
- },
- },
- },
- },
- // # case 3, Multiple pods.
- {
- kubenet.KubenetPluginName,
- []*rktapi.Pod{
- makeRktPod(rktapi.PodState_POD_STATE_EXITED,
- "uuid-4002", "42", "guestbook", "default",
- ns(10), ns(20), "7",
- []string{"app-1", "app-2"},
- []string{"img-id-1", "img-id-2"},
- []string{"img-name-1", "img-name-2"},
- []string{"1001", "1002"},
- []rktapi.AppState{rktapi.AppState_APP_STATE_RUNNING, rktapi.AppState_APP_STATE_EXITED},
- []int32{0, 0},
- nil,
- ),
- makeRktPod(rktapi.PodState_POD_STATE_RUNNING, // The latest pod is running.
- "uuid-4003", "42", "guestbook", "default",
- ns(10), ns(20), "10",
- []string{"app-1", "app-2"},
- []string{"img-id-1", "img-id-2"},
- []string{"img-name-1", "img-name-2"},
- []string{"1001", "1002"},
- []rktapi.AppState{rktapi.AppState_APP_STATE_RUNNING, rktapi.AppState_APP_STATE_EXITED},
- []int32{0, 1},
- nil,
- ),
- },
- &kubecontainer.PodStatus{
- ID: "42",
- Name: "guestbook",
- Namespace: "default",
- IP: "10.10.10.42",
- // Result should contain all containers.
- ContainerStatuses: []*kubecontainer.ContainerStatus{
- {
- ID: kubecontainer.BuildContainerID("rkt", "uuid-4002:app-1"),
- Name: "app-1",
- State: kubecontainer.ContainerStateRunning,
- CreatedAt: time.Unix(10, 0),
- StartedAt: time.Unix(20, 0),
- FinishedAt: time.Unix(0, 30),
- Image: "img-name-1:latest",
- ImageID: "rkt://img-id-1",
- Hash: 1001,
- RestartCount: 7,
- },
- {
- ID: kubecontainer.BuildContainerID("rkt", "uuid-4002:app-2"),
- Name: "app-2",
- State: kubecontainer.ContainerStateExited,
- CreatedAt: time.Unix(10, 0),
- StartedAt: time.Unix(20, 0),
- FinishedAt: time.Unix(0, 30),
- Image: "img-name-2:latest",
- ImageID: "rkt://img-id-2",
- Hash: 1002,
- RestartCount: 7,
- Reason: "Completed",
- },
- {
- ID: kubecontainer.BuildContainerID("rkt", "uuid-4003:app-1"),
- Name: "app-1",
- State: kubecontainer.ContainerStateRunning,
- CreatedAt: time.Unix(10, 0),
- StartedAt: time.Unix(20, 0),
- FinishedAt: time.Unix(0, 30),
- Image: "img-name-1:latest",
- ImageID: "rkt://img-id-1",
- Hash: 1001,
- RestartCount: 10,
- },
- {
- ID: kubecontainer.BuildContainerID("rkt", "uuid-4003:app-2"),
- Name: "app-2",
- State: kubecontainer.ContainerStateExited,
- CreatedAt: time.Unix(10, 0),
- StartedAt: time.Unix(20, 0),
- FinishedAt: time.Unix(0, 30),
- Image: "img-name-2:latest",
- ImageID: "rkt://img-id-2",
- Hash: 1002,
- RestartCount: 10,
- ExitCode: 1,
- Reason: "Error",
- },
- },
- },
- },
- }
- for i, tt := range tests {
- testCaseHint := fmt.Sprintf("test case #%d", i)
- fr.pods = tt.pods
- podTimes := map[string]time.Time{}
- for _, pod := range tt.pods {
- podTimes[podFinishedMarkerPath(r.runtimeHelper.GetPodDir(tt.result.ID), pod.Id)] = tt.result.ContainerStatuses[0].FinishedAt
- }
- r.os.(*containertesting.FakeOS).StatFn = func(name string) (os.FileInfo, error) {
- podTime, ok := podTimes[name]
- if !ok {
- t.Errorf("osStat called with %v, but only knew about %#v", name, podTimes)
- }
- mockFI := mock_os.NewMockFileInfo(ctrl)
- mockFI.EXPECT().ModTime().Return(podTime)
- return mockFI, nil
- }
- fnp.EXPECT().Name().Return(tt.networkPluginName)
- if tt.networkPluginName == kubenet.KubenetPluginName {
- if tt.result.IP != "" {
- fnp.EXPECT().GetPodNetworkStatus("default", "guestbook", kubecontainer.ContainerID{ID: "42"}).
- Return(&network.PodNetworkStatus{IP: net.ParseIP(tt.result.IP)}, nil)
- } else {
- fnp.EXPECT().GetPodNetworkStatus("default", "guestbook", kubecontainer.ContainerID{ID: "42"}).
- Return(nil, fmt.Errorf("no such network"))
- }
- }
- status, err := r.GetPodStatus("42", "guestbook", "default")
- if err != nil {
- t.Errorf("test case #%d: unexpected error: %v", i, err)
- }
- assert.Equal(t, tt.result, status, testCaseHint)
- assert.Equal(t, []string{"ListPods"}, fr.called, testCaseHint)
- fr.CleanCalls()
- }
- }
- func generateCapRetainIsolator(t *testing.T, caps ...string) appctypes.Isolator {
- retain, err := appctypes.NewLinuxCapabilitiesRetainSet(caps...)
- if err != nil {
- t.Fatalf("Error generating cap retain isolator: %v", err)
- }
- return retain.AsIsolator()
- }
- func generateCapRevokeIsolator(t *testing.T, caps ...string) appctypes.Isolator {
- revoke, err := appctypes.NewLinuxCapabilitiesRevokeSet(caps...)
- if err != nil {
- t.Fatalf("Error generating cap revoke isolator: %v", err)
- }
- return revoke.AsIsolator()
- }
- func generateCPUIsolator(t *testing.T, request, limit string) appctypes.Isolator {
- cpu, err := appctypes.NewResourceCPUIsolator(request, limit)
- if err != nil {
- t.Fatalf("Error generating cpu resource isolator: %v", err)
- }
- return cpu.AsIsolator()
- }
- func generateMemoryIsolator(t *testing.T, request, limit string) appctypes.Isolator {
- memory, err := appctypes.NewResourceMemoryIsolator(request, limit)
- if err != nil {
- t.Fatalf("Error generating memory resource isolator: %v", err)
- }
- return memory.AsIsolator()
- }
- func baseApp(t *testing.T) *appctypes.App {
- return &appctypes.App{
- User: "0",
- Group: "0",
- Exec: appctypes.Exec{"/bin/foo", "bar"},
- SupplementaryGIDs: []int{4, 5, 6},
- WorkingDirectory: "/foo",
- Environment: []appctypes.EnvironmentVariable{
- {Name: "env-foo", Value: "bar"},
- },
- MountPoints: []appctypes.MountPoint{
- {Name: *appctypes.MustACName("mnt-foo"), Path: "/mnt-foo", ReadOnly: false},
- },
- Ports: []appctypes.Port{
- {Name: *appctypes.MustACName("port-foo"), Protocol: "TCP", Port: 4242},
- },
- Isolators: []appctypes.Isolator{
- generateCapRetainIsolator(t, "CAP_SYS_ADMIN"),
- generateCapRevokeIsolator(t, "CAP_NET_ADMIN"),
- generateCPUIsolator(t, "100m", "200m"),
- generateMemoryIsolator(t, "10M", "20M"),
- },
- }
- }
- func baseImageManifest(t *testing.T) *appcschema.ImageManifest {
- img := &appcschema.ImageManifest{App: baseApp(t)}
- entrypoint, err := json.Marshal([]string{"/bin/foo"})
- if err != nil {
- t.Fatal(err)
- }
- cmd, err := json.Marshal([]string{"bar"})
- if err != nil {
- t.Fatal(err)
- }
- img.Annotations.Set(*appctypes.MustACIdentifier(appcDockerEntrypoint), string(entrypoint))
- img.Annotations.Set(*appctypes.MustACIdentifier(appcDockerCmd), string(cmd))
- return img
- }
- func baseAppWithRootUserGroup(t *testing.T) *appctypes.App {
- app := baseApp(t)
- app.User, app.Group = "0", "0"
- return app
- }
- type envByName []appctypes.EnvironmentVariable
- func (s envByName) Len() int { return len(s) }
- func (s envByName) Less(i, j int) bool { return s[i].Name < s[j].Name }
- func (s envByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
- type mountsByName []appctypes.MountPoint
- func (s mountsByName) Len() int { return len(s) }
- func (s mountsByName) Less(i, j int) bool { return s[i].Name < s[j].Name }
- func (s mountsByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
- type portsByName []appctypes.Port
- func (s portsByName) Len() int { return len(s) }
- func (s portsByName) Less(i, j int) bool { return s[i].Name < s[j].Name }
- func (s portsByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
- type isolatorsByName []appctypes.Isolator
- func (s isolatorsByName) Len() int { return len(s) }
- func (s isolatorsByName) Less(i, j int) bool { return s[i].Name < s[j].Name }
- func (s isolatorsByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
- func sortAppFields(app *appctypes.App) {
- sort.Sort(envByName(app.Environment))
- sort.Sort(mountsByName(app.MountPoints))
- sort.Sort(portsByName(app.Ports))
- sort.Sort(isolatorsByName(app.Isolators))
- }
- type sortedStringList []string
- func (s sortedStringList) Len() int { return len(s) }
- func (s sortedStringList) Less(i, j int) bool { return s[i] < s[j] }
- func (s sortedStringList) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
- func TestSetApp(t *testing.T) {
- tmpDir, err := utiltesting.MkTmpdir("rkt_test")
- if err != nil {
- t.Fatalf("error creating temp dir: %v", err)
- }
- defer os.RemoveAll(tmpDir)
- rootUser := int64(0)
- nonRootUser := int64(42)
- runAsNonRootTrue := true
- fsgid := int64(3)
- tests := []struct {
- container *api.Container
- mountPoints []appctypes.MountPoint
- containerPorts []appctypes.Port
- envs []kubecontainer.EnvVar
- ctx *api.SecurityContext
- podCtx *api.PodSecurityContext
- supplementalGids []int64
- expect *appctypes.App
- err error
- }{
- // Nothing should change, but the "User" and "Group" should be filled.
- {
- container: &api.Container{},
- mountPoints: []appctypes.MountPoint{},
- containerPorts: []appctypes.Port{},
- envs: []kubecontainer.EnvVar{},
- ctx: nil,
- podCtx: nil,
- supplementalGids: nil,
- expect: baseAppWithRootUserGroup(t),
- err: nil,
- },
- // error verifying non-root.
- {
- container: &api.Container{},
- mountPoints: []appctypes.MountPoint{},
- containerPorts: []appctypes.Port{},
- envs: []kubecontainer.EnvVar{},
- ctx: &api.SecurityContext{
- RunAsNonRoot: &runAsNonRootTrue,
- RunAsUser: &rootUser,
- },
- podCtx: nil,
- supplementalGids: nil,
- expect: nil,
- err: fmt.Errorf("container has no runAsUser and image will run as root"),
- },
- // app's args should be changed.
- {
- container: &api.Container{
- Args: []string{"foo"},
- },
- mountPoints: []appctypes.MountPoint{},
- containerPorts: []appctypes.Port{},
- envs: []kubecontainer.EnvVar{},
- ctx: nil,
- podCtx: nil,
- supplementalGids: nil,
- expect: &appctypes.App{
- Exec: appctypes.Exec{"/bin/foo", "foo"},
- User: "0",
- Group: "0",
- SupplementaryGIDs: []int{4, 5, 6},
- WorkingDirectory: "/foo",
- Environment: []appctypes.EnvironmentVariable{
- {Name: "env-foo", Value: "bar"},
- },
- MountPoints: []appctypes.MountPoint{
- {Name: *appctypes.MustACName("mnt-foo"), Path: "/mnt-foo", ReadOnly: false},
- },
- Ports: []appctypes.Port{
- {Name: *appctypes.MustACName("port-foo"), Protocol: "TCP", Port: 4242},
- },
- Isolators: []appctypes.Isolator{
- generateCapRetainIsolator(t, "CAP_SYS_ADMIN"),
- generateCapRevokeIsolator(t, "CAP_NET_ADMIN"),
- generateCPUIsolator(t, "100m", "200m"),
- generateMemoryIsolator(t, "10M", "20M"),
- },
- },
- err: nil,
- },
- // app should be changed.
- {
- container: &api.Container{
- Command: []string{"/bin/bar", "$(env-bar)"},
- WorkingDir: tmpDir,
- Resources: api.ResourceRequirements{
- Limits: api.ResourceList{"cpu": resource.MustParse("50m"), "memory": resource.MustParse("50M")},
- Requests: api.ResourceList{"cpu": resource.MustParse("5m"), "memory": resource.MustParse("5M")},
- },
- },
- mountPoints: []appctypes.MountPoint{
- {Name: *appctypes.MustACName("mnt-bar"), Path: "/mnt-bar", ReadOnly: true},
- },
- containerPorts: []appctypes.Port{
- {Name: *appctypes.MustACName("port-bar"), Protocol: "TCP", Port: 1234},
- },
- envs: []kubecontainer.EnvVar{
- {Name: "env-bar", Value: "foo"},
- },
- ctx: &api.SecurityContext{
- Capabilities: &api.Capabilities{
- Add: []api.Capability{"CAP_SYS_CHROOT", "CAP_SYS_BOOT"},
- Drop: []api.Capability{"CAP_SETUID", "CAP_SETGID"},
- },
- RunAsUser: &nonRootUser,
- RunAsNonRoot: &runAsNonRootTrue,
- },
- podCtx: &api.PodSecurityContext{
- SupplementalGroups: []int64{1, 2},
- FSGroup: &fsgid,
- },
- supplementalGids: []int64{4},
- expect: &appctypes.App{
- Exec: appctypes.Exec{"/bin/bar", "foo"},
- User: "42",
- Group: "0",
- SupplementaryGIDs: []int{1, 2, 3, 4},
- WorkingDirectory: tmpDir,
- Environment: []appctypes.EnvironmentVariable{
- {Name: "env-foo", Value: "bar"},
- {Name: "env-bar", Value: "foo"},
- },
- MountPoints: []appctypes.MountPoint{
- {Name: *appctypes.MustACName("mnt-foo"), Path: "/mnt-foo", ReadOnly: false},
- {Name: *appctypes.MustACName("mnt-bar"), Path: "/mnt-bar", ReadOnly: true},
- },
- Ports: []appctypes.Port{
- {Name: *appctypes.MustACName("port-foo"), Protocol: "TCP", Port: 4242},
- {Name: *appctypes.MustACName("port-bar"), Protocol: "TCP", Port: 1234},
- },
- Isolators: []appctypes.Isolator{
- generateCapRetainIsolator(t, "CAP_SYS_CHROOT", "CAP_SYS_BOOT"),
- generateCapRevokeIsolator(t, "CAP_SETUID", "CAP_SETGID"),
- generateCPUIsolator(t, "5m", "50m"),
- generateMemoryIsolator(t, "5M", "50M"),
- },
- },
- },
- // app should be changed. (env, mounts, ports, are overrided).
- {
- container: &api.Container{
- Name: "hello-world",
- Command: []string{"/bin/hello", "$(env-foo)"},
- Args: []string{"hello", "world", "$(env-bar)"},
- WorkingDir: tmpDir,
- Resources: api.ResourceRequirements{
- Limits: api.ResourceList{"cpu": resource.MustParse("50m")},
- Requests: api.ResourceList{"memory": resource.MustParse("5M")},
- },
- },
- mountPoints: []appctypes.MountPoint{
- {Name: *appctypes.MustACName("mnt-foo"), Path: "/mnt-foo", ReadOnly: true},
- },
- containerPorts: []appctypes.Port{
- {Name: *appctypes.MustACName("port-foo"), Protocol: "TCP", Port: 1234},
- },
- envs: []kubecontainer.EnvVar{
- {Name: "env-foo", Value: "foo"},
- {Name: "env-bar", Value: "bar"},
- },
- ctx: &api.SecurityContext{
- Capabilities: &api.Capabilities{
- Add: []api.Capability{"CAP_SYS_CHROOT", "CAP_SYS_BOOT"},
- Drop: []api.Capability{"CAP_SETUID", "CAP_SETGID"},
- },
- RunAsUser: &nonRootUser,
- RunAsNonRoot: &runAsNonRootTrue,
- },
- podCtx: &api.PodSecurityContext{
- SupplementalGroups: []int64{1, 2},
- FSGroup: &fsgid,
- },
- supplementalGids: []int64{4},
- expect: &appctypes.App{
- Exec: appctypes.Exec{"/bin/hello", "foo", "hello", "world", "bar"},
- User: "42",
- Group: "0",
- SupplementaryGIDs: []int{1, 2, 3, 4},
- WorkingDirectory: tmpDir,
- Environment: []appctypes.EnvironmentVariable{
- {Name: "env-foo", Value: "foo"},
- {Name: "env-bar", Value: "bar"},
- },
- MountPoints: []appctypes.MountPoint{
- {Name: *appctypes.MustACName("mnt-foo"), Path: "/mnt-foo", ReadOnly: true},
- },
- Ports: []appctypes.Port{
- {Name: *appctypes.MustACName("port-foo"), Protocol: "TCP", Port: 1234},
- },
- Isolators: []appctypes.Isolator{
- generateCapRetainIsolator(t, "CAP_SYS_CHROOT", "CAP_SYS_BOOT"),
- generateCapRevokeIsolator(t, "CAP_SETUID", "CAP_SETGID"),
- generateCPUIsolator(t, "50m", "50m"),
- generateMemoryIsolator(t, "5M", "5M"),
- },
- },
- },
- }
- for i, tt := range tests {
- testCaseHint := fmt.Sprintf("test case #%d", i)
- img := baseImageManifest(t)
- err := setApp(img, tt.container,
- tt.mountPoints, tt.containerPorts, tt.envs,
- tt.ctx, tt.podCtx, tt.supplementalGids)
- if err == nil && tt.err != nil || err != nil && tt.err == nil {
- t.Errorf("%s: expect %v, saw %v", testCaseHint, tt.err, err)
- }
- if err == nil {
- sortAppFields(tt.expect)
- sortAppFields(img.App)
- assert.Equal(t, tt.expect, img.App, testCaseHint)
- }
- }
- }
- func TestGenerateRunCommand(t *testing.T) {
- hostName := "test-hostname"
- boolTrue := true
- boolFalse := false
- tests := []struct {
- networkPlugin network.NetworkPlugin
- pod *api.Pod
- uuid string
- netnsName string
- dnsServers []string
- dnsSearches []string
- hostName string
- err error
- expect string
- }{
- // Case #0, returns error.
- {
- kubenet.NewPlugin("/tmp"),
- &api.Pod{
- ObjectMeta: api.ObjectMeta{
- Name: "pod-name-foo",
- },
- Spec: api.PodSpec{
- Containers: []api.Container{{Name: "container-foo"}},
- },
- },
- "rkt-uuid-foo",
- "default",
- []string{},
- []string{},
- "",
- fmt.Errorf("failed to get cluster dns"),
- "",
- },
- // Case #1, returns no dns, with private-net.
- {
- kubenet.NewPlugin("/tmp"),
- &api.Pod{
- ObjectMeta: api.ObjectMeta{
- Name: "pod-name-foo",
- },
- Spec: api.PodSpec{
- Containers: []api.Container{{Name: "container-foo"}},
- },
- },
- "rkt-uuid-foo",
- "default",
- []string{},
- []string{},
- "pod-hostname-foo",
- nil,
- "/usr/bin/nsenter --net=/var/run/netns/default -- /bin/rkt/rkt --insecure-options=image,ondisk --local-config=/var/rkt/local/data --dir=/var/data run-prepared --net=host --hostname=pod-hostname-foo rkt-uuid-foo",
- },
- // Case #2, returns no dns, with host-net.
- {
- kubenet.NewPlugin("/tmp"),
- &api.Pod{
- ObjectMeta: api.ObjectMeta{
- Name: "pod-name-foo",
- },
- Spec: api.PodSpec{
- SecurityContext: &api.PodSecurityContext{
- HostNetwork: true,
- },
- Containers: []api.Container{{Name: "container-foo"}},
- },
- },
- "rkt-uuid-foo",
- "",
- []string{},
- []string{},
- "",
- nil,
- fmt.Sprintf("/bin/rkt/rkt --insecure-options=image,ondisk --local-config=/var/rkt/local/data --dir=/var/data run-prepared --net=host --hostname=%s rkt-uuid-foo", hostName),
- },
- // Case #3, returns dns, dns searches, with private-net.
- {
- kubenet.NewPlugin("/tmp"),
- &api.Pod{
- ObjectMeta: api.ObjectMeta{
- Name: "pod-name-foo",
- },
- Spec: api.PodSpec{
- SecurityContext: &api.PodSecurityContext{
- HostNetwork: false,
- },
- Containers: []api.Container{{Name: "container-foo"}},
- },
- },
- "rkt-uuid-foo",
- "default",
- []string{"127.0.0.1"},
- []string{"."},
- "pod-hostname-foo",
- nil,
- "/usr/bin/nsenter --net=/var/run/netns/default -- /bin/rkt/rkt --insecure-options=image,ondisk --local-config=/var/rkt/local/data --dir=/var/data run-prepared --net=host --dns=127.0.0.1 --dns-search=. --dns-opt=ndots:5 --hostname=pod-hostname-foo rkt-uuid-foo",
- },
- // Case #4, returns no dns, dns searches, with host-network.
- {
- kubenet.NewPlugin("/tmp"),
- &api.Pod{
- ObjectMeta: api.ObjectMeta{
- Name: "pod-name-foo",
- },
- Spec: api.PodSpec{
- SecurityContext: &api.PodSecurityContext{
- HostNetwork: true,
- },
- Containers: []api.Container{{Name: "container-foo"}},
- },
- },
- "rkt-uuid-foo",
- "",
- []string{"127.0.0.1"},
- []string{"."},
- "pod-hostname-foo",
- nil,
- fmt.Sprintf("/bin/rkt/rkt --insecure-options=image,ondisk --local-config=/var/rkt/local/data --dir=/var/data run-prepared --net=host --hostname=%s rkt-uuid-foo", hostName),
- },
- // Case #5, with no-op plugin, returns --net=rkt.kubernetes.io, with dns and dns search.
- {
- &network.NoopNetworkPlugin{},
- &api.Pod{
- ObjectMeta: api.ObjectMeta{
- Name: "pod-name-foo",
- },
- Spec: api.PodSpec{
- Containers: []api.Container{{Name: "container-foo"}},
- },
- },
- "rkt-uuid-foo",
- "default",
- []string{"127.0.0.1"},
- []string{"."},
- "pod-hostname-foo",
- nil,
- "/bin/rkt/rkt --insecure-options=image,ondisk --local-config=/var/rkt/local/data --dir=/var/data run-prepared --net=rkt.kubernetes.io --dns=127.0.0.1 --dns-search=. --dns-opt=ndots:5 --hostname=pod-hostname-foo rkt-uuid-foo",
- },
- // Case #6, if all containers are privileged, the result should have 'insecure-options=all-run'
- {
- kubenet.NewPlugin("/tmp"),
- &api.Pod{
- ObjectMeta: api.ObjectMeta{
- Name: "pod-name-foo",
- },
- Spec: api.PodSpec{
- Containers: []api.Container{
- {Name: "container-foo", SecurityContext: &api.SecurityContext{Privileged: &boolTrue}},
- {Name: "container-bar", SecurityContext: &api.SecurityContext{Privileged: &boolTrue}},
- },
- },
- },
- "rkt-uuid-foo",
- "default",
- []string{},
- []string{},
- "pod-hostname-foo",
- nil,
- "/usr/bin/nsenter --net=/var/run/netns/default -- /bin/rkt/rkt --insecure-options=image,ondisk,all-run --local-config=/var/rkt/local/data --dir=/var/data run-prepared --net=host --hostname=pod-hostname-foo rkt-uuid-foo",
- },
- // Case #7, if not all containers are privileged, the result should not have 'insecure-options=all-run'
- {
- kubenet.NewPlugin("/tmp"),
- &api.Pod{
- ObjectMeta: api.ObjectMeta{
- Name: "pod-name-foo",
- },
- Spec: api.PodSpec{
- Containers: []api.Container{
- {Name: "container-foo", SecurityContext: &api.SecurityContext{Privileged: &boolTrue}},
- {Name: "container-bar", SecurityContext: &api.SecurityContext{Privileged: &boolFalse}},
- },
- },
- },
- "rkt-uuid-foo",
- "default",
- []string{},
- []string{},
- "pod-hostname-foo",
- nil,
- "/usr/bin/nsenter --net=/var/run/netns/default -- /bin/rkt/rkt --insecure-options=image,ondisk --local-config=/var/rkt/local/data --dir=/var/data run-prepared --net=host --hostname=pod-hostname-foo rkt-uuid-foo",
- },
- }
- rkt := &Runtime{
- nsenterPath: "/usr/bin/nsenter",
- os: &kubetesting.FakeOS{HostName: hostName},
- config: &Config{
- Path: "/bin/rkt/rkt",
- Stage1Image: "/bin/rkt/stage1-coreos.aci",
- Dir: "/var/data",
- InsecureOptions: "image,ondisk",
- LocalConfigDir: "/var/rkt/local/data",
- },
- }
- for i, tt := range tests {
- testCaseHint := fmt.Sprintf("test case #%d", i)
- rkt.networkPlugin = tt.networkPlugin
- 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)
- assert.Equal(t, tt.expect, result, testCaseHint)
- }
- }
- func TestLifeCycleHooks(t *testing.T) {
- runner := lifecycle.NewFakeHandlerRunner()
- fr := newFakeRktInterface()
- fs := newFakeSystemd()
- rkt := &Runtime{
- runner: runner,
- apisvc: fr,
- systemd: fs,
- containerRefManager: kubecontainer.NewRefManager(),
- }
- tests := []struct {
- pod *api.Pod
- runtimePod *kubecontainer.Pod
- postStartRuns []string
- preStopRuns []string
- err error
- }{
- {
- // Case 0, container without any hooks.
- &api.Pod{
- ObjectMeta: api.ObjectMeta{
- Name: "pod-1",
- Namespace: "ns-1",
- UID: "uid-1",
- },
- Spec: api.PodSpec{
- Containers: []api.Container{
- {Name: "container-name-1"},
- },
- },
- },
- &kubecontainer.Pod{
- Containers: []*kubecontainer.Container{
- {ID: kubecontainer.BuildContainerID("rkt", "id-1")},
- },
- },
- []string{},
- []string{},
- nil,
- },
- {
- // Case 1, containers with post-start and pre-stop hooks.
- &api.Pod{
- ObjectMeta: api.ObjectMeta{
- Name: "pod-1",
- Namespace: "ns-1",
- UID: "uid-1",
- },
- Spec: api.PodSpec{
- Containers: []api.Container{
- {
- Name: "container-name-1",
- Lifecycle: &api.Lifecycle{
- PostStart: &api.Handler{
- Exec: &api.ExecAction{},
- },
- },
- },
- {
- Name: "container-name-2",
- Lifecycle: &api.Lifecycle{
- PostStart: &api.Handler{
- HTTPGet: &api.HTTPGetAction{},
- },
- },
- },
- {
- Name: "container-name-3",
- Lifecycle: &api.Lifecycle{
- PreStop: &api.Handler{
- Exec: &api.ExecAction{},
- },
- },
- },
- {
- Name: "container-name-4",
- Lifecycle: &api.Lifecycle{
- PreStop: &api.Handler{
- HTTPGet: &api.HTTPGetAction{},
- },
- },
- },
- },
- },
- },
- &kubecontainer.Pod{
- Containers: []*kubecontainer.Container{
- {
- ID: kubecontainer.ParseContainerID("rkt://uuid:container-name-4"),
- Name: "container-name-4",
- },
- {
- ID: kubecontainer.ParseContainerID("rkt://uuid:container-name-3"),
- Name: "container-name-3",
- },
- {
- ID: kubecontainer.ParseContainerID("rkt://uuid:container-name-2"),
- Name: "container-name-2",
- },
- {
- ID: kubecontainer.ParseContainerID("rkt://uuid:container-name-1"),
- Name: "container-name-1",
- },
- },
- },
- []string{
- "exec on pod: pod-1_ns-1(uid-1), container: container-name-1: rkt://uuid:container-name-1",
- "http-get on pod: pod-1_ns-1(uid-1), container: container-name-2: rkt://uuid:container-name-2",
- },
- []string{
- "exec on pod: pod-1_ns-1(uid-1), container: container-name-3: rkt://uuid:container-name-3",
- "http-get on pod: pod-1_ns-1(uid-1), container: container-name-4: rkt://uuid:container-name-4",
- },
- nil,
- },
- {
- // Case 2, one container with invalid hooks.
- &api.Pod{
- ObjectMeta: api.ObjectMeta{
- Name: "pod-1",
- Namespace: "ns-1",
- UID: "uid-1",
- },
- Spec: api.PodSpec{
- Containers: []api.Container{
- {
- Name: "container-name-1",
- Lifecycle: &api.Lifecycle{
- PostStart: &api.Handler{},
- PreStop: &api.Handler{},
- },
- },
- },
- },
- },
- &kubecontainer.Pod{
- Containers: []*kubecontainer.Container{
- {
- ID: kubecontainer.ParseContainerID("rkt://uuid:container-name-1"),
- Name: "container-name-1",
- },
- },
- },
- []string{},
- []string{},
- errors.NewAggregate([]error{fmt.Errorf("Invalid handler: %v", &api.Handler{})}),
- },
- }
- for i, tt := range tests {
- testCaseHint := fmt.Sprintf("test case #%d", i)
- pod := &rktapi.Pod{Id: "uuid"}
- for _, c := range tt.runtimePod.Containers {
- pod.Apps = append(pod.Apps, &rktapi.App{
- Name: c.Name,
- State: rktapi.AppState_APP_STATE_RUNNING,
- })
- }
- fr.pods = []*rktapi.Pod{pod}
- // Run post-start hooks
- err := rkt.runLifecycleHooks(tt.pod, tt.runtimePod, lifecyclePostStartHook)
- assert.Equal(t, tt.err, err, testCaseHint)
- sort.Sort(sortedStringList(tt.postStartRuns))
- sort.Sort(sortedStringList(runner.HandlerRuns))
- assert.Equal(t, tt.postStartRuns, runner.HandlerRuns, testCaseHint)
- runner.Reset()
- // Run pre-stop hooks.
- err = rkt.runLifecycleHooks(tt.pod, tt.runtimePod, lifecyclePreStopHook)
- assert.Equal(t, tt.err, err, testCaseHint)
- sort.Sort(sortedStringList(tt.preStopRuns))
- sort.Sort(sortedStringList(runner.HandlerRuns))
- assert.Equal(t, tt.preStopRuns, runner.HandlerRuns, testCaseHint)
- runner.Reset()
- }
- }
- func TestImageStats(t *testing.T) {
- fr := newFakeRktInterface()
- rkt := &Runtime{apisvc: fr}
- fr.images = []*rktapi.Image{
- {Size: 100},
- {Size: 200},
- {Size: 300},
- }
- result, err := rkt.ImageStats()
- assert.NoError(t, err)
- assert.Equal(t, result, &kubecontainer.ImageStats{TotalStorageBytes: 600})
- }
- func TestGarbageCollect(t *testing.T) {
- fr := newFakeRktInterface()
- fs := newFakeSystemd()
- cli := newFakeRktCli()
- fakeOS := kubetesting.NewFakeOS()
- getter := newFakePodGetter()
- rkt := &Runtime{
- os: fakeOS,
- cli: cli,
- apisvc: fr,
- podGetter: getter,
- systemd: fs,
- containerRefManager: kubecontainer.NewRefManager(),
- }
- fakeApp := &rktapi.App{Name: "app-foo"}
- tests := []struct {
- gcPolicy kubecontainer.ContainerGCPolicy
- apiPods []*api.Pod
- pods []*rktapi.Pod
- serviceFilesOnDisk []string
- expectedCommands []string
- expectedServiceFiles []string
- }{
- // All running pods, should not be gc'd.
- // Dead, new pods should not be gc'd.
- // Dead, old pods should be gc'd.
- // Deleted pods should be gc'd.
- // Service files without corresponded pods should be removed.
- {
- kubecontainer.ContainerGCPolicy{
- MinAge: 0,
- MaxContainers: 0,
- },
- []*api.Pod{
- {ObjectMeta: api.ObjectMeta{UID: "pod-uid-1"}},
- {ObjectMeta: api.ObjectMeta{UID: "pod-uid-2"}},
- {ObjectMeta: api.ObjectMeta{UID: "pod-uid-3"}},
- {ObjectMeta: api.ObjectMeta{UID: "pod-uid-4"}},
- },
- []*rktapi.Pod{
- {
- Id: "deleted-foo",
- State: rktapi.PodState_POD_STATE_EXITED,
- CreatedAt: time.Now().Add(time.Hour).UnixNano(),
- StartedAt: time.Now().Add(time.Hour).UnixNano(),
- Apps: []*rktapi.App{fakeApp},
- Annotations: []*rktapi.KeyValue{
- {
- Key: types.KubernetesPodUIDLabel,
- Value: "pod-uid-0",
- },
- },
- },
- {
- Id: "running-foo",
- State: rktapi.PodState_POD_STATE_RUNNING,
- CreatedAt: 0,
- StartedAt: 0,
- Apps: []*rktapi.App{fakeApp},
- Annotations: []*rktapi.KeyValue{
- {
- Key: types.KubernetesPodUIDLabel,
- Value: "pod-uid-1",
- },
- },
- },
- {
- Id: "running-bar",
- State: rktapi.PodState_POD_STATE_RUNNING,
- CreatedAt: 0,
- StartedAt: 0,
- Apps: []*rktapi.App{fakeApp},
- Annotations: []*rktapi.KeyValue{
- {
- Key: types.KubernetesPodUIDLabel,
- Value: "pod-uid-2",
- },
- },
- },
- {
- Id: "dead-old",
- State: rktapi.PodState_POD_STATE_EXITED,
- CreatedAt: 0,
- StartedAt: 0,
- Apps: []*rktapi.App{fakeApp},
- Annotations: []*rktapi.KeyValue{
- {
- Key: types.KubernetesPodUIDLabel,
- Value: "pod-uid-3",
- },
- },
- },
- {
- Id: "dead-new",
- State: rktapi.PodState_POD_STATE_EXITED,
- CreatedAt: time.Now().Add(time.Hour).UnixNano(),
- StartedAt: time.Now().Add(time.Hour).UnixNano(),
- Apps: []*rktapi.App{fakeApp},
- Annotations: []*rktapi.KeyValue{
- {
- Key: types.KubernetesPodUIDLabel,
- Value: "pod-uid-4",
- },
- },
- },
- },
- []string{"k8s_dead-old.service", "k8s_deleted-foo.service", "k8s_non-existing-bar.service"},
- []string{"rkt rm dead-old", "rkt rm deleted-foo"},
- []string{"/run/systemd/system/k8s_dead-old.service", "/run/systemd/system/k8s_deleted-foo.service", "/run/systemd/system/k8s_non-existing-bar.service"},
- },
- // gcPolicy.MaxContainers should be enforced.
- // Oldest ones are removed first.
- {
- kubecontainer.ContainerGCPolicy{
- MinAge: 0,
- MaxContainers: 1,
- },
- []*api.Pod{
- {ObjectMeta: api.ObjectMeta{UID: "pod-uid-0"}},
- {ObjectMeta: api.ObjectMeta{UID: "pod-uid-1"}},
- {ObjectMeta: api.ObjectMeta{UID: "pod-uid-2"}},
- },
- []*rktapi.Pod{
- {
- Id: "dead-2",
- State: rktapi.PodState_POD_STATE_EXITED,
- CreatedAt: 2,
- StartedAt: 2,
- Apps: []*rktapi.App{fakeApp},
- Annotations: []*rktapi.KeyValue{
- {
- Key: types.KubernetesPodUIDLabel,
- Value: "pod-uid-2",
- },
- },
- },
- {
- Id: "dead-1",
- State: rktapi.PodState_POD_STATE_EXITED,
- CreatedAt: 1,
- StartedAt: 1,
- Apps: []*rktapi.App{fakeApp},
- Annotations: []*rktapi.KeyValue{
- {
- Key: types.KubernetesPodUIDLabel,
- Value: "pod-uid-1",
- },
- },
- },
- {
- Id: "dead-0",
- State: rktapi.PodState_POD_STATE_EXITED,
- CreatedAt: 0,
- StartedAt: 0,
- Apps: []*rktapi.App{fakeApp},
- Annotations: []*rktapi.KeyValue{
- {
- Key: types.KubernetesPodUIDLabel,
- Value: "pod-uid-0",
- },
- },
- },
- },
- []string{"k8s_dead-0.service", "k8s_dead-1.service", "k8s_dead-2.service"},
- []string{"rkt rm dead-0", "rkt rm dead-1"},
- []string{"/run/systemd/system/k8s_dead-0.service", "/run/systemd/system/k8s_dead-1.service"},
- },
- }
- for i, tt := range tests {
- testCaseHint := fmt.Sprintf("test case #%d", i)
- ctrl := gomock.NewController(t)
- fakeOS.ReadDirFn = func(dirname string) ([]os.FileInfo, error) {
- serviceFileNames := tt.serviceFilesOnDisk
- var fileInfos []os.FileInfo
- for _, name := range serviceFileNames {
- mockFI := mock_os.NewMockFileInfo(ctrl)
- mockFI.EXPECT().Name().Return(name)
- fileInfos = append(fileInfos, mockFI)
- }
- return fileInfos, nil
- }
- fr.pods = tt.pods
- for _, p := range tt.apiPods {
- getter.pods[p.UID] = p
- }
- allSourcesReady := true
- err := rkt.GarbageCollect(tt.gcPolicy, allSourcesReady)
- assert.NoError(t, err, testCaseHint)
- sort.Sort(sortedStringList(tt.expectedCommands))
- sort.Sort(sortedStringList(cli.cmds))
- assert.Equal(t, tt.expectedCommands, cli.cmds, testCaseHint)
- sort.Sort(sortedStringList(tt.expectedServiceFiles))
- sort.Sort(sortedStringList(fakeOS.Removes))
- sort.Sort(sortedStringList(fs.resetFailedUnits))
- assert.Equal(t, tt.expectedServiceFiles, fakeOS.Removes, testCaseHint)
- var expectedService []string
- for _, f := range tt.expectedServiceFiles {
- expectedService = append(expectedService, filepath.Base(f))
- }
- assert.Equal(t, expectedService, fs.resetFailedUnits, testCaseHint)
- // Cleanup after each test.
- cli.Reset()
- ctrl.Finish()
- fakeOS.Removes = []string{}
- fs.resetFailedUnits = []string{}
- getter.pods = make(map[kubetypes.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()
- fr := newFakeRktInterface()
- fs := newFakeSystemd()
- r := &Runtime{apisvc: fr, systemd: fs}
- 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: "io.kubernetes.container.name",
- Value: "POD",
- },
- {
- Name: appctypes.ACIdentifier(k8sRktStage1NameAnno),
- Value: "stage1-override-img",
- },
- {
- Name: appctypes.ACIdentifier(types.KubernetesPodUIDLabel),
- Value: "uid-1",
- },
- {
- Name: appctypes.ACIdentifier(types.KubernetesPodNameLabel),
- Value: "name-1",
- },
- {
- Name: appctypes.ACIdentifier(k8sRktKubeletAnno),
- Value: "true",
- },
- {
- Name: appctypes.ACIdentifier(types.KubernetesPodNamespaceLabel),
- Value: "namespace-1",
- },
- {
- Name: appctypes.ACIdentifier(k8sRktRestartCountAnno),
- Value: "0",
- },
- },
- },
- },
- }
- for i, testCase := range testCases {
- hint := fmt.Sprintf("case #%d", i)
- result, err := r.makePodManifest(testCase.in, "", []api.Secret{})
- assert.Equal(t, testCase.outerr, err, hint)
- if err == nil {
- sort.Sort(annotationsByName(result.Annotations))
- sort.Sort(annotationsByName(testCase.out.Annotations))
- assert.Equal(t, testCase.out.Annotations, result.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-image0",
- []string{"prepare", "--quiet", "--pod-manifest", "file", "--stage1-name=stage1-image"},
- },
- {
- appcschema.PodManifest{
- Annotations: appctypes.Annotations{},
- },
- "stage1-image0",
- []string{"prepare", "--quiet", "--pod-manifest", "file", "--stage1-name=stage1-image0"},
- },
- {
- 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))
- }
- }
|