github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/test/e2e/play_kube_test.go (about)

     1  // +build !remoteclient
     2  
     3  package integration
     4  
     5  import (
     6  	"fmt"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"text/template"
    11  
    12  	. "github.com/containers/libpod/test/utils"
    13  	. "github.com/onsi/ginkgo"
    14  	. "github.com/onsi/gomega"
    15  )
    16  
    17  var yamlTemplate = `
    18  apiVersion: v1
    19  kind: Pod
    20  metadata:
    21    creationTimestamp: "2019-07-17T14:44:08Z"
    22    labels:
    23      app: {{ .Name }}
    24    name: {{ .Name }}
    25  {{ with .Annotations }}
    26    annotations:
    27    {{ range $key, $value := . }}
    28      {{ $key }}: {{ $value }}
    29    {{ end }}
    30  {{ end }}
    31  
    32  spec:
    33    hostname: {{ .Hostname }}
    34    containers:
    35  {{ with .Ctrs }}
    36    {{ range . }}
    37    - command:
    38      {{ range .Cmd }}
    39      - {{.}}
    40      {{ end }}
    41      env:
    42      - name: PATH
    43        value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    44      - name: TERM
    45        value: xterm
    46      - name: HOSTNAME
    47      - name: container
    48        value: podman
    49      image: {{ .Image }}
    50      name: {{ .Name }}
    51      imagePullPolicy: {{ .PullPolicy }}
    52      resources: {}
    53      {{ if .SecurityContext }}
    54      securityContext:
    55        allowPrivilegeEscalation: true
    56        {{ if .Caps }}
    57        capabilities:
    58          {{ with .CapAdd }}
    59          add:
    60            {{ range . }}
    61            - {{.}}
    62            {{ end }}
    63          {{ end }}
    64          {{ with .CapDrop }}
    65          drop:
    66            {{ range . }}
    67            - {{.}}
    68            {{ end }}
    69          {{ end }}
    70        {{ end }}
    71        privileged: false
    72        readOnlyRootFilesystem: false
    73      workingDir: /
    74      {{ end }}
    75    {{ end }}
    76  {{ end }}
    77  status: {}
    78  `
    79  
    80  var (
    81  	defaultCtrName  = "testCtr"
    82  	defaultCtrCmd   = []string{"top"}
    83  	defaultCtrImage = ALPINE
    84  	defaultPodName  = "testPod"
    85  	seccompPwdEPERM = []byte(`{"defaultAction":"SCMP_ACT_ALLOW","syscalls":[{"name":"getcwd","action":"SCMP_ACT_ERRNO"}]}`)
    86  )
    87  
    88  func generateKubeYaml(pod *Pod, fileName string) error {
    89  	f, err := os.Create(fileName)
    90  	if err != nil {
    91  		return err
    92  	}
    93  	defer f.Close()
    94  
    95  	t, err := template.New("pod").Parse(yamlTemplate)
    96  	if err != nil {
    97  		return err
    98  	}
    99  
   100  	if err := t.Execute(f, pod); err != nil {
   101  		return err
   102  	}
   103  
   104  	return nil
   105  }
   106  
   107  // Pod describes the options a kube yaml can be configured at pod level
   108  type Pod struct {
   109  	Name        string
   110  	Hostname    string
   111  	Ctrs        []*Ctr
   112  	Annotations map[string]string
   113  }
   114  
   115  // getPod takes a list of podOptions and returns a pod with sane defaults
   116  // and the configured options
   117  // if no containers are added, it will add the default container
   118  func getPod(options ...podOption) *Pod {
   119  	p := Pod{defaultPodName, "", make([]*Ctr, 0), make(map[string]string)}
   120  	for _, option := range options {
   121  		option(&p)
   122  	}
   123  	if len(p.Ctrs) == 0 {
   124  		p.Ctrs = []*Ctr{getCtr()}
   125  	}
   126  	return &p
   127  }
   128  
   129  type podOption func(*Pod)
   130  
   131  func withHostname(h string) podOption {
   132  	return func(pod *Pod) {
   133  		pod.Hostname = h
   134  	}
   135  }
   136  
   137  func withCtr(c *Ctr) podOption {
   138  	return func(pod *Pod) {
   139  		pod.Ctrs = append(pod.Ctrs, c)
   140  	}
   141  }
   142  
   143  func withAnnotation(k, v string) podOption {
   144  	return func(pod *Pod) {
   145  		pod.Annotations[k] = v
   146  	}
   147  }
   148  
   149  // Ctr describes the options a kube yaml can be configured at container level
   150  type Ctr struct {
   151  	Name            string
   152  	Image           string
   153  	Cmd             []string
   154  	SecurityContext bool
   155  	Caps            bool
   156  	CapAdd          []string
   157  	CapDrop         []string
   158  	PullPolicy      string
   159  }
   160  
   161  // getCtr takes a list of ctrOptions and returns a Ctr with sane defaults
   162  // and the configured options
   163  func getCtr(options ...ctrOption) *Ctr {
   164  	c := Ctr{defaultCtrName, defaultCtrImage, defaultCtrCmd, true, false, nil, nil, ""}
   165  	for _, option := range options {
   166  		option(&c)
   167  	}
   168  	return &c
   169  }
   170  
   171  type ctrOption func(*Ctr)
   172  
   173  func withCmd(cmd []string) ctrOption {
   174  	return func(c *Ctr) {
   175  		c.Cmd = cmd
   176  	}
   177  }
   178  
   179  func withImage(img string) ctrOption {
   180  	return func(c *Ctr) {
   181  		c.Image = img
   182  	}
   183  }
   184  
   185  func withSecurityContext(sc bool) ctrOption {
   186  	return func(c *Ctr) {
   187  		c.SecurityContext = sc
   188  	}
   189  }
   190  
   191  func withCapAdd(caps []string) ctrOption {
   192  	return func(c *Ctr) {
   193  		c.CapAdd = caps
   194  		c.Caps = true
   195  	}
   196  }
   197  
   198  func withCapDrop(caps []string) ctrOption {
   199  	return func(c *Ctr) {
   200  		c.CapDrop = caps
   201  		c.Caps = true
   202  	}
   203  }
   204  
   205  func withPullPolicy(policy string) ctrOption {
   206  	return func(c *Ctr) {
   207  		c.PullPolicy = policy
   208  	}
   209  }
   210  
   211  var _ = Describe("Podman generate kube", func() {
   212  	var (
   213  		tempdir    string
   214  		err        error
   215  		podmanTest *PodmanTestIntegration
   216  		kubeYaml   string
   217  	)
   218  
   219  	BeforeEach(func() {
   220  		tempdir, err = CreateTempDirInTempDir()
   221  		if err != nil {
   222  			os.Exit(1)
   223  		}
   224  		podmanTest = PodmanTestCreate(tempdir)
   225  		podmanTest.Setup()
   226  		podmanTest.SeedImages()
   227  
   228  		kubeYaml = filepath.Join(podmanTest.TempDir, "kube.yaml")
   229  	})
   230  
   231  	AfterEach(func() {
   232  		podmanTest.Cleanup()
   233  		f := CurrentGinkgoTestDescription()
   234  		processTestResult(f)
   235  	})
   236  
   237  	It("podman play kube fail with nonexist authfile", func() {
   238  		err := generateKubeYaml(getPod(), kubeYaml)
   239  		Expect(err).To(BeNil())
   240  
   241  		kube := podmanTest.Podman([]string{"play", "kube", "--authfile", "/tmp/nonexist", kubeYaml})
   242  		kube.WaitWithDefaultTimeout()
   243  		Expect(kube.ExitCode()).To(Not(Equal(0)))
   244  
   245  	})
   246  
   247  	It("podman play kube test correct command", func() {
   248  		err := generateKubeYaml(getPod(), kubeYaml)
   249  		Expect(err).To(BeNil())
   250  
   251  		kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
   252  		kube.WaitWithDefaultTimeout()
   253  		Expect(kube.ExitCode()).To(Equal(0))
   254  
   255  		inspect := podmanTest.Podman([]string{"inspect", defaultCtrName})
   256  		inspect.WaitWithDefaultTimeout()
   257  		Expect(inspect.ExitCode()).To(Equal(0))
   258  		Expect(inspect.OutputToString()).To(ContainSubstring(defaultCtrCmd[0]))
   259  	})
   260  
   261  	It("podman play kube test correct output", func() {
   262  		p := getPod(withCtr(getCtr(withCmd([]string{"echo", "hello"}))))
   263  
   264  		err := generateKubeYaml(p, kubeYaml)
   265  		Expect(err).To(BeNil())
   266  
   267  		kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
   268  		kube.WaitWithDefaultTimeout()
   269  		Expect(kube.ExitCode()).To(Equal(0))
   270  
   271  		logs := podmanTest.Podman([]string{"logs", defaultCtrName})
   272  		logs.WaitWithDefaultTimeout()
   273  		Expect(logs.ExitCode()).To(Equal(0))
   274  		Expect(logs.OutputToString()).To(ContainSubstring("hello"))
   275  
   276  		inspect := podmanTest.Podman([]string{"inspect", defaultCtrName, "--format", "'{{ .Config.Cmd }}'"})
   277  		inspect.WaitWithDefaultTimeout()
   278  		Expect(inspect.ExitCode()).To(Equal(0))
   279  		Expect(inspect.OutputToString()).To(ContainSubstring("hello"))
   280  	})
   281  
   282  	It("podman play kube test hostname", func() {
   283  		err := generateKubeYaml(getPod(), kubeYaml)
   284  		Expect(err).To(BeNil())
   285  
   286  		kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
   287  		kube.WaitWithDefaultTimeout()
   288  		Expect(kube.ExitCode()).To(Equal(0))
   289  
   290  		inspect := podmanTest.Podman([]string{"inspect", defaultCtrName, "--format", "{{ .Config.Hostname }}"})
   291  		inspect.WaitWithDefaultTimeout()
   292  		Expect(inspect.ExitCode()).To(Equal(0))
   293  		Expect(inspect.OutputToString()).To(Equal(defaultPodName))
   294  	})
   295  
   296  	It("podman play kube test with customized hostname", func() {
   297  		hostname := "myhostname"
   298  		err := generateKubeYaml(getPod(withHostname(hostname)), kubeYaml)
   299  		Expect(err).To(BeNil())
   300  
   301  		kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
   302  		kube.WaitWithDefaultTimeout()
   303  		Expect(kube.ExitCode()).To(Equal(0))
   304  
   305  		inspect := podmanTest.Podman([]string{"inspect", defaultCtrName, "--format", "{{ .Config.Hostname }}"})
   306  		inspect.WaitWithDefaultTimeout()
   307  		Expect(inspect.ExitCode()).To(Equal(0))
   308  		Expect(inspect.OutputToString()).To(Equal(hostname))
   309  	})
   310  
   311  	It("podman play kube cap add", func() {
   312  		capAdd := "CAP_SYS_ADMIN"
   313  		ctr := getCtr(withCapAdd([]string{capAdd}), withCmd([]string{"cat", "/proc/self/status"}))
   314  
   315  		err := generateKubeYaml(getPod(withCtr(ctr)), kubeYaml)
   316  		Expect(err).To(BeNil())
   317  
   318  		kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
   319  		kube.WaitWithDefaultTimeout()
   320  		Expect(kube.ExitCode()).To(Equal(0))
   321  
   322  		inspect := podmanTest.Podman([]string{"inspect", defaultCtrName})
   323  		inspect.WaitWithDefaultTimeout()
   324  		Expect(inspect.ExitCode()).To(Equal(0))
   325  		Expect(inspect.OutputToString()).To(ContainSubstring(capAdd))
   326  	})
   327  
   328  	It("podman play kube cap drop", func() {
   329  		capDrop := "CAP_CHOWN"
   330  		ctr := getCtr(withCapDrop([]string{capDrop}))
   331  
   332  		err := generateKubeYaml(getPod(withCtr(ctr)), kubeYaml)
   333  		Expect(err).To(BeNil())
   334  
   335  		kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
   336  		kube.WaitWithDefaultTimeout()
   337  		Expect(kube.ExitCode()).To(Equal(0))
   338  
   339  		inspect := podmanTest.Podman([]string{"inspect", defaultCtrName})
   340  		inspect.WaitWithDefaultTimeout()
   341  		Expect(inspect.ExitCode()).To(Equal(0))
   342  		Expect(inspect.OutputToString()).To(ContainSubstring(capDrop))
   343  	})
   344  
   345  	It("podman play kube no security context", func() {
   346  		// expect play kube to not fail if no security context is specified
   347  		err := generateKubeYaml(getPod(withCtr(getCtr(withSecurityContext(false)))), kubeYaml)
   348  		Expect(err).To(BeNil())
   349  
   350  		kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
   351  		kube.WaitWithDefaultTimeout()
   352  		Expect(kube.ExitCode()).To(Equal(0))
   353  
   354  		inspect := podmanTest.Podman([]string{"inspect", defaultCtrName})
   355  		inspect.WaitWithDefaultTimeout()
   356  		Expect(inspect.ExitCode()).To(Equal(0))
   357  	})
   358  
   359  	It("podman play kube seccomp container level", func() {
   360  		// expect play kube is expected to set a seccomp label if it's applied as an annotation
   361  		jsonFile, err := podmanTest.CreateSeccompJson(seccompPwdEPERM)
   362  		if err != nil {
   363  			fmt.Println(err)
   364  			Skip("Failed to prepare seccomp.json for test.")
   365  		}
   366  
   367  		ctrAnnotation := "container.seccomp.security.alpha.kubernetes.io/" + defaultCtrName
   368  		ctr := getCtr(withCmd([]string{"pwd"}))
   369  
   370  		err = generateKubeYaml(getPod(withCtr(ctr), withAnnotation(ctrAnnotation, "localhost/"+filepath.Base(jsonFile))), kubeYaml)
   371  		Expect(err).To(BeNil())
   372  
   373  		// CreateSeccompJson will put the profile into podmanTest.TempDir. Use --seccomp-profile-root to tell play kube where to look
   374  		kube := podmanTest.Podman([]string{"play", "kube", "--seccomp-profile-root", podmanTest.TempDir, kubeYaml})
   375  		kube.WaitWithDefaultTimeout()
   376  		Expect(kube.ExitCode()).To(Equal(0))
   377  
   378  		logs := podmanTest.Podman([]string{"logs", defaultCtrName})
   379  		logs.WaitWithDefaultTimeout()
   380  		Expect(logs.ExitCode()).To(Equal(0))
   381  		Expect(logs.OutputToString()).To(ContainSubstring("Operation not permitted"))
   382  	})
   383  
   384  	It("podman play kube seccomp pod level", func() {
   385  		// expect play kube is expected to set a seccomp label if it's applied as an annotation
   386  		jsonFile, err := podmanTest.CreateSeccompJson(seccompPwdEPERM)
   387  		if err != nil {
   388  			fmt.Println(err)
   389  			Skip("Failed to prepare seccomp.json for test.")
   390  		}
   391  		defer os.Remove(jsonFile)
   392  
   393  		ctr := getCtr(withCmd([]string{"pwd"}))
   394  
   395  		err = generateKubeYaml(getPod(withCtr(ctr), withAnnotation("seccomp.security.alpha.kubernetes.io/pod", "localhost/"+filepath.Base(jsonFile))), kubeYaml)
   396  		Expect(err).To(BeNil())
   397  
   398  		// CreateSeccompJson will put the profile into podmanTest.TempDir. Use --seccomp-profile-root to tell play kube where to look
   399  		kube := podmanTest.Podman([]string{"play", "kube", "--seccomp-profile-root", podmanTest.TempDir, kubeYaml})
   400  		kube.WaitWithDefaultTimeout()
   401  		Expect(kube.ExitCode()).To(Equal(0))
   402  
   403  		logs := podmanTest.Podman([]string{"logs", defaultCtrName})
   404  		logs.WaitWithDefaultTimeout()
   405  		Expect(logs.ExitCode()).To(Equal(0))
   406  		Expect(logs.OutputToString()).To(ContainSubstring("Operation not permitted"))
   407  	})
   408  
   409  	It("podman play kube with pull policy of never should be 125", func() {
   410  		ctr := getCtr(withPullPolicy("never"), withImage(BB_GLIBC))
   411  		err := generateKubeYaml(getPod(withCtr(ctr)), kubeYaml)
   412  		Expect(err).To(BeNil())
   413  
   414  		kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
   415  		kube.WaitWithDefaultTimeout()
   416  		Expect(kube.ExitCode()).To(Equal(125))
   417  	})
   418  
   419  	It("podman play kube with pull policy of missing", func() {
   420  		ctr := getCtr(withPullPolicy("missing"), withImage(BB))
   421  		err := generateKubeYaml(getPod(withCtr(ctr)), kubeYaml)
   422  		Expect(err).To(BeNil())
   423  
   424  		kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
   425  		kube.WaitWithDefaultTimeout()
   426  		Expect(kube.ExitCode()).To(Equal(0))
   427  	})
   428  
   429  	It("podman play kube with pull always", func() {
   430  		oldBB := "docker.io/library/busybox:1.30.1"
   431  		pull := podmanTest.Podman([]string{"pull", oldBB})
   432  		pull.WaitWithDefaultTimeout()
   433  
   434  		tag := podmanTest.Podman([]string{"tag", oldBB, BB})
   435  		tag.WaitWithDefaultTimeout()
   436  		Expect(tag.ExitCode()).To(BeZero())
   437  
   438  		rmi := podmanTest.Podman([]string{"rmi", oldBB})
   439  		rmi.WaitWithDefaultTimeout()
   440  		Expect(rmi.ExitCode()).To(BeZero())
   441  
   442  		inspect := podmanTest.Podman([]string{"inspect", BB})
   443  		inspect.WaitWithDefaultTimeout()
   444  		oldBBinspect := inspect.InspectImageJSON()
   445  
   446  		ctr := getCtr(withPullPolicy("always"), withImage(BB))
   447  		err := generateKubeYaml(getPod(withCtr(ctr)), kubeYaml)
   448  		Expect(err).To(BeNil())
   449  
   450  		kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
   451  		kube.WaitWithDefaultTimeout()
   452  		Expect(kube.ExitCode()).To(Equal(0))
   453  
   454  		inspect = podmanTest.Podman([]string{"inspect", BB})
   455  		inspect.WaitWithDefaultTimeout()
   456  		newBBinspect := inspect.InspectImageJSON()
   457  		Expect(oldBBinspect[0].Digest).To(Not(Equal(newBBinspect[0].Digest)))
   458  	})
   459  
   460  	It("podman play kube with latest image should always pull", func() {
   461  		oldBB := "docker.io/library/busybox:1.30.1"
   462  		pull := podmanTest.Podman([]string{"pull", oldBB})
   463  		pull.WaitWithDefaultTimeout()
   464  
   465  		tag := podmanTest.Podman([]string{"tag", oldBB, BB})
   466  		tag.WaitWithDefaultTimeout()
   467  		Expect(tag.ExitCode()).To(BeZero())
   468  
   469  		rmi := podmanTest.Podman([]string{"rmi", oldBB})
   470  		rmi.WaitWithDefaultTimeout()
   471  		Expect(rmi.ExitCode()).To(BeZero())
   472  
   473  		inspect := podmanTest.Podman([]string{"inspect", BB})
   474  		inspect.WaitWithDefaultTimeout()
   475  		oldBBinspect := inspect.InspectImageJSON()
   476  
   477  		ctr := getCtr(withImage(BB))
   478  		err := generateKubeYaml(getPod(withCtr(ctr)), kubeYaml)
   479  		Expect(err).To(BeNil())
   480  
   481  		kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
   482  		kube.WaitWithDefaultTimeout()
   483  		Expect(kube.ExitCode()).To(Equal(0))
   484  
   485  		inspect = podmanTest.Podman([]string{"inspect", BB})
   486  		inspect.WaitWithDefaultTimeout()
   487  		newBBinspect := inspect.InspectImageJSON()
   488  		Expect(oldBBinspect[0].Digest).To(Not(Equal(newBBinspect[0].Digest)))
   489  	})
   490  
   491  	It("podman play kube with image data", func() {
   492  		testyaml := `
   493  apiVersion: v1
   494  kind: Pod
   495  metadata:
   496    name: demo_pod
   497  spec:
   498    containers:
   499    - image: demo
   500      name: demo_kube
   501  `
   502  		pull := podmanTest.Podman([]string{"create", "--workdir", "/etc", "--name", "newBB", "--label", "key1=value1", "alpine"})
   503  
   504  		pull.WaitWithDefaultTimeout()
   505  		Expect(pull.ExitCode()).To(BeZero())
   506  
   507  		c := podmanTest.Podman([]string{"commit", "-c", "STOPSIGNAL=51", "newBB", "demo"})
   508  		c.WaitWithDefaultTimeout()
   509  		Expect(c.ExitCode()).To(Equal(0))
   510  
   511  		conffile := filepath.Join(podmanTest.TempDir, "kube.yaml")
   512  		tempdir, err = CreateTempDirInTempDir()
   513  		Expect(err).To(BeNil())
   514  
   515  		err := ioutil.WriteFile(conffile, []byte(testyaml), 0755)
   516  		Expect(err).To(BeNil())
   517  
   518  		kube := podmanTest.Podman([]string{"play", "kube", conffile})
   519  		kube.WaitWithDefaultTimeout()
   520  		Expect(kube.ExitCode()).To(Equal(0))
   521  
   522  		inspect := podmanTest.Podman([]string{"inspect", "demo_kube"})
   523  		inspect.WaitWithDefaultTimeout()
   524  		Expect(inspect.ExitCode()).To(Equal(0))
   525  
   526  		ctr := inspect.InspectContainerToJSON()
   527  		Expect(ctr[0].Config.WorkingDir).To(ContainSubstring("/etc"))
   528  		Expect(ctr[0].Config.Labels["key1"]).To(ContainSubstring("value1"))
   529  		Expect(ctr[0].Config.Labels["key1"]).To(ContainSubstring("value1"))
   530  		Expect(ctr[0].Config.StopSignal).To(Equal(uint(51)))
   531  	})
   532  })