diff options
Diffstat (limited to 'test')
28 files changed, 1099 insertions, 322 deletions
diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index b35c27215..5c35edf2b 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -169,7 +169,7 @@ t GET containers/$cid/json 200 \ .Args[1]="param2" t DELETE containers/$cid 204 -# test only set the entrpoint, Cmd should be [] +# test only set the entrypoint, Cmd should be [] t POST containers/create '"Image":"'$IMAGE'","Entrypoint":["echo","param1"]' 201 \ .Id~[0-9a-f]\\{64\\} cid=$(jq -r '.Id' <<<"$output") diff --git a/test/apiv2/25-containersMore.at b/test/apiv2/25-containersMore.at index 4f6b80a5f..9d774ef27 100644 --- a/test/apiv2/25-containersMore.at +++ b/test/apiv2/25-containersMore.at @@ -79,4 +79,13 @@ like "$output" ".*spec:.*" "Check generated kube yaml(service=true) - spec" like "$output" ".*kind:\\sService.*" "Check generated kube yaml(service=true) - kind: Service" t DELETE libpod/containers/$cid 204 + +# Create 3 stopped containers to test containers prune +podman run $IMAGE true +podman run $IMAGE true +podman run $IMAGE true + +t POST libpod/containers/prune '' 200 +t GET libpod/containers/json 200 \ + length=0 # vim: filetype=sh diff --git a/test/apiv2/35-networks.at b/test/apiv2/35-networks.at index ad34511c7..0ce56ee3c 100644 --- a/test/apiv2/35-networks.at +++ b/test/apiv2/35-networks.at @@ -9,8 +9,8 @@ t GET networks/non-existing-network 404 \ t POST libpod/networks/create?name=network1 '' 200 \ .Filename~.*/network1\\.conflist -# --data '{"Subnet":{"IP":"10.10.254.0","Mask":[255,255,255,0]}}' -t POST libpod/networks/create?name=network2 '"Subnet":{"IP":"10.10.254.0","Mask":[255,255,255,0]}' 200 \ +# --data '{"Subnet":{"IP":"10.10.254.0","Mask":[255,255,255,0]},"Labels":{"abc":"val"}}' +t POST libpod/networks/create?name=network2 '"Subnet":{"IP":"10.10.254.0","Mask":[255,255,255,0]},"Labels":{"abc":"val"}' 200 \ .Filename~.*/network2\\.conflist # test for empty mask @@ -22,7 +22,8 @@ t POST libpod/networks/create '"Subnet":{"IP":"10.10.1.0","Mask":[0,255,255,0]}' # network list t GET libpod/networks/json 200 -t GET libpod/networks/json?filter=name=network1 200 \ +# filters={"name":["network1"]} +t GET libpod/networks/json?filters=%7B%22name%22%3A%5B%22network1%22%5D%7D 200 \ length=1 \ .[0].Name=network1 t GET networks 200 @@ -34,12 +35,22 @@ length=2 #filters={"name":["network"]} t GET networks?filters=%7B%22name%22%3A%5B%22network%22%5D%7D 200 \ length=2 -# invalid filter filters={"label":"abc"} -t GET networks?filters=%7B%22label%22%3A%5B%22abc%22%5D%7D 500 \ -.cause="only the name filter for listing networks is implemented" -# invalid filter filters={"label":"abc","name":["network"]} -t GET networks?filters=%7B%22label%22%3A%22abc%22%2C%22name%22%3A%5B%22network%22%5D%7D 500 \ -.cause="only the name filter for listing networks is implemented" +# filters={"label":["abc"]} +t GET networks?filters=%7B%22label%22%3A%5B%22abc%22%5D%7D 200 \ +length=1 +# id filter filters={"id":["a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1"]} +t GET networks?filters=%7B%22id%22%3A%5B%22a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1%22%5D%7D 200 \ +length=1 \ +.[0].Name=network1 \ +.[0].Id=a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1 +# invalid filter filters={"dangling":["1"]} +t GET networks?filters=%7B%22dangling%22%3A%5B%221%22%5D%7D 500 \ +.cause='invalid filter "dangling"' + +# network inspect docker +t GET networks/a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1 200 \ +.Name=network1 \ +.Id=a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1 # clean the network t DELETE libpod/networks/network1 200 \ diff --git a/test/apiv2/rest_api/test_rest_v2_0_0.py b/test/apiv2/rest_api/test_rest_v2_0_0.py index 52348d4f4..2f9e62149 100644 --- a/test/apiv2/rest_api/test_rest_v2_0_0.py +++ b/test/apiv2/rest_api/test_rest_v2_0_0.py @@ -1,11 +1,13 @@ import json +import random +import string import subprocess -import sys -import time import unittest from multiprocessing import Process import requests +import sys +import time from dateutil.parser import parse from test.apiv2.rest_api import Podman @@ -91,14 +93,21 @@ class TestApi(unittest.TestCase): self.assertIsNotNone(r.content) _ = json.loads(r.text) + info = requests.get(PODMAN_URL + "/v1.40/info") + self.assertEqual(info.status_code, 200, info.content) + _ = json.loads(info.text) + def test_events(self): r = requests.get(_url("/events?stream=false")) self.assertEqual(r.status_code, 200, r.text) self.assertIsNotNone(r.content) - for line in r.text.splitlines(): + + report = r.text.splitlines() + self.assertGreater(len(report), 0, "No events found!") + for line in report: obj = json.loads(line) # Actor.ID is uppercase for compatibility - _ = obj["Actor"]["ID"] + self.assertIn("ID", obj["Actor"]) def test_containers(self): r = requests.get(_url("/containers/json"), timeout=5) @@ -172,22 +181,26 @@ class TestApi(unittest.TestCase): self.assertEqual(net_default.status_code, 201, net_default.text) create = requests.post( - PODMAN_URL + "/v1.40/containers/create?name=postCreate", + PODMAN_URL + "/v1.40/containers/create?name=postCreateConnect", json={ "Cmd": ["top"], "Image": "alpine:latest", "NetworkDisabled": False, # FIXME adding these 2 lines cause: (This is sampled from docker-py) # "network already exists","message":"container - # 01306e499df5441560d70071a54342611e422a94de20865add50a9565fd79fb9 is already connected to CNI network \"TestDefaultNetwork\": network already exists" + # 01306e499df5441560d70071a54342611e422a94de20865add50a9565fd79fb9 is already connected to CNI + # network \"TestDefaultNetwork\": network already exists" # "HostConfig": {"NetworkMode": "TestDefaultNetwork"}, # "NetworkingConfig": {"EndpointsConfig": {"TestDefaultNetwork": None}}, # FIXME These two lines cause: - # CNI network \"TestNetwork\" not found","message":"error configuring network namespace for container 369ddfa7d3211ebf1fbd5ddbff91bd33fa948858cea2985c133d6b6507546dff: CNI network \"TestNetwork\" not found" + # CNI network \"TestNetwork\" not found","message":"error configuring network namespace for container + # 369ddfa7d3211ebf1fbd5ddbff91bd33fa948858cea2985c133d6b6507546dff: CNI network \"TestNetwork\" not + # found" # "HostConfig": {"NetworkMode": "TestNetwork"}, # "NetworkingConfig": {"EndpointsConfig": {"TestNetwork": None}}, # FIXME no networking defined cause: (note this error is from the container inspect below) - # "internal libpod error","message":"network inspection mismatch: asked to join 2 CNI network(s) [TestDefaultNetwork podman], but have information on 1 network(s): internal libpod error" + # "internal libpod error","message":"network inspection mismatch: asked to join 2 CNI network(s) [ + # TestDefaultNetwork podman], but have information on 1 network(s): internal libpod error" }, ) self.assertEqual(create.status_code, 201, create.text) @@ -255,23 +268,68 @@ class TestApi(unittest.TestCase): def test_commit(self): r = requests.post(_url(ctnr("/commit?container={}"))) self.assertEqual(r.status_code, 200, r.text) - validateObjectFields(r.text) - def test_images(self): - r = requests.get(_url("/images/json")) + obj = json.loads(r.content) + self.assertIsInstance(obj, dict) + self.assertIn("Id", obj) + + def test_images_compat(self): + r = requests.get(PODMAN_URL + "/v1.40/images/json") self.assertEqual(r.status_code, 200, r.text) - validateObjectFields(r.content) - def test_inspect_image(self): - r = requests.get(_url("/images/alpine/json")) + # See https://docs.docker.com/engine/api/v1.40/#operation/ImageList + required_keys = ( + "Id", + "ParentId", + "RepoTags", + "RepoDigests", + "Created", + "Size", + "SharedSize", + "VirtualSize", + "Labels", + "Containers", + ) + objs = json.loads(r.content) + self.assertIn(type(objs), (list,)) + for o in objs: + self.assertIsInstance(o, dict) + for k in required_keys: + self.assertIn(k, o) + + def test_inspect_image_compat(self): + r = requests.get(PODMAN_URL + "/v1.40/images/alpine/json") self.assertEqual(r.status_code, 200, r.text) - obj = validateObjectFields(r.content) + + # See https://docs.docker.com/engine/api/v1.40/#operation/ImageInspect + required_keys = ( + "Id", + "Parent", + "Comment", + "Created", + "Container", + "DockerVersion", + "Author", + "Architecture", + "Os", + "Size", + "VirtualSize", + "GraphDriver", + "RootFS", + "Metadata", + ) + + obj = json.loads(r.content) + self.assertIn(type(obj), (dict,)) + for k in required_keys: + self.assertIn(k, obj) _ = parse(obj["Created"]) - def test_delete_image(self): - r = requests.delete(_url("/images/alpine?force=true")) + def test_delete_image_compat(self): + r = requests.delete(PODMAN_URL + "/v1.40/images/alpine?force=true") self.assertEqual(r.status_code, 200, r.text) - json.loads(r.text) + obj = json.loads(r.content) + self.assertIn(type(obj), (list,)) def test_pull(self): r = requests.post(_url("/images/pull?reference=alpine"), timeout=15) @@ -295,12 +353,13 @@ class TestApi(unittest.TestCase): self.assertTrue(keys["images"], "Expected to find images stanza") self.assertTrue(keys["stream"], "Expected to find stream progress stanza's") - def test_search(self): + def test_search_compat(self): # Had issues with this test hanging when repositories not happy def do_search(): - r = requests.get(_url("/images/search?term=alpine"), timeout=5) + r = requests.get(PODMAN_URL + "/v1.40/images/search?term=alpine", timeout=5) self.assertEqual(r.status_code, 200, r.text) - json.loads(r.text) + objs = json.loads(r.text) + self.assertIn(type(objs), (list,)) search = Process(target=do_search) search.start() @@ -308,17 +367,168 @@ class TestApi(unittest.TestCase): self.assertFalse(search.is_alive(), "/images/search took too long") def test_ping(self): + required_headers = ( + "API-Version", + "Builder-Version", + "Docker-Experimental", + "Cache-Control", + "Pragma", + "Pragma", + ) + + def check_headers(req): + for k in required_headers: + self.assertIn(k, req.headers) + r = requests.get(PODMAN_URL + "/_ping") self.assertEqual(r.status_code, 200, r.text) + self.assertEqual(r.text, "OK") + check_headers(r) r = requests.head(PODMAN_URL + "/_ping") self.assertEqual(r.status_code, 200, r.text) + self.assertEqual(r.text, "") + check_headers(r) r = requests.get(_url("/_ping")) self.assertEqual(r.status_code, 200, r.text) + self.assertEqual(r.text, "OK") + check_headers(r) - r = requests.get(_url("/_ping")) + r = requests.head(_url("/_ping")) self.assertEqual(r.status_code, 200, r.text) + self.assertEqual(r.text, "") + check_headers(r) + + def test_history_compat(self): + r = requests.get(PODMAN_URL + "/v1.40/images/alpine/history") + self.assertEqual(r.status_code, 200, r.text) + + # See https://docs.docker.com/engine/api/v1.40/#operation/ImageHistory + required_keys = ("Id", "Created", "CreatedBy", "Tags", "Size", "Comment") + + objs = json.loads(r.content) + self.assertIn(type(objs), (list,)) + for o in objs: + self.assertIsInstance(o, dict) + for k in required_keys: + self.assertIn(k, o) + + def test_network_compat(self): + name = "Network_" + "".join(random.choice(string.ascii_letters) for i in range(10)) + + # Cannot test for 0 existing networks because default "podman" network always exists + + create = requests.post(PODMAN_URL + "/v1.40/networks/create", json={"Name": name}) + self.assertEqual(create.status_code, 201, create.content) + obj = json.loads(create.content) + self.assertIn(type(obj), (dict,)) + self.assertIn("Id", obj) + ident = obj["Id"] + self.assertNotEqual(name, ident) + + ls = requests.get(PODMAN_URL + "/v1.40/networks") + self.assertEqual(ls.status_code, 200, ls.content) + objs = json.loads(ls.content) + self.assertIn(type(objs), (list,)) + + found = False + for network in objs: + if network["Name"] == name: + found = True + self.assertTrue(found, f"Network {name} not found") + + inspect = requests.get(PODMAN_URL + f"/v1.40/networks/{ident}") + self.assertEqual(inspect.status_code, 200, inspect.content) + obj = json.loads(create.content) + self.assertIn(type(obj), (dict,)) + + inspect = requests.delete(PODMAN_URL + f"/v1.40/networks/{ident}") + self.assertEqual(inspect.status_code, 204, inspect.content) + inspect = requests.get(PODMAN_URL + f"/v1.40/networks/{ident}") + self.assertEqual(inspect.status_code, 404, inspect.content) + + prune = requests.post(PODMAN_URL + "/v1.40/networks/prune") + self.assertEqual(prune.status_code, 405, prune.content) + + def test_volumes_compat(self): + name = "Volume_" + "".join(random.choice(string.ascii_letters) for i in range(10)) + + ls = requests.get(PODMAN_URL + "/v1.40/volumes") + self.assertEqual(ls.status_code, 200, ls.content) + + # See https://docs.docker.com/engine/api/v1.40/#operation/VolumeList + required_keys = ( + "Volumes", + "Warnings", + ) + + obj = json.loads(ls.content) + self.assertIn(type(obj), (dict,)) + for k in required_keys: + self.assertIn(k, obj) + + create = requests.post(PODMAN_URL + "/v1.40/volumes/create", json={"Name": name}) + self.assertEqual(create.status_code, 201, create.content) + + # See https://docs.docker.com/engine/api/v1.40/#operation/VolumeCreate + # and https://docs.docker.com/engine/api/v1.40/#operation/VolumeInspect + required_keys = ( + "Name", + "Driver", + "Mountpoint", + "Labels", + "Scope", + "Options", + ) + + obj = json.loads(create.content) + self.assertIn(type(obj), (dict,)) + for k in required_keys: + self.assertIn(k, obj) + self.assertEqual(obj["Name"], name) + + inspect = requests.get(PODMAN_URL + f"/v1.40/volumes/{name}") + self.assertEqual(inspect.status_code, 200, inspect.content) + + obj = json.loads(create.content) + self.assertIn(type(obj), (dict,)) + for k in required_keys: + self.assertIn(k, obj) + + rm = requests.delete(PODMAN_URL + f"/v1.40/volumes/{name}") + self.assertEqual(rm.status_code, 204, rm.content) + + prune = requests.post(PODMAN_URL + "/v1.40/volumes/prune") + self.assertEqual(prune.status_code, 200, prune.content) + + def test_auth_compat(self): + r = requests.post( + PODMAN_URL + "/v1.40/auth", + json={ + "username": "bozo", + "password": "wedontneednopasswords", + "serveraddress": "https://localhost/v1.40/", + }, + ) + self.assertEqual(r.status_code, 404, r.content) + + def test_version(self): + r = requests.get(PODMAN_URL + "/v1.40/version") + self.assertEqual(r.status_code, 200, r.content) + + r = requests.get(_url("/version")) + self.assertEqual(r.status_code, 200, r.content) + + def test_df_compat(self): + r = requests.get(PODMAN_URL + "/v1.40/system/df") + self.assertEqual(r.status_code, 200, r.content) + + obj = json.loads(r.content) + self.assertIn("Images", obj) + self.assertIn("Containers", obj) + self.assertIn("Volumes", obj) + self.assertIn("BuildCache", obj) if __name__ == "__main__": diff --git a/test/e2e/build_test.go b/test/e2e/build_test.go index 63a2df67a..ac9481797 100644 --- a/test/e2e/build_test.go +++ b/test/e2e/build_test.go @@ -239,7 +239,7 @@ RUN printenv http_proxy` Expect(session.ExitCode()).To(Equal(0)) // Verify that OS and Arch are being set - inspect := podmanTest.PodmanNoCache([]string{"image", "inspect", "--format", "{{ index .Config.Labels }}", "test"}) + inspect := podmanTest.Podman([]string{"image", "inspect", "--format", "{{ index .Config.Labels }}", "test"}) inspect.WaitWithDefaultTimeout() data := inspect.OutputToString() Expect(data).To(ContainSubstring(buildah.Version)) diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index 16d8bb770..d7bbdc633 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -317,7 +317,7 @@ func (p *PodmanTestIntegration) createArtifact(image string) { fmt.Printf("Caching %s at %s...", image, destName) if _, err := os.Stat(destName); os.IsNotExist(err) { pull := p.PodmanNoCache([]string{"pull", image}) - pull.Wait(90) + pull.Wait(240) Expect(pull.ExitCode()).To(Equal(0)) save := p.PodmanNoCache([]string{"save", "-o", destName, image}) diff --git a/test/e2e/config/containers.conf b/test/e2e/config/containers.conf index 5f852468d..35153ba05 100644 --- a/test/e2e/config/containers.conf +++ b/test/e2e/config/containers.conf @@ -52,3 +52,7 @@ dns_options=[ "debug", ] tz = "Pacific/Honolulu" umask = "0002" + +[engine] + +network_cmd_options=["allow_host_loopback=true"] diff --git a/test/e2e/containers_conf_test.go b/test/e2e/containers_conf_test.go index 906153c0f..28672cfc6 100644 --- a/test/e2e/containers_conf_test.go +++ b/test/e2e/containers_conf_test.go @@ -203,35 +203,35 @@ var _ = Describe("Podman run", func() { session := podmanTest.Podman([]string{"run", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session.LineInOuputStartsWith("search foobar.com") + session.LineInOutputStartsWith("search foobar.com") }) It("podman run add dns server", func() { session := podmanTest.Podman([]string{"run", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session.LineInOuputStartsWith("server 1.2.3.4") + session.LineInOutputStartsWith("server 1.2.3.4") }) It("podman run add dns option", func() { session := podmanTest.Podman([]string{"run", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session.LineInOuputStartsWith("options debug") + session.LineInOutputStartsWith("options debug") }) It("podman run containers.conf remove all search domain", func() { session := podmanTest.Podman([]string{"run", "--dns-search=.", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.LineInOuputStartsWith("search")).To(BeFalse()) + Expect(session.LineInOutputStartsWith("search")).To(BeFalse()) }) It("podman run use containers.conf search domain", func() { session := podmanTest.Podman([]string{"run", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.LineInOuputStartsWith("search")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("search")).To(BeTrue()) Expect(session.OutputToString()).To(ContainSubstring("foobar.com")) Expect(session.OutputToString()).To(ContainSubstring("1.2.3.4")) @@ -258,6 +258,12 @@ var _ = Describe("Podman run", func() { Expect(session.OutputToString()).To(Equal("0002")) }) + It("podman set network cmd options slirp options to allow host loopback", func() { + session := podmanTest.Podman([]string{"run", "--network", "slirp4netns", ALPINE, "ping", "-c1", "10.0.2.2"}) + session.Wait(30) + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman-remote test localcontainers.conf versus remote containers.conf", func() { if !IsRemote() { Skip("this test is only for remote") @@ -275,7 +281,7 @@ var _ = Describe("Podman run", func() { session = podmanTest.Podman([]string{"run", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.LineInOuputStartsWith("search")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("search")).To(BeTrue()) Expect(session.OutputToString()).To(ContainSubstring("foobar.com")) Expect(session.OutputToString()).To(ContainSubstring("1.2.3.4")) Expect(session.OutputToString()).To(ContainSubstring("debug")) @@ -311,4 +317,5 @@ var _ = Describe("Podman run", func() { Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(Equal("0022")) }) + }) diff --git a/test/e2e/cp_test.go b/test/e2e/cp_test.go index c1d3be5ab..33908b60e 100644 --- a/test/e2e/cp_test.go +++ b/test/e2e/cp_test.go @@ -4,14 +4,18 @@ import ( "io/ioutil" "os" "os/exec" + "os/user" "path/filepath" - "strings" . "github.com/containers/podman/v2/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) +// NOTE: Only smoke tests. The system tests (i.e., "./test/system/*") take +// care of function and regression tests. Please consider adding system tests +// rather than e2e tests. System tests are used in RHEL gating. + var _ = Describe("Podman cp", func() { var ( tempdir string @@ -37,240 +41,108 @@ var _ = Describe("Podman cp", func() { }) + // Copy a file to the container, then back to the host and make sure + // that the contents match. It("podman cp file", func() { - srcPath := filepath.Join(podmanTest.RunRoot, "cp_test.txt") - dstPath := filepath.Join(podmanTest.RunRoot, "cp_from_container") - fromHostToContainer := []byte("copy from host to container") - - session := podmanTest.Podman([]string{"create", ALPINE, "cat", "foo"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - name := session.OutputToString() - - err := ioutil.WriteFile(srcPath, fromHostToContainer, 0644) + srcFile, err := ioutil.TempFile("", "") Expect(err).To(BeNil()) + defer srcFile.Close() + defer os.Remove(srcFile.Name()) - session = podmanTest.Podman([]string{"cp", srcPath, name + ":foo/"}) - session.WaitWithDefaultTimeout() - Expect(session).To(ExitWithError()) - - session = podmanTest.Podman([]string{"cp", srcPath, name + ":foo"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - - session = podmanTest.Podman([]string{"cp", name + ":foo", dstPath}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - - session = podmanTest.Podman([]string{"start", name}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - }) - - It("podman cp file to dir", func() { - name := "testctr" - setup := podmanTest.RunTopContainer(name) - setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) - - srcPath := "/tmp/cp_test.txt" - fromHostToContainer := []byte("copy from host to container directory") - err := ioutil.WriteFile(srcPath, fromHostToContainer, 0644) + originalContent := []byte("podman cp file test") + err = ioutil.WriteFile(srcFile.Name(), originalContent, 0644) Expect(err).To(BeNil()) - session := podmanTest.Podman([]string{"exec", name, "mkdir", "foodir"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - - session = podmanTest.Podman([]string{"cp", srcPath, name + ":foodir/"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - - session = podmanTest.Podman([]string{"exec", name, "ls", "foodir/cp_test.txt"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - - os.Remove("/tmp/cp_test.txt") - }) - - It("podman cp dir to dir", func() { - testDirPath := filepath.Join(podmanTest.RunRoot, "TestDir1") - - session := podmanTest.Podman([]string{"create", ALPINE, "ls", "/foodir"}) + // Create a container. NOTE that container mustn't be running for copying. + session := podmanTest.Podman([]string{"create", ALPINE}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) name := session.OutputToString() - err := os.Mkdir(testDirPath, 0755) - Expect(err).To(BeNil()) - defer os.RemoveAll(testDirPath) - - session = podmanTest.Podman([]string{"cp", testDirPath, name + ":/foodir"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - - session = podmanTest.Podman([]string{"cp", testDirPath, name + ":/foodir"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - - testctr := "testctr" - setup := podmanTest.RunTopContainer(testctr) - setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + // Copy TO the container. - session = podmanTest.Podman([]string{"exec", testctr, "mkdir", "foo"}) + // Cannot copy to a non-existent path (note the trailing "/"). + session = podmanTest.Podman([]string{"cp", srcFile.Name(), name + ":foo/"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - - session = podmanTest.Podman([]string{"cp", testDirPath + "/.", testctr + ":/foo"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"exec", testctr, "ls", "foo"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - Expect(len(session.OutputToString())).To(Equal(0)) + Expect(session).To(ExitWithError()) - session = podmanTest.Podman([]string{"cp", testctr + ":/foo/.", testDirPath}) + // The file will now be created (and written to). + session = podmanTest.Podman([]string{"cp", srcFile.Name(), name + ":foo"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - cmd := exec.Command("ls", testDirPath) - res, err := cmd.Output() - Expect(err).To(BeNil()) - Expect(len(res)).To(Equal(0)) - }) - It("podman cp stdin/stdout", func() { - SkipIfRemote("FIXME: podman-remote cp not implemented yet") - session := podmanTest.Podman([]string{"create", ALPINE, "ls", "foo"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - name := session.OutputToString() + // Copy FROM the container. - testDirPath := filepath.Join(podmanTest.RunRoot, "TestDir2") - err := os.Mkdir(testDirPath, 0755) - Expect(err).To(BeNil()) - defer os.RemoveAll(testDirPath) - cmd := exec.Command("tar", "-zcvf", "file.tar.gz", testDirPath) - _, err = cmd.Output() + destFile, err := ioutil.TempFile("", "") Expect(err).To(BeNil()) + defer destFile.Close() + defer os.Remove(destFile.Name()) - data, err := ioutil.ReadFile("foo.tar.gz") - reader := strings.NewReader(string(data)) - cmd.Stdin = reader - session = podmanTest.Podman([]string{"cp", "-", name + ":/foo"}) + session = podmanTest.Podman([]string{"cp", name + ":foo", destFile.Name()}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"cp", "file.tar.gz", name + ":/foo.tar.gz"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"cp", name + ":/foo.tar.gz", "-"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - - os.Remove("file.tar.gz") - }) - - It("podman cp tar", func() { - testctr := "testctr" - setup := podmanTest.RunTopContainer(testctr) - setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) - - session := podmanTest.Podman([]string{"exec", testctr, "mkdir", "foo"}) + session = podmanTest.Podman([]string{"start", name}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - path, err := os.Getwd() - Expect(err).To(BeNil()) - testDirPath := filepath.Join(path, "TestDir3") - err = os.Mkdir(testDirPath, 0777) + // Now make sure the content matches. + roundtripContent, err := ioutil.ReadFile(destFile.Name()) Expect(err).To(BeNil()) - defer os.RemoveAll(testDirPath) - cmd := exec.Command("tar", "-cvf", "file.tar", testDirPath) - _, err = cmd.Output() - Expect(err).To(BeNil()) - - session = podmanTest.Podman([]string{"cp", "file.tar", "testctr:/foo/"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - - session = podmanTest.Podman([]string{"exec", testctr, "ls", "-l", "foo"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - Expect(session.OutputToString()).To(ContainSubstring("file.tar")) - - os.Remove("file.tar") + Expect(roundtripContent).To(Equal(originalContent)) }) - It("podman cp tar --extract", func() { - testctr := "testctr" - setup := podmanTest.RunTopContainer(testctr) - setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) - - session := podmanTest.Podman([]string{"exec", testctr, "mkdir", "/foo"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - - path, err := os.Getwd() - Expect(err).To(BeNil()) - testDirPath := filepath.Join(path, "TestDir4") - err = os.Mkdir(testDirPath, 0777) - Expect(err).To(BeNil()) - defer os.RemoveAll(testDirPath) - f, err := os.Create(filepath.Join(testDirPath, "a.txt")) - Expect(err).To(BeNil()) - _, err = f.Write([]byte("Hello World!!!\n")) - f.Close() - cmd := exec.Command("tar", "-cvf", "file.tar", "TestDir4") - exec.Command("tar", "-cvf", "/home/mvasek/file.tar", testDirPath) - _, err = cmd.Output() + // Create a symlink in the container, use it as a copy destination and + // make sure that the link and the resolved path are accessible and + // give the right content. + It("podman cp symlink", func() { + srcFile, err := ioutil.TempFile("", "") Expect(err).To(BeNil()) - defer os.Remove("file.tar") + defer srcFile.Close() + defer os.Remove(srcFile.Name()) - session = podmanTest.Podman([]string{"cp", "--extract", "file.tar", "testctr:/foo/"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - - session = podmanTest.Podman([]string{"exec", testctr, "cat", "/foo/TestDir4/a.txt"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - Expect(session.OutputToString()).To(ContainSubstring("Hello World!!!")) - }) + originalContent := []byte("podman cp symlink test") + err = ioutil.WriteFile(srcFile.Name(), originalContent, 0644) + Expect(err).To(BeNil()) - It("podman cp symlink", func() { session := podmanTest.Podman([]string{"run", "-d", ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) name := session.OutputToString() - srcPath := filepath.Join(podmanTest.RunRoot, "cp_test.txt") - fromHostToContainer := []byte("copy from host to container") - err := ioutil.WriteFile(srcPath, fromHostToContainer, 0644) - Expect(err).To(BeNil()) - session = podmanTest.Podman([]string{"exec", name, "ln", "-s", "/tmp", "/test"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"cp", "--pause=false", srcPath, name + ":/test"}) + session = podmanTest.Podman([]string{"cp", "--pause=false", srcFile.Name(), name + ":/test"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - _, err = os.Stat("/tmp/cp_test.txt") - Expect(err).To(Not(BeNil())) - - session = podmanTest.Podman([]string{"exec", name, "ln", "-s", "/tmp/nonesuch", "/test1"}) + session = podmanTest.Podman([]string{"exec", name, "cat", "/tmp/" + filepath.Base(srcFile.Name())}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(string(originalContent))) - session = podmanTest.Podman([]string{"cp", "--pause=false", srcPath, name + ":/test1/"}) + session = podmanTest.Podman([]string{"exec", name, "cat", "/test/" + filepath.Base(srcFile.Name())}) session.WaitWithDefaultTimeout() - Expect(session).To(ExitWithError()) - + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(string(originalContent))) }) + + // Copy a file to a volume in the container. The tricky part is that + // containers mustn't be running for copying, so Podman has to do some + // intense Yoga and 1) detect volume paths on the container, 2) resolve + // the path to the volume's mount point on the host, and 3) copy the + // data to the volume and not the container. It("podman cp volume", func() { + srcFile, err := ioutil.TempFile("", "") + Expect(err).To(BeNil()) + defer srcFile.Close() + defer os.Remove(srcFile.Name()) + + originalContent := []byte("podman cp volume") + err = ioutil.WriteFile(srcFile.Name(), originalContent, 0644) + Expect(err).To(BeNil()) session := podmanTest.Podman([]string{"volume", "create", "data"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -279,23 +151,31 @@ var _ = Describe("Podman cp", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - err = ioutil.WriteFile("cp_vol", []byte("copy to the volume"), 0644) - if err != nil { - os.Exit(1) - } - session = podmanTest.Podman([]string{"cp", "cp_vol", "container1" + ":/data/cp_vol1"}) + session = podmanTest.Podman([]string{"cp", srcFile.Name(), "container1" + ":/data/file.txt"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"cp", "container1" + ":/data/cp_vol1", "cp_vol2"}) + // Now get the volume's mount point, read the file and make + // sure the contents match. + session = podmanTest.Podman([]string{"volume", "inspect", "data", "--format", "{{.Mountpoint}}"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - os.Remove("cp_vol") - os.Remove("cp_vol2") + volumeMountPoint := session.OutputToString() + copiedContent, err := ioutil.ReadFile(filepath.Join(volumeMountPoint, "file.txt")) + Expect(err).To(BeNil()) + Expect(copiedContent).To(Equal(originalContent)) }) + // Create another user in the container, let them create a file, copy + // it to the host and back to the container and make sure that we can + // access it, and (roughly) the right users own it. It("podman cp from ctr chown ", func() { + srcFile, err := ioutil.TempFile("", "") + Expect(err).To(BeNil()) + defer srcFile.Close() + defer os.Remove(srcFile.Name()) + setup := podmanTest.RunTopContainer("testctr") setup.WaitWithDefaultTimeout() Expect(setup.ExitCode()).To(Equal(0)) @@ -308,17 +188,19 @@ var _ = Describe("Podman cp", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"cp", "--pause=false", "testctr:/tmp/testfile", "testfile1"}) + session = podmanTest.Podman([]string{"cp", "--pause=false", "testctr:/tmp/testfile", srcFile.Name()}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) // owner of the file copied to local machine is not testuser - cmd := exec.Command("ls", "-l", "testfile1") + u, err := user.Current() + Expect(err).To(BeNil()) + cmd := exec.Command("ls", "-l", srcFile.Name()) cmdRet, err := cmd.Output() Expect(err).To(BeNil()) - Expect(strings.Contains(string(cmdRet), "testuser")).To(BeFalse()) + Expect(string(cmdRet)).To(ContainSubstring(u.Username)) - session = podmanTest.Podman([]string{"cp", "--pause=false", "testfile1", "testctr:testfile2"}) + session = podmanTest.Podman([]string{"cp", "--pause=false", srcFile.Name(), "testctr:testfile2"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -327,45 +209,35 @@ var _ = Describe("Podman cp", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("root")) - - os.Remove("testfile1") }) - It("podman cp the root directory from the ctr to an existing directory on the host ", func() { - imgName := "test-cp-root-dir:latest" - DockerfileName := "Dockerfile.test-cp-root-dir" - ctrName := "test-container-cp-root" - session := podmanTest.Podman([]string{"build", "-f", "build/" + DockerfileName, "-t", imgName, "build/"}) + // Copy the root dir "/" of a container to the host. + It("podman cp the root directory from the ctr to an existing directory on the host ", func() { + container := "copyroottohost" + session := podmanTest.RunTopContainer(container) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - testDirPath := filepath.Join(podmanTest.RunRoot, "TestDirForCp") - - session = podmanTest.Podman([]string{"create", "--name", ctrName, imgName, "dummy"}) + session = podmanTest.Podman([]string{"exec", container, "touch", "/dummy.txt"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - err := os.Mkdir(testDirPath, 0755) + tmpDir, err := ioutil.TempDir("", "") Expect(err).To(BeNil()) - defer os.RemoveAll(testDirPath) - // Copy the root directory of the container to an existing directory - session = podmanTest.Podman([]string{"cp", ctrName + ":/", testDirPath}) + session = podmanTest.Podman([]string{"cp", container + ":/", tmpDir}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - // The file should be in the directory, - // not one layer too much of the directory called merged - checkFile := filepath.Join(testDirPath, DockerfileName) - _, err = os.Stat(checkFile) + cmd := exec.Command("ls", "-la", tmpDir) + output, err := cmd.Output() + lsOutput := string(output) Expect(err).To(BeNil()) - - session = podmanTest.Podman([]string{"container", "rm", ctrName}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - - session = podmanTest.Podman([]string{"rmi", "-f", imgName}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(lsOutput).To(ContainSubstring("dummy.txt")) + Expect(lsOutput).To(ContainSubstring("tmp")) + Expect(lsOutput).To(ContainSubstring("etc")) + Expect(lsOutput).To(ContainSubstring("var")) + Expect(lsOutput).To(ContainSubstring("bin")) + Expect(lsOutput).To(ContainSubstring("usr")) }) }) diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index 760345a67..aaf089d97 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "os" "path/filepath" + "runtime" "strings" . "github.com/containers/podman/v2/test/utils" @@ -644,4 +645,35 @@ var _ = Describe("Podman create", func() { Expect(session.ErrorToString()).To(ContainSubstring("unknown flag")) }) + It("podman create --platform", func() { + session := podmanTest.Podman([]string{"create", "--platform=linux/bogus", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + expectedError := "no image found in manifest list for architecture bogus" + Expect(session.ErrorToString()).To(ContainSubstring(expectedError)) + + session = podmanTest.Podman([]string{"create", "--platform=linux/arm64", "--override-os", "windows", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + expectedError = "--platform option can not be specified with --overide-arch or --override-os" + Expect(session.ErrorToString()).To(ContainSubstring(expectedError)) + + session = podmanTest.Podman([]string{"create", "-q", "--platform=linux/arm64", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + setup := podmanTest.Podman([]string{"container", "inspect", session.OutputToString()}) + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + data := setup.InspectContainerToJSON() + setup = podmanTest.Podman([]string{"image", "inspect", data[0].Image}) + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + idata := setup.InspectImageJSON() // returns []inspect.ImageData + Expect(len(idata)).To(Equal(1)) + Expect(idata[0].Os).To(Equal(runtime.GOOS)) + Expect(idata[0].Architecture).To(Equal("arm64")) + }) }) diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go index 4c65a85d5..281b2c313 100644 --- a/test/e2e/images_test.go +++ b/test/e2e/images_test.go @@ -41,8 +41,8 @@ var _ = Describe("Podman images", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 2)) - Expect(session.LineInOuputStartsWith("quay.io/libpod/alpine")).To(BeTrue()) - Expect(session.LineInOuputStartsWith("quay.io/libpod/busybox")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("quay.io/libpod/alpine")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("quay.io/libpod/busybox")).To(BeTrue()) }) It("podman image List", func() { @@ -50,8 +50,8 @@ var _ = Describe("Podman images", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 2)) - Expect(session.LineInOuputStartsWith("quay.io/libpod/alpine")).To(BeTrue()) - Expect(session.LineInOuputStartsWith("quay.io/libpod/busybox")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("quay.io/libpod/alpine")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("quay.io/libpod/busybox")).To(BeTrue()) }) It("podman images with multiple tags", func() { @@ -86,8 +86,8 @@ var _ = Describe("Podman images", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 2)) - Expect(session.LineInOuputStartsWith("quay.io/libpod/alpine")).To(BeTrue()) - Expect(session.LineInOuputStartsWith("quay.io/libpod/busybox")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("quay.io/libpod/alpine")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("quay.io/libpod/busybox")).To(BeTrue()) }) It("podman empty images list in JSON format", func() { @@ -278,7 +278,7 @@ WORKDIR /test It("podman images sort by values", func() { sortValueTest := func(value string, result int, format string) []string { f := fmt.Sprintf("{{.%s}}", format) - session := podmanTest.Podman([]string{"images", "--sort", value, "--format", f}) + session := podmanTest.Podman([]string{"images", "--noheading", "--sort", value, "--format", f}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(result)) diff --git a/test/e2e/import_test.go b/test/e2e/import_test.go index 1be4ef920..bd465bf38 100644 --- a/test/e2e/import_test.go +++ b/test/e2e/import_test.go @@ -87,7 +87,7 @@ var _ = Describe("Podman import", func() { results := podmanTest.Podman([]string{"history", "imported-image", "--format", "{{.Comment}}"}) results.WaitWithDefaultTimeout() Expect(results.ExitCode()).To(Equal(0)) - Expect(results.LineInOuputStartsWith("importing container test message")).To(BeTrue()) + Expect(results.LineInOutputStartsWith("importing container test message")).To(BeTrue()) }) It("podman import with change flag CMD=<path>", func() { diff --git a/test/e2e/network_create_test.go b/test/e2e/network_create_test.go index 043046c33..21b3074fc 100644 --- a/test/e2e/network_create_test.go +++ b/test/e2e/network_create_test.go @@ -329,4 +329,37 @@ var _ = Describe("Podman network create", func() { Expect(nc).To(ExitWithError()) }) + It("podman network create with mtu option", func() { + net := "mtu-test" + nc := podmanTest.Podman([]string{"network", "create", "--opt", "mtu=9000", net}) + nc.WaitWithDefaultTimeout() + Expect(nc.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(net) + + nc = podmanTest.Podman([]string{"network", "inspect", net}) + nc.WaitWithDefaultTimeout() + Expect(nc.ExitCode()).To(BeZero()) + Expect(nc.OutputToString()).To(ContainSubstring(`"mtu": 9000,`)) + }) + + It("podman network create with vlan option", func() { + net := "vlan-test" + nc := podmanTest.Podman([]string{"network", "create", "--opt", "vlan=9", net}) + nc.WaitWithDefaultTimeout() + Expect(nc.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(net) + + nc = podmanTest.Podman([]string{"network", "inspect", net}) + nc.WaitWithDefaultTimeout() + Expect(nc.ExitCode()).To(BeZero()) + Expect(nc.OutputToString()).To(ContainSubstring(`"vlan": 9`)) + }) + + It("podman network create with invalid option", func() { + net := "invalid-test" + nc := podmanTest.Podman([]string{"network", "create", "--opt", "foo=bar", net}) + nc.WaitWithDefaultTimeout() + Expect(nc).To(ExitWithError()) + }) + }) diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go index 20e1d5b6b..ffc914bc2 100644 --- a/test/e2e/network_test.go +++ b/test/e2e/network_test.go @@ -66,6 +66,65 @@ var _ = Describe("Podman network", func() { Expect(session.LineInOutputContains(name)).To(BeTrue()) }) + It("podman network list --filter plugin and name", func() { + name, path := generateNetworkConfig(podmanTest) + defer removeConf(path) + + session := podmanTest.Podman([]string{"network", "ls", "--filter", "plugin=bridge", "--filter", "name=" + name}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(name)) + }) + + It("podman network list --filter two names", func() { + name1, path1 := generateNetworkConfig(podmanTest) + defer removeConf(path1) + + name2, path2 := generateNetworkConfig(podmanTest) + defer removeConf(path2) + + session := podmanTest.Podman([]string{"network", "ls", "--filter", "name=" + name1, "--filter", "name=" + name2}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(name1)) + Expect(session.OutputToString()).To(ContainSubstring(name2)) + }) + + It("podman network list --filter labels", func() { + net1 := "labelnet" + stringid.GenerateNonCryptoID() + label1 := "testlabel1=abc" + label2 := "abcdef" + session := podmanTest.Podman([]string{"network", "create", "--label", label1, net1}) + session.WaitWithDefaultTimeout() + defer podmanTest.removeCNINetwork(net1) + Expect(session.ExitCode()).To(BeZero()) + + net2 := "labelnet" + stringid.GenerateNonCryptoID() + session = podmanTest.Podman([]string{"network", "create", "--label", label1, "--label", label2, net2}) + session.WaitWithDefaultTimeout() + defer podmanTest.removeCNINetwork(net2) + Expect(session.ExitCode()).To(BeZero()) + + session = podmanTest.Podman([]string{"network", "ls", "--filter", "label=" + label1}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(net1)) + Expect(session.OutputToString()).To(ContainSubstring(net2)) + + session = podmanTest.Podman([]string{"network", "ls", "--filter", "label=" + label1, "--filter", "label=" + label2}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).ToNot(ContainSubstring(net1)) + Expect(session.OutputToString()).To(ContainSubstring(net2)) + }) + + It("podman network list --filter invalid value", func() { + session := podmanTest.Podman([]string{"network", "ls", "--filter", "namr=ab"}) + session.WaitWithDefaultTimeout() + Expect(session).To(ExitWithError()) + Expect(session.ErrorToString()).To(ContainSubstring(`invalid filter "namr"`)) + }) + It("podman network list --filter failure", func() { name, path := generateNetworkConfig(podmanTest) defer removeConf(path) @@ -76,6 +135,40 @@ var _ = Describe("Podman network", func() { Expect(session.LineInOutputContains(name)).To(BeFalse()) }) + It("podman network ID test", func() { + net := "networkIDTest" + // the network id should be the sha256 hash of the network name + netID := "6073aefe03cdf8f29be5b23ea9795c431868a3a22066a6290b187691614fee84" + session := podmanTest.Podman([]string{"network", "create", net}) + session.WaitWithDefaultTimeout() + defer podmanTest.removeCNINetwork(net) + Expect(session.ExitCode()).To(BeZero()) + + session = podmanTest.Podman([]string{"network", "ls", "--format", "{{.Name}} {{.ID}}", "--filter", "id=" + netID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + Expect(session.OutputToString()).To(ContainSubstring(net + " " + netID[:12])) + + session = podmanTest.Podman([]string{"network", "ls", "--format", "{{.Name}} {{.ID}}", "--filter", "id=" + netID[10:50], "--no-trunc"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + Expect(session.OutputToString()).To(ContainSubstring(net + " " + netID)) + + session = podmanTest.Podman([]string{"network", "inspect", netID[:40]}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + Expect(session.OutputToString()).To(ContainSubstring(net)) + + session = podmanTest.Podman([]string{"network", "inspect", netID[1:]}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).ToNot(BeZero()) + Expect(session.ErrorToString()).To(ContainSubstring("no such network")) + + session = podmanTest.Podman([]string{"network", "rm", netID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + }) + rm_func := func(rm string) { It(fmt.Sprintf("podman network %s no args", rm), func() { session := podmanTest.Podman([]string{"network", rm}) diff --git a/test/e2e/port_test.go b/test/e2e/port_test.go index a3ce8bd69..4aaf2cbc1 100644 --- a/test/e2e/port_test.go +++ b/test/e2e/port_test.go @@ -61,7 +61,7 @@ var _ = Describe("Podman port", func() { result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) port := strings.Split(result.OutputToStringArray()[0], ":")[1] - Expect(result.LineInOuputStartsWith(fmt.Sprintf("80/tcp -> 0.0.0.0:%s", port))).To(BeTrue()) + Expect(result.LineInOutputStartsWith(fmt.Sprintf("80/tcp -> 0.0.0.0:%s", port))).To(BeTrue()) }) It("podman container port -l nginx", func() { @@ -79,7 +79,7 @@ var _ = Describe("Podman port", func() { result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) port := strings.Split(result.OutputToStringArray()[0], ":")[1] - Expect(result.LineInOuputStartsWith(fmt.Sprintf("80/tcp -> 0.0.0.0:%s", port))).To(BeTrue()) + Expect(result.LineInOutputStartsWith(fmt.Sprintf("80/tcp -> 0.0.0.0:%s", port))).To(BeTrue()) }) It("podman port -l port nginx", func() { @@ -97,7 +97,7 @@ var _ = Describe("Podman port", func() { result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) port := strings.Split(result.OutputToStringArray()[0], ":")[1] - Expect(result.LineInOuputStartsWith(fmt.Sprintf("0.0.0.0:%s", port))).To(BeTrue()) + Expect(result.LineInOutputStartsWith(fmt.Sprintf("0.0.0.0:%s", port))).To(BeTrue()) }) It("podman port -a nginx", func() { @@ -124,7 +124,7 @@ var _ = Describe("Podman port", func() { result := podmanTest.Podman([]string{"port", "portcheck"}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) - result.LineInOuputStartsWith("80/tcp -> 0.0.0.0:") + result.LineInOutputStartsWith("80/tcp -> 0.0.0.0:") }) It("podman port multiple ports", func() { @@ -142,12 +142,12 @@ var _ = Describe("Podman port", func() { result1 := podmanTest.Podman([]string{"port", "test", "5000"}) result1.WaitWithDefaultTimeout() Expect(result1.ExitCode()).To(BeZero()) - Expect(result1.LineInOuputStartsWith("0.0.0.0:5000")).To(BeTrue()) + Expect(result1.LineInOutputStartsWith("0.0.0.0:5000")).To(BeTrue()) // Check that the second port was honored result2 := podmanTest.Podman([]string{"port", "test", "5001"}) result2.WaitWithDefaultTimeout() Expect(result2.ExitCode()).To(BeZero()) - Expect(result2.LineInOuputStartsWith("0.0.0.0:5001")).To(BeTrue()) + Expect(result2.LineInOutputStartsWith("0.0.0.0:5001")).To(BeTrue()) }) }) diff --git a/test/e2e/pull_test.go b/test/e2e/pull_test.go index f1b055d6d..446e2bd38 100644 --- a/test/e2e/pull_test.go +++ b/test/e2e/pull_test.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path/filepath" + "runtime" "strings" . "github.com/containers/podman/v2/test/utils" @@ -384,7 +385,7 @@ var _ = Describe("Podman pull", func() { session := podmanTest.Podman([]string{"pull", "--all-tags", "k8s.gcr.io/pause"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.LineInOuputStartsWith("Pulled Images:")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("Pulled Images:")).To(BeTrue()) session = podmanTest.Podman([]string{"images"}) session.WaitWithDefaultTimeout() @@ -494,4 +495,31 @@ var _ = Describe("Podman pull", func() { Expect(data[0].ID).To(Equal(image1)) } }) + + It("podman pull --platform", func() { + session := podmanTest.Podman([]string{"pull", "--platform=linux/bogus", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + expectedError := "no image found in manifest list for architecture bogus" + Expect(session.ErrorToString()).To(ContainSubstring(expectedError)) + + session = podmanTest.Podman([]string{"pull", "--platform=linux/arm64", "--override-os", "windows", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + expectedError = "--platform option can not be specified with --overide-arch or --override-os" + Expect(session.ErrorToString()).To(ContainSubstring(expectedError)) + + session = podmanTest.Podman([]string{"pull", "-q", "--platform=linux/arm64", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + setup := podmanTest.Podman([]string{"image", "inspect", session.OutputToString()}) + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + data := setup.InspectImageJSON() // returns []inspect.ImageData + Expect(len(data)).To(Equal(1)) + Expect(data[0].Os).To(Equal(runtime.GOOS)) + Expect(data[0].Architecture).To(Equal("arm64")) + }) }) diff --git a/test/e2e/run_dns_test.go b/test/e2e/run_dns_test.go index ff018f5d8..c8996c5e8 100644 --- a/test/e2e/run_dns_test.go +++ b/test/e2e/run_dns_test.go @@ -36,14 +36,14 @@ var _ = Describe("Podman run dns", func() { session := podmanTest.Podman([]string{"run", "--dns-search=foobar.com", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session.LineInOuputStartsWith("search foobar.com") + session.LineInOutputStartsWith("search foobar.com") }) It("podman run remove all search domain", func() { session := podmanTest.Podman([]string{"run", "--dns-search=.", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.LineInOuputStartsWith("search")).To(BeFalse()) + Expect(session.LineInOutputStartsWith("search")).To(BeFalse()) }) It("podman run add bad dns server", func() { @@ -56,14 +56,14 @@ var _ = Describe("Podman run dns", func() { session := podmanTest.Podman([]string{"run", "--dns=1.2.3.4", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session.LineInOuputStartsWith("server 1.2.3.4") + session.LineInOutputStartsWith("server 1.2.3.4") }) It("podman run add dns option", func() { session := podmanTest.Podman([]string{"run", "--dns-opt=debug", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session.LineInOuputStartsWith("options debug") + session.LineInOutputStartsWith("options debug") }) It("podman run add bad host", func() { @@ -76,8 +76,8 @@ var _ = Describe("Podman run dns", func() { session := podmanTest.Podman([]string{"run", "--add-host=foobar:1.1.1.1", "--add-host=foobaz:2001:db8::68", ALPINE, "cat", "/etc/hosts"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session.LineInOuputStartsWith("1.1.1.1 foobar") - session.LineInOuputStartsWith("2001:db8::68 foobaz") + session.LineInOutputStartsWith("1.1.1.1 foobar") + session.LineInOutputStartsWith("2001:db8::68 foobaz") }) It("podman run add hostname", func() { diff --git a/test/e2e/run_entrypoint_test.go b/test/e2e/run_entrypoint_test.go index 2185d6b13..cac3d759d 100644 --- a/test/e2e/run_entrypoint_test.go +++ b/test/e2e/run_entrypoint_test.go @@ -99,12 +99,12 @@ ENTRYPOINT ["grep", "Alpine", "/etc/os-release"] session := podmanTest.Podman([]string{"run", "--entrypoint=uname", "foobar.com/entrypoint:latest"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.LineInOuputStartsWith("Linux")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("Linux")).To(BeTrue()) session = podmanTest.Podman([]string{"run", "--entrypoint", "", "foobar.com/entrypoint:latest", "uname"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.LineInOuputStartsWith("Linux")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("Linux")).To(BeTrue()) }) It("podman run user entrypoint with command overrides image entrypoint and image cmd", func() { @@ -116,6 +116,6 @@ ENTRYPOINT ["grep", "Alpine", "/etc/os-release"] session := podmanTest.Podman([]string{"run", "--entrypoint=uname", "foobar.com/entrypoint:latest", "-r"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.LineInOuputStartsWith("Linux")).To(BeFalse()) + Expect(session.LineInOutputStartsWith("Linux")).To(BeFalse()) }) }) diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index 3e80e953e..3fb00a28b 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -49,9 +49,28 @@ var _ = Describe("Podman run networking", func() { Expect(session.ExitCode()).To(Equal(0)) }) + It("podman run network connection with default", func() { + session := podmanTest.Podman([]string{"run", "--network", "default", ALPINE, "wget", "www.podman.io"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("podman run network connection with none", func() { + session := podmanTest.Podman([]string{"run", "--network", "none", ALPINE, "wget", "www.podman.io"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(1)) + Expect(session.ErrorToString()).To(ContainSubstring("wget: bad address 'www.podman.io'")) + }) + + It("podman run network connection with private", func() { + session := podmanTest.Podman([]string{"run", "--network", "private", ALPINE, "wget", "www.podman.io"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman run network connection with loopback", func() { - session := podmanTest.Podman([]string{"run", "-dt", "--network", "host", ALPINE, "wget", "www.podman.io"}) - session.Wait(90) + session := podmanTest.Podman([]string{"run", "--network", "host", ALPINE, "wget", "www.podman.io"}) + session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) }) diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 5831bb2f9..58ef9a647 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -233,6 +233,39 @@ var _ = Describe("Podman run", func() { return jsonFile } + It("podman run mask and unmask path test", func() { + session := podmanTest.Podman([]string{"run", "-d", "--name=maskCtr1", "--security-opt", "unmask=ALL", "--security-opt", "mask=/proc/acpi", ALPINE, "sleep", "200"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"exec", "maskCtr1", "ls", "/sys/firmware"}) + session.WaitWithDefaultTimeout() + Expect(session.OutputToString()).To(Not(BeEmpty())) + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"exec", "maskCtr1", "ls", "/proc/acpi"}) + session.WaitWithDefaultTimeout() + Expect(session.OutputToString()).To(BeEmpty()) + + session = podmanTest.Podman([]string{"run", "-d", "--name=maskCtr2", "--security-opt", "unmask=/proc/acpi:/sys/firmware", ALPINE, "sleep", "200"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"exec", "maskCtr2", "ls", "/sys/firmware"}) + session.WaitWithDefaultTimeout() + Expect(session.OutputToString()).To(Not(BeEmpty())) + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"exec", "maskCtr2", "ls", "/proc/acpi"}) + session.WaitWithDefaultTimeout() + Expect(session.OutputToString()).To(Not(BeEmpty())) + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"run", "-d", "--name=maskCtr3", "--security-opt", "mask=/sys/power/disk", ALPINE, "sleep", "200"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"exec", "maskCtr3", "cat", "/sys/power/disk"}) + session.WaitWithDefaultTimeout() + Expect(session.OutputToString()).To(BeEmpty()) + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman run seccomp test", func() { session := podmanTest.Podman([]string{"run", "-it", "--security-opt", strings.Join([]string{"seccomp=", forbidGetCWDSeccompProfile()}, ""), ALPINE, "pwd"}) session.WaitWithDefaultTimeout() diff --git a/test/system/010-images.bats b/test/system/010-images.bats index ee6da30ec..76caf282b 100644 --- a/test/system/010-images.bats +++ b/test/system/010-images.bats @@ -199,9 +199,16 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z local format=$2 run_podman images --sort repository --format "$format" - _check_line 0 ${aaa_name} ${aaa_tag} - _check_line 1 "${PODMAN_TEST_IMAGE_REGISTRY}/${PODMAN_TEST_IMAGE_USER}/${PODMAN_TEST_IMAGE_NAME}" "${PODMAN_TEST_IMAGE_TAG}" - _check_line 2 ${zzz_name} ${zzz_tag} + + line_no=0 + if [[ $format == table* ]]; then + # skip headers from table command + line_no=1 + fi + + _check_line $line_no ${aaa_name} ${aaa_tag} + _check_line $((line_no+1)) "${PODMAN_TEST_IMAGE_REGISTRY}/${PODMAN_TEST_IMAGE_USER}/${PODMAN_TEST_IMAGE_NAME}" "${PODMAN_TEST_IMAGE_TAG}" + _check_line $((line_no+2)) ${zzz_name} ${zzz_tag} } # Begin the test: tag $IMAGE with both the given names diff --git a/test/system/030-run.bats b/test/system/030-run.bats index 37695f205..3ee141f5f 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -536,6 +536,43 @@ json-file | f run_podman untag $IMAGE $newtag $newtag2 } +# Regression test for issue #8558 +@test "podman run on untagged image: make sure that image metadata is set" { + run_podman inspect $IMAGE --format "{{.ID}}" + imageID="$output" + + # prior to #8623 `podman run` would error out on untagged images with: + # Error: both RootfsImageName and RootfsImageID must be set if either is set: invalid argument + run_podman untag $IMAGE + run_podman run --rm $imageID ls + + run_podman tag $imageID $IMAGE +} + +@test "Verify /run/.containerenv exist" { + run_podman run --rm $IMAGE ls -1 /run/.containerenv + is "$output" "/run/.containerenv" + + run_podman run --privileged --rm $IMAGE sh -c '. /run/.containerenv; echo $engine' + is "$output" ".*podman.*" "failed to identify engine" + + run_podman run --privileged --name "testcontainerenv" --rm $IMAGE sh -c '. /run/.containerenv; echo $name' + is "$output" ".*testcontainerenv.*" + + run_podman run --privileged --rm $IMAGE sh -c '. /run/.containerenv; echo $image' + is "$output" ".*$IMAGE.*" "failed to idenitfy image" + + run_podman run --privileged --rm $IMAGE sh -c '. /run/.containerenv; echo $rootless' + # FIXME: on some CI systems, 'run --privileged' emits a spurious + # warning line about dup devices. Ignore it. + remove_same_dev_warning + if is_rootless; then + is "$output" "1" + else + is "$output" "0" + fi +} + @test "podman run with --net=host and --port prints warning" { rand=$(random_string 10) diff --git a/test/system/035-logs.bats b/test/system/035-logs.bats index a3d6a5800..a081a7ce1 100644 --- a/test/system/035-logs.bats +++ b/test/system/035-logs.bats @@ -21,6 +21,9 @@ load helpers run_podman logs $cid is "$output" "$rand_string" "output from podman-logs after container is run" + # test --since with Unix timestamps + run_podman logs --since 1000 $cid + run_podman rm $cid } diff --git a/test/system/065-cp.bats b/test/system/065-cp.bats index 6bf897790..43bdf217d 100644 --- a/test/system/065-cp.bats +++ b/test/system/065-cp.bats @@ -7,6 +7,290 @@ load helpers +@test "podman cp file from host to container" { + skip_if_remote "podman-remote does not yet handle cp" + + srcdir=$PODMAN_TMPDIR/cp-test-file-host-to-ctr + mkdir -p $srcdir + local -a randomcontent=( + random-0-$(random_string 10) + random-1-$(random_string 15) + random-2-$(random_string 20) + ) + echo "${randomcontent[0]}" > $srcdir/hostfile0 + echo "${randomcontent[1]}" > $srcdir/hostfile1 + echo "${randomcontent[2]}" > $srcdir/hostfile2 + + run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity + run_podman exec cpcontainer mkdir /srv/subdir + + # format is: <id> | <destination arg to cp> | <full dest path> | <test name> + # where: + # id is 0-2, one of the random strings/files + # dest arg is the right-hand argument to 'podman cp' (may be implicit) + # dest path is the full explicit path we expect to see + # test name is a short description of what we're testing here + tests=" +0 | / | /hostfile0 | copy to root +0 | /anotherbase.txt | /anotherbase.txt | copy to root, new name +0 | /tmp | /tmp/hostfile0 | copy to /tmp +1 | /tmp/ | /tmp/hostfile1 | copy to /tmp/ +2 | /tmp/. | /tmp/hostfile2 | copy to /tmp/. +0 | /tmp/hostfile2 | /tmp/hostfile2 | overwrite previous copy +0 | /tmp/anotherbase.txt | /tmp/anotherbase.txt | copy to /tmp, new name +0 | . | /srv/hostfile0 | copy to workdir (rel path), new name +1 | ./ | /srv/hostfile1 | copy to workdir (rel path), new name +0 | anotherbase.txt | /srv/anotherbase.txt | copy to workdir (rel path), new name +0 | subdir | /srv/subdir/hostfile0 | copy to workdir/subdir +" + + # Copy one of the files into container, exec+cat, confirm the file + # is there and matches what we expect + while read id dest dest_fullname description; do + run_podman cp $srcdir/hostfile$id cpcontainer:$dest + run_podman exec cpcontainer cat $dest_fullname + is "$output" "${randomcontent[$id]}" "$description (cp -> ctr:$dest)" + done < <(parse_table "$tests") + + # Host path does not exist. + run_podman 125 cp $srcdir/IdoNotExist cpcontainer:/tmp + is "$output" 'Error: ".*/IdoNotExist" could not be found on the host' \ + "copy nonexistent host path" + + # Container path does not exist. Notice that the error message shows how + # the specified container is resolved. + run_podman 125 cp $srcdir/hostfile0 cpcontainer:/IdoNotExist/ + is "$output" 'Error: "/IdoNotExist/" could not be found on container.*(resolved to .*/IdoNotExist.*' \ + "copy into nonexistent path in container" + + run_podman rm -f cpcontainer +} + + +@test "podman cp --extract=true tar archive to container" { + skip_if_remote "podman-remote does not yet handle cp" + + # Create tempfile with random name and content + dirname=cp-test-extract + srcdir=$PODMAN_TMPDIR/$dirname + mkdir -p $srcdir + rand_filename=$(random_string 20) + rand_content=$(random_string 50) + echo $rand_content > $srcdir/$rand_filename + chmod 644 $srcdir/$rand_filename + + # Now tar it up! + tar_file=$PODMAN_TMPDIR/archive.tar.gz + tar -C $PODMAN_TMPDIR -zvcf $tar_file $dirname + + run_podman run -d --name cpcontainer $IMAGE sleep infinity + + # First just copy without extracting the archive. + run_podman cp $tar_file cpcontainer:/tmp + # Now remove the archive which will also test if it exists and is a file. + # To save expensive exec'ing, create a file for the next tests. + run_podman exec cpcontainer sh -c "rm /tmp/archive.tar.gz; touch /tmp/file.txt" + + # Now copy with extracting the archive. NOTE that Podman should + # auto-decompress the file if needed. + run_podman cp --extract=true $tar_file cpcontainer:/tmp + run_podman exec cpcontainer cat /tmp/$dirname/$rand_filename + is "$output" "$rand_content" + + # Test extract on non archive. + run_podman cp --extract=true $srcdir/$rand_filename cpcontainer:/foo.txt + + # Cannot extract an archive to a file! + run_podman 125 cp --extract=true $tar_file cpcontainer:/tmp/file.txt + is "$output" 'Error: cannot extract archive .* to file "/tmp/file.txt"' + + run_podman rm -f cpcontainer +} + + +@test "podman cp file from container to host" { + skip_if_remote "podman-remote does not yet handle cp" + + srcdir=$PODMAN_TMPDIR/cp-test-file-ctr-to-host + mkdir -p $srcdir + + # Create 3 files with random content in the container. + local -a randomcontent=( + random-0-$(random_string 10) + random-1-$(random_string 15) + random-2-$(random_string 20) + ) + run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity + run_podman exec cpcontainer sh -c "echo ${randomcontent[0]} > /tmp/containerfile" + run_podman exec cpcontainer sh -c "echo ${randomcontent[1]} > /srv/containerfile1" + run_podman exec cpcontainer sh -c "mkdir /srv/subdir; echo ${randomcontent[2]} > /srv/subdir/containerfile2" + + # format is: <id> | <source arg to cp> | <destination arg (appended to $srcdir) to cp> | <full dest path (appended to $srcdir)> | <test name> + tests=" +0 | /tmp/containerfile | | /containerfile | copy to srcdir/ +0 | /tmp/containerfile | / | /containerfile | copy to srcdir/ +0 | /tmp/containerfile | /. | /containerfile | copy to srcdir/. +0 | /tmp/containerfile | /newfile | /newfile | copy to srcdir/newfile +1 | containerfile1 | / | /containerfile1 | copy from workdir (rel path) to srcdir +2 | subdir/containerfile2 | / | /containerfile2 | copy from workdir/subdir (rel path) to srcdir +" + + # Copy one of the files to the host, cat, confirm the file + # is there and matches what we expect + while read id src dest dest_fullname description; do + # dest may be "''" for empty table cells + if [[ $dest == "''" ]];then + unset dest + fi + run_podman cp cpcontainer:$src "$srcdir$dest" + run cat $srcdir$dest_fullname + is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to \$srcdir$dest)" + rm $srcdir/$dest_fullname + done < <(parse_table "$tests") + + run_podman rm -f cpcontainer +} + + +@test "podman cp dir from host to container" { + skip_if_remote "podman-remote does not yet handle cp" + + dirname=dir-test + srcdir=$PODMAN_TMPDIR/$dirname + mkdir -p $srcdir + local -a randomcontent=( + random-0-$(random_string 10) + random-1-$(random_string 15) + ) + echo "${randomcontent[0]}" > $srcdir/hostfile0 + echo "${randomcontent[1]}" > $srcdir/hostfile1 + + run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity + run_podman exec cpcontainer mkdir /srv/subdir + + # format is: <source arg to cp (appended to srcdir)> | <destination arg to cp> | <full dest path> | <test name> + tests=" + | / | /dir-test | copy to root + / | /tmp | /tmp/dir-test | copy to tmp + /. | /usr/ | /usr/ | copy contents of dir to usr/ + | . | /srv/dir-test | copy to workdir (rel path) + | subdir/. | /srv/subdir/dir-test | copy to workdir subdir (rel path) +" + + while read src dest dest_fullname description; do + # src may be "''" for empty table cells + if [[ $src == "''" ]];then + unset src + fi + run_podman cp $srcdir$src cpcontainer:$dest + run_podman exec cpcontainer ls $dest_fullname + run_podman exec cpcontainer cat $dest_fullname/hostfile0 + is "$output" "${randomcontent[0]}" "$description (cp -> ctr:$dest)" + run_podman exec cpcontainer cat $dest_fullname/hostfile1 + is "$output" "${randomcontent[1]}" "$description (cp -> ctr:$dest)" + done < <(parse_table "$tests") + + run_podman rm -f cpcontainer +} + + +@test "podman cp dir from container to host" { + skip_if_remote "podman-remote does not yet handle cp" + + srcdir=$PODMAN_TMPDIR/dir-test + mkdir -p $srcdir + + run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity + run_podman exec cpcontainer sh -c 'mkdir /srv/subdir; echo "This first file is on the container" > /srv/subdir/containerfile1' + run_podman exec cpcontainer sh -c 'echo "This second file is on the container as well" > /srv/subdir/containerfile2' + + run_podman cp cpcontainer:/srv $srcdir + run cat $srcdir/srv/subdir/containerfile1 + is "$output" "This first file is on the container" + run cat $srcdir/srv/subdir/containerfile2 + is "$output" "This second file is on the container as well" + rm -rf $srcdir/srv/subdir + + run_podman cp cpcontainer:/srv/. $srcdir + run ls $srcdir/subdir + run cat $srcdir/subdir/containerfile1 + is "$output" "This first file is on the container" + run cat $srcdir/subdir/containerfile2 + is "$output" "This second file is on the container as well" + rm -rf $srcdir/subdir + + run_podman cp cpcontainer:/srv/subdir/. $srcdir + run cat $srcdir/containerfile1 + is "$output" "This first file is on the container" + run cat $srcdir/containerfile2 + is "$output" "This second file is on the container as well" + + run_podman rm -f cpcontainer +} + + +@test "podman cp file from host to container volume" { + skip_if_remote "podman-remote does not yet handle cp" + + srcdir=$PODMAN_TMPDIR/cp-test-volume + mkdir -p $srcdir + echo "This file should be in volume2" > $srcdir/hostfile + volume1=$(random_string 20) + volume2=$(random_string 20) + + run_podman volume create $volume1 + run_podman volume inspect $volume1 --format "{{.Mountpoint}}" + volume1_mount="$output" + run_podman volume create $volume2 + run_podman volume inspect $volume2 --format "{{.Mountpoint}}" + volume2_mount="$output" + + # Create a container using the volume. Note that copying on not-running + # containers is allowed, so Podman has to analyze the container paths and + # check if they are hitting a volume, and eventually resolve to the path on + # the *host*. + # This test is extra tricky, as volume2 is mounted into a sub-directory of + # volume1. Podman must copy the file into volume2 and not volume1. + run_podman create --name cpcontainer -v $volume1:/tmp/volume -v $volume2:/tmp/volume/sub-volume $IMAGE + + run_podman cp $srcdir/hostfile cpcontainer:/tmp/volume/sub-volume + + run cat $volume2_mount/hostfile + is "$output" "This file should be in volume2" + + # Volume 1 must be empty. + run ls $volume1_mount + is "$output" "" + + run_podman rm -f cpcontainer + run_podman volume rm $volume1 $volume2 +} + + +@test "podman cp file from host to container mount" { + skip_if_remote "podman-remote does not yet handle cp" + + srcdir=$PODMAN_TMPDIR/cp-test-mount-src + mountdir=$PODMAN_TMPDIR/cp-test-mount + mkdir -p $srcdir $mountdir + echo "This file should be in the mount" > $srcdir/hostfile + + volume=$(random_string 20) + run_podman volume create $volume + + # Make it a bit more complex and put the mount on a volume. + run_podman create --name cpcontainer -v $volume:/tmp/volume -v $mountdir:/tmp/volume/mount $IMAGE + + run_podman cp $srcdir/hostfile cpcontainer:/tmp/volume/mount + + run cat $mountdir/hostfile + is "$output" "This file should be in the mount" + + run_podman rm -f cpcontainer + run_podman volume rm $volume +} + + # Create two random-name random-content files in /tmp in the container # podman-cp them into the host using '/tmp/*', i.e. asking podman to # perform wildcard expansion in the container. We should get both @@ -51,8 +335,7 @@ load helpers run_podman 125 cp 'cpcontainer:/tmp/*' $dstdir/ # FIXME: this might not be the exactly correct error message - is "$output" ".*error evaluating symlinks.*lstat.*no such file or dir" \ - "Expected error from copying invalid symlink" + is "$output" 'Error: "/tmp/\*" could not be found on container.*' # make sure there are no files in dstdir is "$(/bin/ls -1 $dstdir)" "" "incorrectly copied symlink from host" @@ -78,8 +361,7 @@ load helpers sh -c "ln -s $srcdir/hostfile file1;ln -s file\* copyme" run_podman 125 cp cpcontainer:copyme $dstdir - is "$output" ".*error evaluating symlinks.*lstat.*no such file or dir" \ - "Expected error from copying invalid symlink" + is "$output" 'Error: "copyme*" could not be found on container.*' # make sure there are no files in dstdir is "$(/bin/ls -1 $dstdir)" "" "incorrectly copied symlink from host" @@ -101,8 +383,7 @@ load helpers sh -c "ln -s $srcdir/hostfile /tmp/\*" run_podman 125 cp 'cpcontainer:/tmp/*' $dstdir - is "$output" ".*error evaluating symlinks.*lstat.*no such file or dir" \ - "Expected error from copying invalid symlink" + is "$output" 'Error: "/tmp/\*" could not be found on container.*' # dstdir must be empty is "$(/bin/ls -1 $dstdir)" "" "incorrectly copied symlink from host" @@ -110,8 +391,6 @@ load helpers run_podman rm cpcontainer } -############################################################################### -# cp INTO container # THIS IS EXTREMELY WEIRD. Podman expands symlinks in weird ways. @test "podman cp into container: weird symlink expansion" { @@ -148,7 +427,7 @@ load helpers is "$output" "" "output from podman cp 1" run_podman 125 cp --pause=false $srcdir/$rand_filename2 cpcontainer:/tmp/d2/x/ - is "$output" ".*stat.* no such file or directory" "cp will not create nonexistent destination directory" + is "$output" 'Error: "/tmp/d2/x/" could not be found on container.*' "cp will not create nonexistent destination directory" run_podman cp --pause=false $srcdir/$rand_filename3 cpcontainer:/tmp/d3/x is "$output" "" "output from podman cp 3" @@ -160,6 +439,7 @@ load helpers run_podman exec cpcontainer cat /tmp/nonesuch1 is "$output" "$rand_content1" "cp creates destination file" + # cp into nonexistent directory should not mkdir nonesuch2 directory run_podman 1 exec cpcontainer test -e /tmp/nonesuch2 @@ -168,8 +448,6 @@ load helpers is "$output" "$rand_content3" "cp creates file named x" run_podman rm -f cpcontainer - - } @@ -212,6 +490,103 @@ load helpers } +@test "podman cp from stdin to container" { + skip_if_remote "podman-remote does not yet handle cp" + + # Create tempfile with random name and content + srcdir=$PODMAN_TMPDIR/cp-test-stdin + mkdir -p $srcdir + rand_filename=$(random_string 20) + rand_content=$(random_string 50) + echo $rand_content > $srcdir/$rand_filename + chmod 644 $srcdir/$rand_filename + + # Now tar it up! + tar_file=$PODMAN_TMPDIR/archive.tar.gz + tar -zvcf $tar_file $srcdir + + run_podman run -d --name cpcontainer $IMAGE sleep infinity + + # NOTE: podman is supposed to auto-detect the gzip compression and + # decompress automatically. + # + # "-" will evaluate to "/dev/stdin" when used a source. + run_podman cp - cpcontainer:/tmp < $tar_file + run_podman exec cpcontainer cat /tmp/$srcdir/$rand_filename + is "$output" "$rand_content" + run_podman exec cpcontainer rm -rf /tmp/$srcdir + + # Now for "/dev/stdin". + run_podman cp /dev/stdin cpcontainer:/tmp < $tar_file + run_podman exec cpcontainer cat /tmp/$srcdir/$rand_filename + is "$output" "$rand_content" + + # Error checks below ... + + # Input stream must be a (compressed) tar archive. + run_podman 125 cp - cpcontainer:/tmp < $srcdir/$rand_filename + is "$output" "Error:.*: error reading tar stream.*" "input stream must be a (compressed) tar archive" + + # Destination must be a directory (on an existing file). + run_podman exec cpcontainer touch /tmp/file.txt + run_podman 125 cp /dev/stdin cpcontainer:/tmp/file.txt < $tar_file + is "$output" 'Error: destination must be a directory or stream when copying from a stream' + + # Destination must be a directory (on an absent path). + run_podman 125 cp /dev/stdin cpcontainer:/tmp/IdoNotExist < $tar_file + is "$output" 'Error: destination must be a directory or stream when copying from a stream' + + run_podman rm -f cpcontainer +} + + +@test "podman cp from container to stdout" { + skip_if_remote "podman-remote does not yet handle cp" + + srcdir=$PODMAN_TMPDIR/cp-test-stdout + mkdir -p $srcdir + rand_content=$(random_string 50) + + run_podman run -d --name cpcontainer $IMAGE sleep infinity + + run_podman exec cpcontainer sh -c "echo '$rand_content' > /tmp/file.txt" + run_podman exec cpcontainer touch /tmp/empty.txt + + # Copying from stdout will always compress. So let's copy the previously + # created file from the container via stdout, untar the archive and make + # sure the file exists with the expected content. + # + # NOTE that we can't use run_podman because that uses the BATS 'run' + # function which redirects stdout and stderr. Here we need to guarantee + # that podman's stdout is a pipe, not any other form of redirection. + + # Copy file. + $PODMAN cp cpcontainer:/tmp/file.txt - > $srcdir/stdout.tar + if [ $? -ne 0 ]; then + die "Command failed: podman cp ... - | cat" + fi + + tar xvf $srcdir/stdout.tar -C $srcdir + run cat $srcdir/file.txt + is "$output" "$rand_content" + run 1 ls $srcfir/empty.txt + rm -f $srcdir/* + + # Copy directory. + $PODMAN cp cpcontainer:/tmp - > $srcdir/stdout.tar + if [ $? -ne 0 ]; then + die "Command failed: podman cp ... - | cat : $output" + fi + + tar xvf $srcdir/stdout.tar -C $srcdir + run cat $srcdir/file.txt + is "$output" "$rand_content" + run cat $srcdir/empty.txt + is "$output" "" + + run_podman rm -f cpcontainer +} + function teardown() { # In case any test fails, clean up the container we left behind run_podman rm -f cpcontainer diff --git a/test/system/120-load.bats b/test/system/120-load.bats index 8ea9b1c69..272e2ae93 100644 --- a/test/system/120-load.bats +++ b/test/system/120-load.bats @@ -28,12 +28,15 @@ verify_iid_and_name() { @test "podman save to pipe and load" { # Generate a random name and tag (must be lower-case) - local random_name=x$(random_string 12 | tr A-Z a-z) - local random_tag=t$(random_string 7 | tr A-Z a-z) + local random_name=x0$(random_string 12 | tr A-Z a-z) + local random_tag=t0$(random_string 7 | tr A-Z a-z) local fqin=localhost/$random_name:$random_tag run_podman tag $IMAGE $fqin - archive=$PODMAN_TMPDIR/myimage-$(random_string 8).tar + # Believe it or not, 'podman load' would barf if any path element + # included a capital letter + archive=$PODMAN_TMPDIR/MySubDirWithCaps/MyImage-$(random_string 8).tar + mkdir -p $(dirname $archive) # We can't use run_podman because that uses the BATS 'run' function # which redirects stdout and stderr. Here we need to guarantee @@ -51,19 +54,20 @@ verify_iid_and_name() { run_podman images $fqin --format '{{.Repository}}:{{.Tag}}' is "$output" "$fqin" "image preserves name across save/load" - # FIXME: when/if 7337 gets fixed, load with a new tag - if false; then - local new_name=x$(random_string 14 | tr A-Z a-z) - local new_tag=t$(random_string 6 | tr A-Z a-z) + # Load with a new tag + local new_name=x1$(random_string 14 | tr A-Z a-z) + local new_tag=t1$(random_string 6 | tr A-Z a-z) run_podman rmi $fqin - fqin=localhost/$new_name:$new_tag - run_podman load -i $archive $fqin - run_podman images $fqin --format '{{.Repository}}:{{.Tag}}' - is "$output" "$fqin" "image can be loaded with new name:tag" - fi + + new_fqin=localhost/$new_name:$new_tag + run_podman load -i $archive $new_fqin + run_podman images --format '{{.Repository}}:{{.Tag}}' --sort tag + is "${lines[0]}" "$IMAGE" "image is preserved" + is "${lines[1]}" "$fqin" "image is reloaded with old fqin" + is "${lines[2]}" "$new_fqin" "image is reloaded with new fqin too" # Clean up - run_podman rmi $fqin + run_podman rmi $fqin $new_fqin } diff --git a/test/system/400-unprivileged-access.bats b/test/system/400-unprivileged-access.bats index 142d7dcd9..20fdd068f 100644 --- a/test/system/400-unprivileged-access.bats +++ b/test/system/400-unprivileged-access.bats @@ -118,7 +118,7 @@ EOF /proc/scsi /sys/firmware /sys/fs/selinux - /sys/dev + /sys/dev/block ) # Some of the above may not exist on our host. Find only the ones that do. diff --git a/test/utils/podmansession_test.go b/test/utils/podmansession_test.go index d0e2f3d06..763cb4f26 100644 --- a/test/utils/podmansession_test.go +++ b/test/utils/podmansession_test.go @@ -53,8 +53,8 @@ var _ = Describe("PodmanSession test", func() { }) It("Test LineInOutputStartsWith", func() { - Expect(session.LineInOuputStartsWith("Podman")).To(BeTrue()) - Expect(session.LineInOuputStartsWith("Session")).To(Not(BeTrue())) + Expect(session.LineInOutputStartsWith("Podman")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("Session")).To(Not(BeTrue())) }) It("Test LineInOutputContains", func() { diff --git a/test/utils/utils.go b/test/utils/utils.go index dd836f258..d08939678 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -277,7 +277,7 @@ func (s *PodmanSession) ErrorGrepString(term string) (bool, []string) { // LineInOutputStartsWith returns true if a line in a // session output starts with the supplied string -func (s *PodmanSession) LineInOuputStartsWith(term string) bool { +func (s *PodmanSession) LineInOutputStartsWith(term string) bool { for _, i := range s.OutputToStringArray() { if strings.HasPrefix(i, term) { return true |