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