Skip to content

Commit 1d1532f

Browse files
author
Ulysses Souza
authored
Merge pull request #2506 from ulyssessouza/4.2.0-release
4.2.0 release
2 parents 6649587 + ab56784 commit 1d1532f

20 files changed

+719
-38
lines changed

.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ matrix:
1515
- env: TOXENV=flake8
1616

1717
install:
18-
- pip install tox
18+
- pip install tox==2.9.1
1919
script:
2020
- tox

Jenkinsfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def buildImages = { ->
3232

3333
def getDockerVersions = { ->
3434
def dockerVersions = ["17.06.2-ce"]
35-
wrappedNode(label: "ubuntu && !zfs") {
35+
wrappedNode(label: "ubuntu && !zfs && amd64") {
3636
def result = sh(script: """docker run --rm \\
3737
--entrypoint=python \\
3838
${imageNamePy3} \\

appveyor.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
version: '{branch}-{build}'
22

33
install:
4-
- "SET PATH=C:\\Python27-x64;C:\\Python27-x64\\Scripts;%PATH%"
4+
- "SET PATH=C:\\Python37-x64;C:\\Python37-x64\\Scripts;%PATH%"
55
- "python --version"
6+
- "python -m pip install --upgrade pip"
67
- "pip install tox==2.9.1"
78

89
# Build the binary after tests

docker/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# flake8: noqa
22
from .api import APIClient
33
from .client import DockerClient, from_env
4+
from .context import Context
5+
from .context import ContextAPI
6+
from .tls import TLSConfig
47
from .version import version, version_info
58

69
__version__ = version

docker/constants.py

+12
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,18 @@
99
'memory', 'memswap', 'cpushares', 'cpusetcpus'
1010
]
1111

12+
DEFAULT_HTTP_HOST = "127.0.0.1"
13+
DEFAULT_UNIX_SOCKET = "http+unix:///var/run/docker.sock"
14+
DEFAULT_NPIPE = 'npipe:////./pipe/docker_engine'
15+
16+
BYTE_UNITS = {
17+
'b': 1,
18+
'k': 1024,
19+
'm': 1024 * 1024,
20+
'g': 1024 * 1024 * 1024
21+
}
22+
23+
1224
INSECURE_REGISTRY_DEPRECATION_WARNING = \
1325
'The `insecure_registry` argument to {} ' \
1426
'is deprecated and non-functional. Please remove it.'

docker/context/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# flake8: noqa
2+
from .context import Context
3+
from .api import ContextAPI

docker/context/api.py

+205
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import json
2+
import os
3+
4+
from docker import errors
5+
from docker.context.config import get_meta_dir
6+
from docker.context.config import METAFILE
7+
from docker.context.config import get_current_context_name
8+
from docker.context.config import write_context_name_to_docker_config
9+
from docker.context import Context
10+
11+
12+
class ContextAPI(object):
13+
"""Context API.
14+
Contains methods for context management:
15+
create, list, remove, get, inspect.
16+
"""
17+
DEFAULT_CONTEXT = Context("default")
18+
19+
@classmethod
20+
def create_context(
21+
cls, name, orchestrator="swarm", host=None, tls_cfg=None,
22+
default_namespace=None, skip_tls_verify=False):
23+
"""Creates a new context.
24+
Returns:
25+
(Context): a Context object.
26+
Raises:
27+
:py:class:`docker.errors.MissingContextParameter`
28+
If a context name is not provided.
29+
:py:class:`docker.errors.ContextAlreadyExists`
30+
If a context with the name already exists.
31+
:py:class:`docker.errors.ContextException`
32+
If name is default.
33+
34+
Example:
35+
36+
>>> from docker.context import ContextAPI
37+
>>> ctx = ContextAPI.create_context(name='test')
38+
>>> print(ctx.Metadata)
39+
{
40+
"Name": "test",
41+
"Metadata": {
42+
"StackOrchestrator": "swarm"
43+
},
44+
"Endpoints": {
45+
"docker": {
46+
"Host": "unix:///var/run/docker.sock",
47+
"SkipTLSVerify": false
48+
}
49+
}
50+
}
51+
"""
52+
if not name:
53+
raise errors.MissingContextParameter("name")
54+
if name == "default":
55+
raise errors.ContextException(
56+
'"default" is a reserved context name')
57+
ctx = Context.load_context(name)
58+
if ctx:
59+
raise errors.ContextAlreadyExists(name)
60+
endpoint = "docker" if orchestrator == "swarm" else orchestrator
61+
ctx = Context(name, orchestrator)
62+
ctx.set_endpoint(
63+
endpoint, host, tls_cfg,
64+
skip_tls_verify=skip_tls_verify,
65+
def_namespace=default_namespace)
66+
ctx.save()
67+
return ctx
68+
69+
@classmethod
70+
def get_context(cls, name=None):
71+
"""Retrieves a context object.
72+
Args:
73+
name (str): The name of the context
74+
75+
Example:
76+
77+
>>> from docker.context import ContextAPI
78+
>>> ctx = ContextAPI.get_context(name='test')
79+
>>> print(ctx.Metadata)
80+
{
81+
"Name": "test",
82+
"Metadata": {
83+
"StackOrchestrator": "swarm"
84+
},
85+
"Endpoints": {
86+
"docker": {
87+
"Host": "unix:///var/run/docker.sock",
88+
"SkipTLSVerify": false
89+
}
90+
}
91+
}
92+
"""
93+
if not name:
94+
name = get_current_context_name()
95+
if name == "default":
96+
return cls.DEFAULT_CONTEXT
97+
return Context.load_context(name)
98+
99+
@classmethod
100+
def contexts(cls):
101+
"""Context list.
102+
Returns:
103+
(Context): List of context objects.
104+
Raises:
105+
:py:class:`docker.errors.APIError`
106+
If the server returns an error.
107+
"""
108+
names = []
109+
for dirname, dirnames, fnames in os.walk(get_meta_dir()):
110+
for filename in fnames + dirnames:
111+
if filename == METAFILE:
112+
try:
113+
data = json.load(
114+
open(os.path.join(dirname, filename), "r"))
115+
names.append(data["Name"])
116+
except Exception as e:
117+
raise errors.ContextException(
118+
"Failed to load metafile {}: {}".format(
119+
filename, e))
120+
121+
contexts = [cls.DEFAULT_CONTEXT]
122+
for name in names:
123+
contexts.append(Context.load_context(name))
124+
return contexts
125+
126+
@classmethod
127+
def get_current_context(cls):
128+
"""Get current context.
129+
Returns:
130+
(Context): current context object.
131+
"""
132+
return cls.get_context()
133+
134+
@classmethod
135+
def set_current_context(cls, name="default"):
136+
ctx = cls.get_context(name)
137+
if not ctx:
138+
raise errors.ContextNotFound(name)
139+
140+
err = write_context_name_to_docker_config(name)
141+
if err:
142+
raise errors.ContextException(
143+
'Failed to set current context: {}'.format(err))
144+
145+
@classmethod
146+
def remove_context(cls, name):
147+
"""Remove a context. Similar to the ``docker context rm`` command.
148+
149+
Args:
150+
name (str): The name of the context
151+
152+
Raises:
153+
:py:class:`docker.errors.MissingContextParameter`
154+
If a context name is not provided.
155+
:py:class:`docker.errors.ContextNotFound`
156+
If a context with the name does not exist.
157+
:py:class:`docker.errors.ContextException`
158+
If name is default.
159+
160+
Example:
161+
162+
>>> from docker.context import ContextAPI
163+
>>> ContextAPI.remove_context(name='test')
164+
>>>
165+
"""
166+
if not name:
167+
raise errors.MissingContextParameter("name")
168+
if name == "default":
169+
raise errors.ContextException(
170+
'context "default" cannot be removed')
171+
ctx = Context.load_context(name)
172+
if not ctx:
173+
raise errors.ContextNotFound(name)
174+
if name == get_current_context_name():
175+
write_context_name_to_docker_config(None)
176+
ctx.remove()
177+
178+
@classmethod
179+
def inspect_context(cls, name="default"):
180+
"""Remove a context. Similar to the ``docker context inspect`` command.
181+
182+
Args:
183+
name (str): The name of the context
184+
185+
Raises:
186+
:py:class:`docker.errors.MissingContextParameter`
187+
If a context name is not provided.
188+
:py:class:`docker.errors.ContextNotFound`
189+
If a context with the name does not exist.
190+
191+
Example:
192+
193+
>>> from docker.context import ContextAPI
194+
>>> ContextAPI.remove_context(name='test')
195+
>>>
196+
"""
197+
if not name:
198+
raise errors.MissingContextParameter("name")
199+
if name == "default":
200+
return cls.DEFAULT_CONTEXT()
201+
ctx = Context.load_context(name)
202+
if not ctx:
203+
raise errors.ContextNotFound(name)
204+
205+
return ctx()

docker/context/config.py

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import os
2+
import json
3+
import hashlib
4+
5+
from docker import utils
6+
from docker.constants import IS_WINDOWS_PLATFORM
7+
from docker.constants import DEFAULT_UNIX_SOCKET
8+
from docker.utils.config import find_config_file
9+
10+
METAFILE = "meta.json"
11+
12+
13+
def get_current_context_name():
14+
name = "default"
15+
docker_cfg_path = find_config_file()
16+
if docker_cfg_path:
17+
try:
18+
with open(docker_cfg_path, "r") as f:
19+
name = json.load(f).get("currentContext", "default")
20+
except Exception:
21+
return "default"
22+
return name
23+
24+
25+
def write_context_name_to_docker_config(name=None):
26+
if name == 'default':
27+
name = None
28+
docker_cfg_path = find_config_file()
29+
config = {}
30+
if docker_cfg_path:
31+
try:
32+
with open(docker_cfg_path, "r") as f:
33+
config = json.load(f)
34+
except Exception as e:
35+
return e
36+
current_context = config.get("currentContext", None)
37+
if current_context and not name:
38+
del config["currentContext"]
39+
elif name:
40+
config["currentContext"] = name
41+
else:
42+
return
43+
try:
44+
with open(docker_cfg_path, "w") as f:
45+
json.dump(config, f, indent=4)
46+
except Exception as e:
47+
return e
48+
49+
50+
def get_context_id(name):
51+
return hashlib.sha256(name.encode('utf-8')).hexdigest()
52+
53+
54+
def get_context_dir():
55+
return os.path.join(os.path.dirname(find_config_file() or ""), "contexts")
56+
57+
58+
def get_meta_dir(name=None):
59+
meta_dir = os.path.join(get_context_dir(), "meta")
60+
if name:
61+
return os.path.join(meta_dir, get_context_id(name))
62+
return meta_dir
63+
64+
65+
def get_meta_file(name):
66+
return os.path.join(get_meta_dir(name), METAFILE)
67+
68+
69+
def get_tls_dir(name=None, endpoint=""):
70+
context_dir = get_context_dir()
71+
if name:
72+
return os.path.join(context_dir, "tls", get_context_id(name), endpoint)
73+
return os.path.join(context_dir, "tls")
74+
75+
76+
def get_context_host(path=None):
77+
host = utils.parse_host(path, IS_WINDOWS_PLATFORM)
78+
if host == DEFAULT_UNIX_SOCKET:
79+
# remove http+ from default docker socket url
80+
return host.strip("http+")
81+
return host

0 commit comments

Comments
 (0)