mirror of https://github.com/containers/podman.git
Merge pull request #8204 from jwhonce/jira/run-976
Add test/apiv2/rest_api tests to make target
This commit is contained in:
commit
1e83aea9e6
1
Makefile
1
Makefile
|
@ -357,6 +357,7 @@ remotesystem:
|
||||||
.PHONY: localapiv2
|
.PHONY: localapiv2
|
||||||
localapiv2:
|
localapiv2:
|
||||||
env PODMAN=./bin/podman ./test/apiv2/test-apiv2
|
env PODMAN=./bin/podman ./test/apiv2/test-apiv2
|
||||||
|
env PODMAN=./bin/podman ${PYTHON} -m unittest discover -v ./test/apiv2/rest_api/
|
||||||
|
|
||||||
.PHONY: remoteapiv2
|
.PHONY: remoteapiv2
|
||||||
remoteapiv2:
|
remoteapiv2:
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
import configparser
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
|
||||||
|
class Podman(object):
|
||||||
|
"""
|
||||||
|
Instances hold the configuration and setup for running podman commands
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Initialize a Podman instance with global options"""
|
||||||
|
binary = os.getenv("PODMAN", "bin/podman")
|
||||||
|
self.cmd = [binary, "--storage-driver=vfs"]
|
||||||
|
|
||||||
|
cgroupfs = os.getenv("CGROUP_MANAGER", "cgroupfs")
|
||||||
|
self.cmd.append(f"--cgroup-manager={cgroupfs}")
|
||||||
|
|
||||||
|
if os.getenv("DEBUG"):
|
||||||
|
self.cmd.append("--log-level=debug")
|
||||||
|
|
||||||
|
self.anchor_directory = tempfile.mkdtemp(prefix="podman_restapi_")
|
||||||
|
self.cmd.append("--root=" + os.path.join(self.anchor_directory, "crio"))
|
||||||
|
self.cmd.append("--runroot=" + os.path.join(self.anchor_directory, "crio-run"))
|
||||||
|
|
||||||
|
os.environ["REGISTRIES_CONFIG_PATH"] = os.path.join(
|
||||||
|
self.anchor_directory, "registry.conf"
|
||||||
|
)
|
||||||
|
p = configparser.ConfigParser()
|
||||||
|
p.read_dict(
|
||||||
|
{
|
||||||
|
"registries.search": {"registries": "['docker.io']"},
|
||||||
|
"registries.insecure": {"registries": "[]"},
|
||||||
|
"registries.block": {"registries": "[]"},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
with open(os.environ["REGISTRIES_CONFIG_PATH"], "w") as w:
|
||||||
|
p.write(w)
|
||||||
|
|
||||||
|
os.environ["CNI_CONFIG_PATH"] = os.path.join(
|
||||||
|
self.anchor_directory, "cni", "net.d"
|
||||||
|
)
|
||||||
|
os.makedirs(os.environ["CNI_CONFIG_PATH"], exist_ok=True)
|
||||||
|
self.cmd.append("--cni-config-dir=" + os.environ["CNI_CONFIG_PATH"])
|
||||||
|
cni_cfg = os.path.join(
|
||||||
|
os.environ["CNI_CONFIG_PATH"], "87-podman-bridge.conflist"
|
||||||
|
)
|
||||||
|
# json decoded and encoded to ensure legal json
|
||||||
|
buf = json.loads(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"cniVersion": "0.3.0",
|
||||||
|
"name": "podman",
|
||||||
|
"plugins": [{
|
||||||
|
"type": "bridge",
|
||||||
|
"bridge": "cni0",
|
||||||
|
"isGateway": true,
|
||||||
|
"ipMasq": true,
|
||||||
|
"ipam": {
|
||||||
|
"type": "host-local",
|
||||||
|
"subnet": "10.88.0.0/16",
|
||||||
|
"routes": [{
|
||||||
|
"dst": "0.0.0.0/0"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "portmap",
|
||||||
|
"capabilities": {
|
||||||
|
"portMappings": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
with open(cni_cfg, "w") as w:
|
||||||
|
json.dump(buf, w)
|
||||||
|
|
||||||
|
def open(self, command, *args, **kwargs):
|
||||||
|
"""Podman initialized instance to run a given command
|
||||||
|
|
||||||
|
:param self: Podman instance
|
||||||
|
:param command: podman sub-command to run
|
||||||
|
:param args: arguments and options for command
|
||||||
|
:param kwargs: See subprocess.Popen() for shell keyword
|
||||||
|
:return: subprocess.Popen() instance configured to run podman instance
|
||||||
|
"""
|
||||||
|
cmd = self.cmd.copy()
|
||||||
|
cmd.append(command)
|
||||||
|
cmd.extend(args)
|
||||||
|
|
||||||
|
shell = kwargs.get("shell", False)
|
||||||
|
|
||||||
|
return subprocess.Popen(
|
||||||
|
cmd,
|
||||||
|
shell=shell,
|
||||||
|
stdin=subprocess.DEVNULL,
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self, command, *args, **kwargs):
|
||||||
|
"""Podman initialized instance to run a given command
|
||||||
|
|
||||||
|
:param self: Podman instance
|
||||||
|
:param command: podman sub-command to run
|
||||||
|
:param args: arguments and options for command
|
||||||
|
:param kwargs: See subprocess.Popen() for shell and check keywords
|
||||||
|
:return: subprocess.Popen() instance configured to run podman instance
|
||||||
|
"""
|
||||||
|
cmd = self.cmd.copy()
|
||||||
|
cmd.append(command)
|
||||||
|
cmd.extend(args)
|
||||||
|
|
||||||
|
check = kwargs.get("check", False)
|
||||||
|
shell = kwargs.get("shell", False)
|
||||||
|
|
||||||
|
return subprocess.run(
|
||||||
|
cmd,
|
||||||
|
shell=shell,
|
||||||
|
check=check,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
)
|
||||||
|
|
||||||
|
def tear_down(self):
|
||||||
|
shutil.rmtree(self.anchor_directory, ignore_errors=True)
|
|
@ -1,5 +1,4 @@
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
@ -9,27 +8,25 @@ from multiprocessing import Process
|
||||||
import requests
|
import requests
|
||||||
from dateutil.parser import parse
|
from dateutil.parser import parse
|
||||||
|
|
||||||
|
from test.apiv2.rest_api import Podman
|
||||||
|
|
||||||
PODMAN_URL = "http://localhost:8080"
|
PODMAN_URL = "http://localhost:8080"
|
||||||
|
|
||||||
|
|
||||||
def _url(path):
|
def _url(path):
|
||||||
return PODMAN_URL + "/v1.0.0/libpod" + path
|
return PODMAN_URL + "/v2.0.0/libpod" + path
|
||||||
|
|
||||||
|
|
||||||
def podman():
|
|
||||||
binary = os.getenv("PODMAN_BINARY")
|
|
||||||
if binary is None:
|
|
||||||
binary = "bin/podman"
|
|
||||||
return binary
|
|
||||||
|
|
||||||
|
|
||||||
def ctnr(path):
|
def ctnr(path):
|
||||||
r = requests.get(_url("/containers/json?all=true"))
|
|
||||||
try:
|
try:
|
||||||
|
r = requests.get(_url("/containers/json?all=true"))
|
||||||
ctnrs = json.loads(r.text)
|
ctnrs = json.loads(r.text)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
sys.stderr.write("Bad container response: {}/{}".format(r.text, e))
|
msg = f"Bad container response: {e}"
|
||||||
raise e
|
if r is not None:
|
||||||
|
msg = msg + " " + r.text
|
||||||
|
sys.stderr.write(msg + "\n")
|
||||||
|
raise
|
||||||
return path.format(ctnrs[0]["Id"])
|
return path.format(ctnrs[0]["Id"])
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,50 +41,50 @@ def validateObjectFields(buffer):
|
||||||
|
|
||||||
|
|
||||||
class TestApi(unittest.TestCase):
|
class TestApi(unittest.TestCase):
|
||||||
podman = None
|
podman = None # initialized podman configuration for tests
|
||||||
|
service = None # podman service instance
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
if TestApi.podman.poll() is not None:
|
|
||||||
sys.stderr.write(f"podman service returned {TestApi.podman.returncode}\n")
|
try:
|
||||||
sys.exit(2)
|
TestApi.podman.run("run", "alpine", "/bin/ls", check=True)
|
||||||
requests.get(
|
except subprocess.CalledProcessError as e:
|
||||||
_url("/images/create?fromSrc=docker.io%2Falpine%3Alatest"))
|
if e.stdout:
|
||||||
# calling out to podman is easier than the API for running a container
|
sys.stdout.write("\nRun Stdout:\n" + e.stdout.decode("utf-8"))
|
||||||
subprocess.run([podman(), "run", "alpine", "/bin/ls"],
|
if e.stderr:
|
||||||
check=True,
|
sys.stderr.write("\nRun Stderr:\n" + e.stderr.decode("utf-8"))
|
||||||
stdout=subprocess.DEVNULL,
|
raise
|
||||||
stderr=subprocess.DEVNULL)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
super().setUpClass()
|
super().setUpClass()
|
||||||
|
|
||||||
TestApi.podman = subprocess.Popen(
|
TestApi.podman = Podman()
|
||||||
[
|
TestApi.service = TestApi.podman.open(
|
||||||
podman(), "system", "service", "tcp:localhost:8080",
|
"system", "service", "tcp:localhost:8080", "--log-level=debug", "--time=0"
|
||||||
"--log-level=debug", "--time=0"
|
|
||||||
],
|
|
||||||
shell=False,
|
|
||||||
stdin=subprocess.DEVNULL,
|
|
||||||
stdout=subprocess.DEVNULL,
|
|
||||||
stderr=subprocess.DEVNULL,
|
|
||||||
)
|
)
|
||||||
|
# give the service some time to be ready...
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
|
returncode = TestApi.service.poll()
|
||||||
|
if returncode is not None:
|
||||||
|
raise subprocess.CalledProcessError(returncode, "podman system service")
|
||||||
|
|
||||||
|
r = requests.post(_url("/images/pull?reference=docker.io%2Falpine%3Alatest"))
|
||||||
|
if r.status_code != 200:
|
||||||
|
raise subprocess.CalledProcessError(
|
||||||
|
r.status_code, f"podman images pull docker.io/alpine:latest {r.text}"
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tearDownClass(cls):
|
def tearDownClass(cls):
|
||||||
TestApi.podman.terminate()
|
TestApi.service.terminate()
|
||||||
stdout, stderr = TestApi.podman.communicate(timeout=0.5)
|
stdout, stderr = TestApi.service.communicate(timeout=0.5)
|
||||||
if stdout:
|
if stdout:
|
||||||
print("\nService Stdout:\n" + stdout.decode('utf-8'))
|
sys.stdout.write("\nService Stdout:\n" + stdout.decode("utf-8"))
|
||||||
if stderr:
|
if stderr:
|
||||||
print("\nService Stderr:\n" + stderr.decode('utf-8'))
|
sys.stderr.write("\nService Stderr:\n" + stderr.decode("utf-8"))
|
||||||
|
|
||||||
if TestApi.podman.returncode > 0:
|
|
||||||
sys.stderr.write(f"podman exited with error code {TestApi.podman.returncode}\n")
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
return super().tearDownClass()
|
return super().tearDownClass()
|
||||||
|
|
||||||
def test_info(self):
|
def test_info(self):
|
||||||
|
@ -160,6 +157,7 @@ class TestApi(unittest.TestCase):
|
||||||
self.assertIsNone(r.text)
|
self.assertIsNone(r.text)
|
||||||
|
|
||||||
def test_attach_containers(self):
|
def test_attach_containers(self):
|
||||||
|
self.skipTest("FIXME: Test timeouts")
|
||||||
r = requests.post(_url(ctnr("/containers/{}/attach")), timeout=5)
|
r = requests.post(_url(ctnr("/containers/{}/attach")), timeout=5)
|
||||||
self.assertIn(r.status_code, (101, 500), r.text)
|
self.assertIn(r.status_code, (101, 500), r.text)
|
||||||
|
|
||||||
|
@ -242,5 +240,5 @@ class TestApi(unittest.TestCase):
|
||||||
self.assertEqual(r.status_code, 200, r.text)
|
self.assertEqual(r.status_code, 200, r.text)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -43,16 +43,16 @@ class TestApi(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
if TestApi.podman.poll() is not None:
|
if TestApi.podman.poll() is not None:
|
||||||
sys.stderr.write("podman service returned {}",
|
sys.stderr.write("podman service returned {}", TestApi.podman.returncode)
|
||||||
TestApi.podman.returncode)
|
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
requests.get(
|
requests.get(_url("/images/create?fromSrc=docker.io%2Falpine%3Alatest"))
|
||||||
_url("/images/create?fromSrc=docker.io%2Falpine%3Alatest"))
|
|
||||||
# calling out to podman is easier than the API for running a container
|
# calling out to podman is easier than the API for running a container
|
||||||
subprocess.run([podman(), "run", "alpine", "/bin/ls"],
|
subprocess.run(
|
||||||
check=True,
|
[podman(), "run", "alpine", "/bin/ls"],
|
||||||
stdout=subprocess.DEVNULL,
|
check=True,
|
||||||
stderr=subprocess.DEVNULL)
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
|
@ -60,8 +60,12 @@ class TestApi(unittest.TestCase):
|
||||||
|
|
||||||
TestApi.podman = subprocess.Popen(
|
TestApi.podman = subprocess.Popen(
|
||||||
[
|
[
|
||||||
podman(), "system", "service", "tcp:localhost:8080",
|
podman(),
|
||||||
"--log-level=debug", "--time=0"
|
"system",
|
||||||
|
"service",
|
||||||
|
"tcp:localhost:8080",
|
||||||
|
"--log-level=debug",
|
||||||
|
"--time=0",
|
||||||
],
|
],
|
||||||
shell=False,
|
shell=False,
|
||||||
stdin=subprocess.DEVNULL,
|
stdin=subprocess.DEVNULL,
|
||||||
|
@ -75,13 +79,14 @@ class TestApi(unittest.TestCase):
|
||||||
TestApi.podman.terminate()
|
TestApi.podman.terminate()
|
||||||
stdout, stderr = TestApi.podman.communicate(timeout=0.5)
|
stdout, stderr = TestApi.podman.communicate(timeout=0.5)
|
||||||
if stdout:
|
if stdout:
|
||||||
print("\nService Stdout:\n" + stdout.decode('utf-8'))
|
print("\nService Stdout:\n" + stdout.decode("utf-8"))
|
||||||
if stderr:
|
if stderr:
|
||||||
print("\nService Stderr:\n" + stderr.decode('utf-8'))
|
print("\nService Stderr:\n" + stderr.decode("utf-8"))
|
||||||
|
|
||||||
if TestApi.podman.returncode > 0:
|
if TestApi.podman.returncode > 0:
|
||||||
sys.stderr.write("podman exited with error code {}\n".format(
|
sys.stderr.write(
|
||||||
TestApi.podman.returncode))
|
"podman exited with error code {}\n".format(TestApi.podman.returncode)
|
||||||
|
)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
return super().tearDownClass()
|
return super().tearDownClass()
|
||||||
|
@ -222,13 +227,14 @@ class TestApi(unittest.TestCase):
|
||||||
|
|
||||||
|
|
||||||
def validateObjectFields(self, buffer):
|
def validateObjectFields(self, buffer):
|
||||||
objs = json.loads(buffer)
|
objs = json.loads(buffer)
|
||||||
if not isinstance(objs, dict):
|
if not isinstance(objs, dict):
|
||||||
for o in objs:
|
for o in objs:
|
||||||
_ = o["Id"]
|
_ = o["Id"]
|
||||||
else:
|
else:
|
||||||
_ = objs["Id"]
|
_ = objs["Id"]
|
||||||
return objs
|
return objs
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
Loading…
Reference in New Issue