mirror of https://github.com/docker/docker-py.git
commit
b22095f742
|
|
@ -31,7 +31,7 @@ def buildImages = { ->
|
||||||
}
|
}
|
||||||
|
|
||||||
def getDockerVersions = { ->
|
def getDockerVersions = { ->
|
||||||
def dockerVersions = ["17.06.2-ce"]
|
def dockerVersions = ["19.03.5"]
|
||||||
wrappedNode(label: "ubuntu && !zfs && amd64") {
|
wrappedNode(label: "ubuntu && !zfs && amd64") {
|
||||||
def result = sh(script: """docker run --rm \\
|
def result = sh(script: """docker run --rm \\
|
||||||
--entrypoint=python \\
|
--entrypoint=python \\
|
||||||
|
|
@ -46,8 +46,6 @@ def getDockerVersions = { ->
|
||||||
|
|
||||||
def getAPIVersion = { engineVersion ->
|
def getAPIVersion = { engineVersion ->
|
||||||
def versionMap = [
|
def versionMap = [
|
||||||
'17.06': '1.30',
|
|
||||||
'18.03': '1.37',
|
|
||||||
'18.09': '1.39',
|
'18.09': '1.39',
|
||||||
'19.03': '1.40'
|
'19.03': '1.40'
|
||||||
]
|
]
|
||||||
|
|
@ -84,7 +82,7 @@ def runTests = { Map settings ->
|
||||||
try {
|
try {
|
||||||
sh """docker network create ${testNetwork}"""
|
sh """docker network create ${testNetwork}"""
|
||||||
sh """docker run -d --name ${dindContainerName} -v /tmp --privileged --network ${testNetwork} \\
|
sh """docker run -d --name ${dindContainerName} -v /tmp --privileged --network ${testNetwork} \\
|
||||||
dockerswarm/dind:${dockerVersion} dockerd -H tcp://0.0.0.0:2375
|
docker:${dockerVersion}-dind dockerd -H tcp://0.0.0.0:2375
|
||||||
"""
|
"""
|
||||||
sh """docker run \\
|
sh """docker run \\
|
||||||
--name ${testContainerName} \\
|
--name ${testContainerName} \\
|
||||||
|
|
|
||||||
8
Makefile
8
Makefile
|
|
@ -42,7 +42,7 @@ integration-test-py3: build-py3
|
||||||
docker run -t --rm -v /var/run/docker.sock:/var/run/docker.sock docker-sdk-python3 py.test -v tests/integration/${file}
|
docker run -t --rm -v /var/run/docker.sock:/var/run/docker.sock docker-sdk-python3 py.test -v tests/integration/${file}
|
||||||
|
|
||||||
TEST_API_VERSION ?= 1.35
|
TEST_API_VERSION ?= 1.35
|
||||||
TEST_ENGINE_VERSION ?= 18.09.5
|
TEST_ENGINE_VERSION ?= 19.03.5
|
||||||
|
|
||||||
.PHONY: setup-network
|
.PHONY: setup-network
|
||||||
setup-network:
|
setup-network:
|
||||||
|
|
@ -55,7 +55,7 @@ integration-dind: integration-dind-py2 integration-dind-py3
|
||||||
integration-dind-py2: build setup-network
|
integration-dind-py2: build setup-network
|
||||||
docker rm -vf dpy-dind-py2 || :
|
docker rm -vf dpy-dind-py2 || :
|
||||||
docker run -d --network dpy-tests --name dpy-dind-py2 --privileged\
|
docker run -d --network dpy-tests --name dpy-dind-py2 --privileged\
|
||||||
dockerswarm/dind:${TEST_ENGINE_VERSION} dockerd -H tcp://0.0.0.0:2375 --experimental
|
docker:${TEST_ENGINE_VERSION}-dind dockerd -H tcp://0.0.0.0:2375 --experimental
|
||||||
docker run -t --rm --env="DOCKER_HOST=tcp://dpy-dind-py2:2375" --env="DOCKER_TEST_API_VERSION=${TEST_API_VERSION}"\
|
docker run -t --rm --env="DOCKER_HOST=tcp://dpy-dind-py2:2375" --env="DOCKER_TEST_API_VERSION=${TEST_API_VERSION}"\
|
||||||
--network dpy-tests docker-sdk-python py.test tests/integration
|
--network dpy-tests docker-sdk-python py.test tests/integration
|
||||||
docker rm -vf dpy-dind-py2
|
docker rm -vf dpy-dind-py2
|
||||||
|
|
@ -64,7 +64,7 @@ integration-dind-py2: build setup-network
|
||||||
integration-dind-py3: build-py3 setup-network
|
integration-dind-py3: build-py3 setup-network
|
||||||
docker rm -vf dpy-dind-py3 || :
|
docker rm -vf dpy-dind-py3 || :
|
||||||
docker run -d --network dpy-tests --name dpy-dind-py3 --privileged\
|
docker run -d --network dpy-tests --name dpy-dind-py3 --privileged\
|
||||||
dockerswarm/dind:${TEST_ENGINE_VERSION} dockerd -H tcp://0.0.0.0:2375 --experimental
|
docker:${TEST_ENGINE_VERSION}-dind dockerd -H tcp://0.0.0.0:2375 --experimental
|
||||||
docker run -t --rm --env="DOCKER_HOST=tcp://dpy-dind-py3:2375" --env="DOCKER_TEST_API_VERSION=${TEST_API_VERSION}"\
|
docker run -t --rm --env="DOCKER_HOST=tcp://dpy-dind-py3:2375" --env="DOCKER_TEST_API_VERSION=${TEST_API_VERSION}"\
|
||||||
--network dpy-tests docker-sdk-python3 py.test tests/integration
|
--network dpy-tests docker-sdk-python3 py.test tests/integration
|
||||||
docker rm -vf dpy-dind-py3
|
docker rm -vf dpy-dind-py3
|
||||||
|
|
@ -76,7 +76,7 @@ integration-dind-ssl: build-dind-certs build build-py3
|
||||||
docker run -d --env="DOCKER_HOST=tcp://localhost:2375" --env="DOCKER_TLS_VERIFY=1"\
|
docker run -d --env="DOCKER_HOST=tcp://localhost:2375" --env="DOCKER_TLS_VERIFY=1"\
|
||||||
--env="DOCKER_CERT_PATH=/certs" --volumes-from dpy-dind-certs --name dpy-dind-ssl\
|
--env="DOCKER_CERT_PATH=/certs" --volumes-from dpy-dind-certs --name dpy-dind-ssl\
|
||||||
--network dpy-tests --network-alias docker -v /tmp --privileged\
|
--network dpy-tests --network-alias docker -v /tmp --privileged\
|
||||||
dockerswarm/dind:${TEST_ENGINE_VERSION}\
|
docker:${TEST_ENGINE_VERSION}-dind\
|
||||||
dockerd --tlsverify --tlscacert=/certs/ca.pem --tlscert=/certs/server-cert.pem\
|
dockerd --tlsverify --tlscacert=/certs/ca.pem --tlscert=/certs/server-cert.pem\
|
||||||
--tlskey=/certs/server-key.pem -H tcp://0.0.0.0:2375 --experimental
|
--tlskey=/certs/server-key.pem -H tcp://0.0.0.0:2375 --experimental
|
||||||
docker run -t --rm --volumes-from dpy-dind-ssl --env="DOCKER_HOST=tcp://docker:2375"\
|
docker run -t --rm --volumes-from dpy-dind-ssl --env="DOCKER_HOST=tcp://docker:2375"\
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,11 @@ class ContextAPI(object):
|
||||||
Contains methods for context management:
|
Contains methods for context management:
|
||||||
create, list, remove, get, inspect.
|
create, list, remove, get, inspect.
|
||||||
"""
|
"""
|
||||||
DEFAULT_CONTEXT = Context("default")
|
DEFAULT_CONTEXT = Context("default", "swarm")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_context(
|
def create_context(
|
||||||
cls, name, orchestrator="swarm", host=None, tls_cfg=None,
|
cls, name, orchestrator=None, host=None, tls_cfg=None,
|
||||||
default_namespace=None, skip_tls_verify=False):
|
default_namespace=None, skip_tls_verify=False):
|
||||||
"""Creates a new context.
|
"""Creates a new context.
|
||||||
Returns:
|
Returns:
|
||||||
|
|
@ -38,9 +38,7 @@ class ContextAPI(object):
|
||||||
>>> print(ctx.Metadata)
|
>>> print(ctx.Metadata)
|
||||||
{
|
{
|
||||||
"Name": "test",
|
"Name": "test",
|
||||||
"Metadata": {
|
"Metadata": {},
|
||||||
"StackOrchestrator": "swarm"
|
|
||||||
},
|
|
||||||
"Endpoints": {
|
"Endpoints": {
|
||||||
"docker": {
|
"docker": {
|
||||||
"Host": "unix:///var/run/docker.sock",
|
"Host": "unix:///var/run/docker.sock",
|
||||||
|
|
@ -57,7 +55,9 @@ class ContextAPI(object):
|
||||||
ctx = Context.load_context(name)
|
ctx = Context.load_context(name)
|
||||||
if ctx:
|
if ctx:
|
||||||
raise errors.ContextAlreadyExists(name)
|
raise errors.ContextAlreadyExists(name)
|
||||||
endpoint = "docker" if orchestrator == "swarm" else orchestrator
|
endpoint = "docker"
|
||||||
|
if orchestrator and orchestrator != "swarm":
|
||||||
|
endpoint = orchestrator
|
||||||
ctx = Context(name, orchestrator)
|
ctx = Context(name, orchestrator)
|
||||||
ctx.set_endpoint(
|
ctx.set_endpoint(
|
||||||
endpoint, host, tls_cfg,
|
endpoint, host, tls_cfg,
|
||||||
|
|
@ -79,9 +79,7 @@ class ContextAPI(object):
|
||||||
>>> print(ctx.Metadata)
|
>>> print(ctx.Metadata)
|
||||||
{
|
{
|
||||||
"Name": "test",
|
"Name": "test",
|
||||||
"Metadata": {
|
"Metadata": {},
|
||||||
"StackOrchestrator": "swarm"
|
|
||||||
},
|
|
||||||
"Endpoints": {
|
"Endpoints": {
|
||||||
"docker": {
|
"docker": {
|
||||||
"Host": "unix:///var/run/docker.sock",
|
"Host": "unix:///var/run/docker.sock",
|
||||||
|
|
|
||||||
|
|
@ -73,8 +73,8 @@ def get_tls_dir(name=None, endpoint=""):
|
||||||
return os.path.join(context_dir, "tls")
|
return os.path.join(context_dir, "tls")
|
||||||
|
|
||||||
|
|
||||||
def get_context_host(path=None):
|
def get_context_host(path=None, tls=False):
|
||||||
host = utils.parse_host(path, IS_WINDOWS_PLATFORM)
|
host = utils.parse_host(path, IS_WINDOWS_PLATFORM, tls)
|
||||||
if host == DEFAULT_UNIX_SOCKET:
|
if host == DEFAULT_UNIX_SOCKET:
|
||||||
# remove http+ from default docker socket url
|
# remove http+ from default docker socket url
|
||||||
return host.strip("http+")
|
return host.strip("http+")
|
||||||
|
|
|
||||||
|
|
@ -11,19 +11,20 @@ from docker.context.config import get_context_host
|
||||||
|
|
||||||
class Context:
|
class Context:
|
||||||
"""A context."""
|
"""A context."""
|
||||||
def __init__(self, name, orchestrator="swarm", host=None, endpoints=None):
|
def __init__(self, name, orchestrator=None, host=None, endpoints=None,
|
||||||
|
tls=False):
|
||||||
if not name:
|
if not name:
|
||||||
raise Exception("Name not provided")
|
raise Exception("Name not provided")
|
||||||
self.name = name
|
self.name = name
|
||||||
self.orchestrator = orchestrator
|
self.orchestrator = orchestrator
|
||||||
if not endpoints:
|
if not endpoints:
|
||||||
default_endpoint = "docker" if (
|
default_endpoint = "docker" if (
|
||||||
orchestrator == "swarm"
|
not orchestrator or orchestrator == "swarm"
|
||||||
) else orchestrator
|
) else orchestrator
|
||||||
self.endpoints = {
|
self.endpoints = {
|
||||||
default_endpoint: {
|
default_endpoint: {
|
||||||
"Host": get_context_host(host),
|
"Host": get_context_host(host, tls),
|
||||||
"SkipTLSVerify": False
|
"SkipTLSVerify": not tls
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
|
|
@ -44,7 +45,7 @@ class Context:
|
||||||
self, name="docker", host=None, tls_cfg=None,
|
self, name="docker", host=None, tls_cfg=None,
|
||||||
skip_tls_verify=False, def_namespace=None):
|
skip_tls_verify=False, def_namespace=None):
|
||||||
self.endpoints[name] = {
|
self.endpoints[name] = {
|
||||||
"Host": get_context_host(host),
|
"Host": get_context_host(host, not skip_tls_verify),
|
||||||
"SkipTLSVerify": skip_tls_verify
|
"SkipTLSVerify": skip_tls_verify
|
||||||
}
|
}
|
||||||
if def_namespace:
|
if def_namespace:
|
||||||
|
|
@ -84,7 +85,8 @@ class Context:
|
||||||
context {} : {}""".format(name, e))
|
context {} : {}""".format(name, e))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
metadata["Name"], metadata["Metadata"]["StackOrchestrator"],
|
metadata["Name"],
|
||||||
|
metadata["Metadata"].get("StackOrchestrator", None),
|
||||||
metadata["Endpoints"])
|
metadata["Endpoints"])
|
||||||
return None, None, None
|
return None, None, None
|
||||||
|
|
||||||
|
|
@ -161,7 +163,7 @@ class Context:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def Host(self):
|
def Host(self):
|
||||||
if self.orchestrator == "swarm":
|
if not self.orchestrator or self.orchestrator == "swarm":
|
||||||
return self.endpoints["docker"]["Host"]
|
return self.endpoints["docker"]["Host"]
|
||||||
return self.endpoints[self.orchestrator]["Host"]
|
return self.endpoints[self.orchestrator]["Host"]
|
||||||
|
|
||||||
|
|
@ -171,18 +173,19 @@ class Context:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def Metadata(self):
|
def Metadata(self):
|
||||||
|
meta = {}
|
||||||
|
if self.orchestrator:
|
||||||
|
meta = {"StackOrchestrator": self.orchestrator}
|
||||||
return {
|
return {
|
||||||
"Name": self.name,
|
"Name": self.name,
|
||||||
"Metadata": {
|
"Metadata": meta,
|
||||||
"StackOrchestrator": self.orchestrator
|
|
||||||
},
|
|
||||||
"Endpoints": self.endpoints
|
"Endpoints": self.endpoints
|
||||||
}
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def TLSConfig(self):
|
def TLSConfig(self):
|
||||||
key = self.orchestrator
|
key = self.orchestrator
|
||||||
if key == "swarm":
|
if not key or key == "swarm":
|
||||||
key = "docker"
|
key = "docker"
|
||||||
if key in self.tls_cfg.keys():
|
if key in self.tls_cfg.keys():
|
||||||
return self.tls_cfg[key]
|
return self.tls_cfg[key]
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
version = "4.2.0"
|
version = "4.2.1"
|
||||||
version_info = tuple([int(d) for d in version.split("-")[0].split(".")])
|
version_info = tuple([int(d) for d in version.split("-")[0].split(".")])
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,16 @@
|
||||||
Change log
|
Change log
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
4.2.1
|
||||||
|
-----
|
||||||
|
|
||||||
|
[List of PRs / issues for this release](https://github.com/docker/docker-py/milestone/65?closed=1)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Add option on when to use `tls` on Context constructor
|
||||||
|
- Make context orchestrator field optional
|
||||||
|
|
||||||
4.2.0
|
4.2.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -273,11 +273,14 @@ class CreateContainerTest(BaseAPIIntegrationTest):
|
||||||
|
|
||||||
def test_invalid_log_driver_raises_exception(self):
|
def test_invalid_log_driver_raises_exception(self):
|
||||||
log_config = docker.types.LogConfig(
|
log_config = docker.types.LogConfig(
|
||||||
type='asdf-nope',
|
type='asdf',
|
||||||
config={}
|
config={}
|
||||||
)
|
)
|
||||||
|
|
||||||
expected_msg = "logger: no log driver named 'asdf-nope' is registered"
|
expected_msgs = [
|
||||||
|
"logger: no log driver named 'asdf' is registered",
|
||||||
|
"looking up logging plugin asdf: plugin \"asdf\" not found",
|
||||||
|
]
|
||||||
with pytest.raises(docker.errors.APIError) as excinfo:
|
with pytest.raises(docker.errors.APIError) as excinfo:
|
||||||
# raises an internal server error 500
|
# raises an internal server error 500
|
||||||
container = self.client.create_container(
|
container = self.client.create_container(
|
||||||
|
|
@ -287,7 +290,7 @@ class CreateContainerTest(BaseAPIIntegrationTest):
|
||||||
)
|
)
|
||||||
self.client.start(container)
|
self.client.start(container)
|
||||||
|
|
||||||
assert excinfo.value.explanation == expected_msg
|
assert excinfo.value.explanation in expected_msgs
|
||||||
|
|
||||||
def test_valid_no_log_driver_specified(self):
|
def test_valid_no_log_driver_specified(self):
|
||||||
log_config = docker.types.LogConfig(
|
log_config = docker.types.LogConfig(
|
||||||
|
|
@ -1102,6 +1105,8 @@ class PortTest(BaseAPIIntegrationTest):
|
||||||
|
|
||||||
|
|
||||||
class ContainerTopTest(BaseAPIIntegrationTest):
|
class ContainerTopTest(BaseAPIIntegrationTest):
|
||||||
|
@pytest.mark.xfail(reason='Output of docker top depends on host distro, '
|
||||||
|
'and is not formalized.')
|
||||||
def test_top(self):
|
def test_top(self):
|
||||||
container = self.client.create_container(
|
container = self.client.create_container(
|
||||||
TEST_IMG, ['sleep', '60']
|
TEST_IMG, ['sleep', '60']
|
||||||
|
|
@ -1112,9 +1117,7 @@ class ContainerTopTest(BaseAPIIntegrationTest):
|
||||||
self.client.start(container)
|
self.client.start(container)
|
||||||
res = self.client.top(container)
|
res = self.client.top(container)
|
||||||
if not IS_WINDOWS_PLATFORM:
|
if not IS_WINDOWS_PLATFORM:
|
||||||
assert res['Titles'] == [
|
assert res['Titles'] == [u'PID', u'USER', u'TIME', u'COMMAND']
|
||||||
'UID', 'PID', 'PPID', 'C', 'STIME', 'TTY', 'TIME', 'CMD'
|
|
||||||
]
|
|
||||||
assert len(res['Processes']) == 1
|
assert len(res['Processes']) == 1
|
||||||
assert res['Processes'][0][-1] == 'sleep 60'
|
assert res['Processes'][0][-1] == 'sleep 60'
|
||||||
self.client.kill(container)
|
self.client.kill(container)
|
||||||
|
|
@ -1122,6 +1125,8 @@ class ContainerTopTest(BaseAPIIntegrationTest):
|
||||||
@pytest.mark.skipif(
|
@pytest.mark.skipif(
|
||||||
IS_WINDOWS_PLATFORM, reason='No psargs support on windows'
|
IS_WINDOWS_PLATFORM, reason='No psargs support on windows'
|
||||||
)
|
)
|
||||||
|
@pytest.mark.xfail(reason='Output of docker top depends on host distro, '
|
||||||
|
'and is not formalized.')
|
||||||
def test_top_with_psargs(self):
|
def test_top_with_psargs(self):
|
||||||
container = self.client.create_container(
|
container = self.client.create_container(
|
||||||
TEST_IMG, ['sleep', '60'])
|
TEST_IMG, ['sleep', '60'])
|
||||||
|
|
@ -1129,11 +1134,8 @@ class ContainerTopTest(BaseAPIIntegrationTest):
|
||||||
self.tmp_containers.append(container)
|
self.tmp_containers.append(container)
|
||||||
|
|
||||||
self.client.start(container)
|
self.client.start(container)
|
||||||
res = self.client.top(container, 'waux')
|
res = self.client.top(container, '-eopid,user')
|
||||||
assert res['Titles'] == [
|
assert res['Titles'] == [u'PID', u'USER']
|
||||||
'USER', 'PID', '%CPU', '%MEM', 'VSZ', 'RSS',
|
|
||||||
'TTY', 'STAT', 'START', 'TIME', 'COMMAND'
|
|
||||||
]
|
|
||||||
assert len(res['Processes']) == 1
|
assert len(res['Processes']) == 1
|
||||||
assert res['Processes'][0][10] == 'sleep 60'
|
assert res['Processes'][0][10] == 'sleep 60'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,3 +50,10 @@ class ContextLifecycleTest(BaseAPIIntegrationTest):
|
||||||
ContextAPI.remove_context("test")
|
ContextAPI.remove_context("test")
|
||||||
with pytest.raises(errors.ContextNotFound):
|
with pytest.raises(errors.ContextNotFound):
|
||||||
ContextAPI.inspect_context("test")
|
ContextAPI.inspect_context("test")
|
||||||
|
|
||||||
|
def test_load_context_without_orchestrator(self):
|
||||||
|
ContextAPI.create_context("test")
|
||||||
|
ctx = ContextAPI.get_context("test")
|
||||||
|
assert ctx
|
||||||
|
assert ctx.Name == "test"
|
||||||
|
assert ctx.Orchestrator is None
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,10 @@ class BaseContextTest(unittest.TestCase):
|
||||||
def test_get_current_context(self):
|
def test_get_current_context(self):
|
||||||
assert ContextAPI.get_current_context().Name == "default"
|
assert ContextAPI.get_current_context().Name == "default"
|
||||||
|
|
||||||
|
def test_https_host(self):
|
||||||
|
c = Context("test", host="tcp://testdomain:8080", tls=True)
|
||||||
|
assert c.Host == "https://testdomain:8080"
|
||||||
|
|
||||||
def test_context_inspect_without_params(self):
|
def test_context_inspect_without_params(self):
|
||||||
ctx = ContextAPI.inspect_context()
|
ctx = ContextAPI.inspect_context()
|
||||||
assert ctx["Name"] == "default"
|
assert ctx["Name"] == "default"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue