// +build !remoteclient package integration import ( "fmt" "io/ioutil" "os" "path/filepath" "text/template" . "github.com/containers/libpod/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var yamlTemplate = ` apiVersion: v1 kind: Pod metadata: creationTimestamp: "2019-07-17T14:44:08Z" labels: app: {{ .Name }} name: {{ .Name }} {{ with .Annotations }} annotations: {{ range $key, $value := . }} {{ $key }}: {{ $value }} {{ end }} {{ end }} spec: hostname: {{ .Hostname }} containers: {{ with .Ctrs }} {{ range . }} - command: {{ range .Cmd }} - {{.}} {{ end }} env: - name: PATH value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - name: TERM value: xterm - name: HOSTNAME - name: container value: podman image: {{ .Image }} name: {{ .Name }} imagePullPolicy: {{ .PullPolicy }} resources: {} {{ if .SecurityContext }} securityContext: allowPrivilegeEscalation: true {{ if .Caps }} capabilities: {{ with .CapAdd }} add: {{ range . }} - {{.}} {{ end }} {{ end }} {{ with .CapDrop }} drop: {{ range . }} - {{.}} {{ end }} {{ end }} {{ end }} privileged: false readOnlyRootFilesystem: false workingDir: / {{ end }} {{ end }} {{ end }} status: {} ` var ( defaultCtrName = "testCtr" defaultCtrCmd = []string{"top"} defaultCtrImage = ALPINE defaultPodName = "testPod" seccompPwdEPERM = []byte(`{"defaultAction":"SCMP_ACT_ALLOW","syscalls":[{"name":"getcwd","action":"SCMP_ACT_ERRNO"}]}`) ) func generateKubeYaml(pod *Pod, fileName string) error { f, err := os.Create(fileName) if err != nil { return err } defer f.Close() t, err := template.New("pod").Parse(yamlTemplate) if err != nil { return err } if err := t.Execute(f, pod); err != nil { return err } return nil } // Pod describes the options a kube yaml can be configured at pod level type Pod struct { Name string Hostname string Ctrs []*Ctr Annotations map[string]string } // getPod takes a list of podOptions and returns a pod with sane defaults // and the configured options // if no containers are added, it will add the default container func getPod(options ...podOption) *Pod { p := Pod{defaultPodName, "", make([]*Ctr, 0), make(map[string]string)} for _, option := range options { option(&p) } if len(p.Ctrs) == 0 { p.Ctrs = []*Ctr{getCtr()} } return &p } type podOption func(*Pod) func withHostname(h string) podOption { return func(pod *Pod) { pod.Hostname = h } } func withCtr(c *Ctr) podOption { return func(pod *Pod) { pod.Ctrs = append(pod.Ctrs, c) } } func withAnnotation(k, v string) podOption { return func(pod *Pod) { pod.Annotations[k] = v } } // Ctr describes the options a kube yaml can be configured at container level type Ctr struct { Name string Image string Cmd []string SecurityContext bool Caps bool CapAdd []string CapDrop []string PullPolicy string } // getCtr takes a list of ctrOptions and returns a Ctr with sane defaults // and the configured options func getCtr(options ...ctrOption) *Ctr { c := Ctr{defaultCtrName, defaultCtrImage, defaultCtrCmd, true, false, nil, nil, ""} for _, option := range options { option(&c) } return &c } type ctrOption func(*Ctr) func withCmd(cmd []string) ctrOption { return func(c *Ctr) { c.Cmd = cmd } } func withImage(img string) ctrOption { return func(c *Ctr) { c.Image = img } } func withSecurityContext(sc bool) ctrOption { return func(c *Ctr) { c.SecurityContext = sc } } func withCapAdd(caps []string) ctrOption { return func(c *Ctr) { c.CapAdd = caps c.Caps = true } } func withCapDrop(caps []string) ctrOption { return func(c *Ctr) { c.CapDrop = caps c.Caps = true } } func withPullPolicy(policy string) ctrOption { return func(c *Ctr) { c.PullPolicy = policy } } func getCtrNameInPod(pod *Pod) string { return fmt.Sprintf("%s-%s", pod.Name, defaultCtrName) } var _ = Describe("Podman generate kube", func() { var ( tempdir string err error podmanTest *PodmanTestIntegration kubeYaml string ) BeforeEach(func() { tempdir, err = CreateTempDirInTempDir() if err != nil { os.Exit(1) } podmanTest = PodmanTestCreate(tempdir) podmanTest.Setup() podmanTest.SeedImages() kubeYaml = filepath.Join(podmanTest.TempDir, "kube.yaml") }) AfterEach(func() { podmanTest.Cleanup() f := CurrentGinkgoTestDescription() processTestResult(f) }) It("podman play kube fail with nonexist authfile", func() { err := generateKubeYaml(getPod(), kubeYaml) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", "--authfile", "/tmp/nonexist", kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Not(Equal(0))) }) It("podman play kube test correct command", func() { pod := getPod() err := generateKubeYaml(pod, kubeYaml) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod)}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) Expect(inspect.OutputToString()).To(ContainSubstring(defaultCtrCmd[0])) }) It("podman play kube test correct output", func() { p := getPod(withCtr(getCtr(withCmd([]string{"echo", "hello"})))) err := generateKubeYaml(p, kubeYaml) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) logs := podmanTest.Podman([]string{"logs", getCtrNameInPod(p)}) logs.WaitWithDefaultTimeout() Expect(logs.ExitCode()).To(Equal(0)) Expect(logs.OutputToString()).To(ContainSubstring("hello")) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(p), "--format", "'{{ .Config.Cmd }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) Expect(inspect.OutputToString()).To(ContainSubstring("hello")) }) It("podman play kube test hostname", func() { pod := getPod() err := generateKubeYaml(pod, kubeYaml) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "{{ .Config.Hostname }}"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) Expect(inspect.OutputToString()).To(Equal(defaultPodName)) }) It("podman play kube test with customized hostname", func() { hostname := "myhostname" pod := getPod(withHostname(hostname)) err := generateKubeYaml(getPod(withHostname(hostname)), kubeYaml) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "{{ .Config.Hostname }}"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) Expect(inspect.OutputToString()).To(Equal(hostname)) }) It("podman play kube cap add", func() { capAdd := "CAP_SYS_ADMIN" ctr := getCtr(withCapAdd([]string{capAdd}), withCmd([]string{"cat", "/proc/self/status"})) pod := getPod(withCtr(ctr)) err := generateKubeYaml(pod, kubeYaml) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod)}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) Expect(inspect.OutputToString()).To(ContainSubstring(capAdd)) }) It("podman play kube cap drop", func() { capDrop := "CAP_CHOWN" ctr := getCtr(withCapDrop([]string{capDrop})) pod := getPod(withCtr(ctr)) err := generateKubeYaml(pod, kubeYaml) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod)}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) Expect(inspect.OutputToString()).To(ContainSubstring(capDrop)) }) It("podman play kube no security context", func() { // expect play kube to not fail if no security context is specified pod := getPod(withCtr(getCtr(withSecurityContext(false)))) err := generateKubeYaml(pod, kubeYaml) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod)}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) }) It("podman play kube seccomp container level", func() { // expect play kube is expected to set a seccomp label if it's applied as an annotation jsonFile, err := podmanTest.CreateSeccompJson(seccompPwdEPERM) if err != nil { fmt.Println(err) Skip("Failed to prepare seccomp.json for test.") } ctrAnnotation := "container.seccomp.security.alpha.kubernetes.io/" + defaultCtrName ctr := getCtr(withCmd([]string{"pwd"})) pod := getPod(withCtr(ctr), withAnnotation(ctrAnnotation, "localhost/"+filepath.Base(jsonFile))) err = generateKubeYaml(pod, kubeYaml) Expect(err).To(BeNil()) // CreateSeccompJson will put the profile into podmanTest.TempDir. Use --seccomp-profile-root to tell play kube where to look kube := podmanTest.Podman([]string{"play", "kube", "--seccomp-profile-root", podmanTest.TempDir, kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) logs := podmanTest.Podman([]string{"logs", getCtrNameInPod(pod)}) logs.WaitWithDefaultTimeout() Expect(logs.ExitCode()).To(Equal(0)) Expect(logs.OutputToString()).To(ContainSubstring("Operation not permitted")) }) It("podman play kube seccomp pod level", func() { // expect play kube is expected to set a seccomp label if it's applied as an annotation jsonFile, err := podmanTest.CreateSeccompJson(seccompPwdEPERM) if err != nil { fmt.Println(err) Skip("Failed to prepare seccomp.json for test.") } defer os.Remove(jsonFile) ctr := getCtr(withCmd([]string{"pwd"})) pod := getPod(withCtr(ctr), withAnnotation("seccomp.security.alpha.kubernetes.io/pod", "localhost/"+filepath.Base(jsonFile))) err = generateKubeYaml(pod, kubeYaml) Expect(err).To(BeNil()) // CreateSeccompJson will put the profile into podmanTest.TempDir. Use --seccomp-profile-root to tell play kube where to look kube := podmanTest.Podman([]string{"play", "kube", "--seccomp-profile-root", podmanTest.TempDir, kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) logs := podmanTest.Podman([]string{"logs", getCtrNameInPod(pod)}) logs.WaitWithDefaultTimeout() Expect(logs.ExitCode()).To(Equal(0)) Expect(logs.OutputToString()).To(ContainSubstring("Operation not permitted")) }) It("podman play kube with pull policy of never should be 125", func() { ctr := getCtr(withPullPolicy("never"), withImage(BB_GLIBC)) err := generateKubeYaml(getPod(withCtr(ctr)), kubeYaml) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(125)) }) It("podman play kube with pull policy of missing", func() { ctr := getCtr(withPullPolicy("missing"), withImage(BB)) err := generateKubeYaml(getPod(withCtr(ctr)), kubeYaml) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) }) It("podman play kube with pull always", func() { oldBB := "docker.io/library/busybox:1.30.1" pull := podmanTest.Podman([]string{"pull", oldBB}) pull.WaitWithDefaultTimeout() tag := podmanTest.Podman([]string{"tag", oldBB, BB}) tag.WaitWithDefaultTimeout() Expect(tag.ExitCode()).To(BeZero()) rmi := podmanTest.Podman([]string{"rmi", oldBB}) rmi.WaitWithDefaultTimeout() Expect(rmi.ExitCode()).To(BeZero()) inspect := podmanTest.Podman([]string{"inspect", BB}) inspect.WaitWithDefaultTimeout() oldBBinspect := inspect.InspectImageJSON() ctr := getCtr(withPullPolicy("always"), withImage(BB)) err := generateKubeYaml(getPod(withCtr(ctr)), kubeYaml) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) inspect = podmanTest.Podman([]string{"inspect", BB}) inspect.WaitWithDefaultTimeout() newBBinspect := inspect.InspectImageJSON() Expect(oldBBinspect[0].Digest).To(Not(Equal(newBBinspect[0].Digest))) }) It("podman play kube with latest image should always pull", func() { oldBB := "docker.io/library/busybox:1.30.1" pull := podmanTest.Podman([]string{"pull", oldBB}) pull.WaitWithDefaultTimeout() tag := podmanTest.Podman([]string{"tag", oldBB, BB}) tag.WaitWithDefaultTimeout() Expect(tag.ExitCode()).To(BeZero()) rmi := podmanTest.Podman([]string{"rmi", oldBB}) rmi.WaitWithDefaultTimeout() Expect(rmi.ExitCode()).To(BeZero()) inspect := podmanTest.Podman([]string{"inspect", BB}) inspect.WaitWithDefaultTimeout() oldBBinspect := inspect.InspectImageJSON() ctr := getCtr(withImage(BB)) err := generateKubeYaml(getPod(withCtr(ctr)), kubeYaml) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) inspect = podmanTest.Podman([]string{"inspect", BB}) inspect.WaitWithDefaultTimeout() newBBinspect := inspect.InspectImageJSON() Expect(oldBBinspect[0].Digest).To(Not(Equal(newBBinspect[0].Digest))) }) It("podman play kube with image data", func() { testyaml := ` apiVersion: v1 kind: Pod metadata: name: demo_pod spec: containers: - image: demo name: demo_kube ` pull := podmanTest.Podman([]string{"create", "--workdir", "/etc", "--name", "newBB", "--label", "key1=value1", "alpine"}) pull.WaitWithDefaultTimeout() Expect(pull.ExitCode()).To(BeZero()) c := podmanTest.Podman([]string{"commit", "-c", "STOPSIGNAL=51", "newBB", "demo"}) c.WaitWithDefaultTimeout() Expect(c.ExitCode()).To(Equal(0)) conffile := filepath.Join(podmanTest.TempDir, "kube.yaml") tempdir, err = CreateTempDirInTempDir() Expect(err).To(BeNil()) err := ioutil.WriteFile(conffile, []byte(testyaml), 0755) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", conffile}) kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) inspect := podmanTest.Podman([]string{"inspect", "demo_pod-demo_kube"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) ctr := inspect.InspectContainerToJSON() Expect(ctr[0].Config.WorkingDir).To(ContainSubstring("/etc")) Expect(ctr[0].Config.Labels["key1"]).To(ContainSubstring("value1")) Expect(ctr[0].Config.Labels["key1"]).To(ContainSubstring("value1")) Expect(ctr[0].Config.StopSignal).To(Equal(uint(51))) }) })