Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 83 additions & 24 deletions compose/cli/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@
from .. import __version__
from ..project import NoSuchService, ConfigurationError
from ..service import BuildError, CannotBeScaledError
from ..config import parse_environment
from ..config import parse_environment, load
from .command import Command
from .docopt_command import NoSuchCommand
from .errors import UserError
Expand DownExpand Up@@ -270,19 +270,29 @@ def run(self, project, options):
Usage: run [options] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]

Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run container in the background, print
new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple times)
-u, --user="" Run as specified username or uid
--no-deps Don't start linked services.
--rm Remove container after run. Ignored in detached mode.
--service-ports Run command with the service's ports enabled and mapped
to the host.
-T Disable pseudo-tty allocation. By default `docker-compose run`
allocates a TTY.
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run container in the background,
print new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple
times)
--labels-file FILE Uses all labels, in the given yaml FILE for the
SERVICE to be run.
--no-deps Don't start linked services.
--no-prefix-labels Don't prefix '.cfg' and '.cmd' when --labels-file
is used.
--remove-labels [REGEX] Removes all labels. If REGEX is given, removes
labels that match the given REGular EXpression.
(Note: This option has less priority than the
given --labels-file)
--rm Remove container after run. Ignored in detached
mode.
--service-ports Run command with the service's ports enabled and
mapped to the host.
-T Disable pseudo-tty allocation. By default
`docker-compose run` allocates a TTY.
-u, --user="" Run as specified username or uid.
"""
service = project.get_service(options['SERVICE'])

Expand DownExpand Up@@ -314,8 +324,42 @@ def run(self, project, options):
'tty': tty,
'stdin_open': not options['-d'],
'detach': options['-d'],
'labels': dict(),
}

if options['--remove-labels']:
service_cfg_lbls = service.options.get('labels')
if service_cfg_lbls is not None:
regex = options.get('--remove-labels')
a = re.compile(regex)
service.options['labels'] = dict(
(k, v) for (k,v) in service_cfg_lbls.iteritems()
if not a.match(k))

if options['--labels-file']:
# Merge labels from config with labels from --labels-file
label_cfg_file = self.get_config_path(options.get('--labels-file'))
label_list_cfg_file = load(label_cfg_file)
service_cmd_opts = [a_serv_lbl for a_serv_lbl in label_list_cfg_file
if a_serv_lbl.get('name') == service.name
and a_serv_lbl.get('labels') is not None]
if len(service_cmd_opts) == 0:
raise UserError('No labels defined for service \'' + service.name +
'\' in ' + options.get('--labels-file'))
service_cmd_lbls = service_cmd_opts[0]['labels']
service_cfg_lbls = service.options.get('labels')
prefix_cmd = ""
prefix_cfg = ""
if not options['--no-prefix-labels']:
prefix_cmd = ".cmd:"
prefix_cfg = ".cfg:"

if service_cfg_lbls is not None:
container_options['labels'].update(
(prefix_cfg + key, service_cfg_lbls[key]) for key in service_cfg_lbls)
container_options['labels'].update(
(prefix_cmd + key, service_cmd_lbls[key]) for key in service_cmd_lbls)

if options['-e']:
# Merge environment from config with -e command line
container_options['environment'] = dict(
Expand DownExpand Up@@ -434,23 +478,35 @@ def up(self, project, options):
Usage: up [options] [SERVICE...]

Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run containers in the background,
print new container names.
--no-color Produce monochrome output.
--no-deps Don't start linked services.
--no-recreate If containers already exist, don't recreate them.
--no-build Don't build an image, even if it's missing
-t, --timeout TIMEOUT When attached, use this timeout in seconds
for the shutdown. (default: 10)
--allow-insecure-ssl Allow insecure connections to the docker registry.
-d Detached mode: Run containers in the background,
print new container names.
--labels-file FILE Uses all labels, in the given yaml FILE for the
SERVICEs to be up.
--no-build Don't build an image, even if it's missing.
--no-deps Don't start linked services.
--no-color Produce monochrome output.
--no-prefix-labels Don't prefix '.cfg' and '.cmd' when --labels-file
is used.
--no-recreate If containers already exist, don't recreate them.
--remove-labels [REGEX] Removes all labels. If REGEX is given, removes
labels that match the given REGular EXpression.
(Note: This option has less priority than the
given --labels-file).
-t, --timeout TIMEOUT When attached, use this timeout in seconds for the
shutdown. (default: 10)

"""
insecure_registry = options['--allow-insecure-ssl']
detached = options['-d']

monochrome = options['--no-color']

if options['--labels-file']:
labels_file = self.get_config_path(options['--labels-file'])
else:
labels_file = ''

start_deps = not options['--no-deps']
recreate = not options['--no-recreate']
service_names = options['SERVICE']
Expand All@@ -462,6 +518,9 @@ def up(self, project, options):
insecure_registry=insecure_registry,
detach=detached,
do_build=not options['--no-build'],
prefix_labels=not options['--no-prefix-labels'],
remove_labels=str(options['--remove-labels']),
labels_file=labels_file
)

to_attach = [c for s in project.get_services(service_names) for c in s.containers()]
Expand Down
3 changes: 1 addition & 2 deletions compose/config.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
'environment',
'hostname',
'image',
'labels',
'links',
'mem_limit',
'net',
Expand DownExpand Up@@ -138,7 +139,6 @@ def resolve_environment(service_dict, working_dir=None):
service_dict['environment'] = env
return service_dict


def parse_environment(environment):
if not environment:
return {}
Expand All@@ -154,7 +154,6 @@ def parse_environment(environment):
environment
)


def split_env(env):
if '=' in env:
return env.split('=', 1)
Expand Down
16 changes: 13 additions & 3 deletions compose/project.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -209,20 +209,30 @@ def up(self,
recreate=True,
insecure_registry=False,
detach=False,
do_build=True):
do_build=True,
prefix_labels=False,
remove_labels='',
labels_file=None
):
running_containers = []
for service in self.get_services(service_names, include_deps=start_deps):
if recreate:
for (_, container) in service.recreate_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
do_build=do_build,
prefix_labels=prefix_labels,
remove_labels=remove_labels,
labels_file=labels_file):
running_containers.append(container)
else:
for container in service.start_or_create_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
do_build=do_build,
prefix_labels=prefix_labels,
remove_labels=remove_labels,
labels_file=labels_file):
running_containers.append(container)

return running_containers
Expand Down
57 changes: 53 additions & 4 deletions compose/service.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@

from docker.errors import APIError

from .config import DOCKER_CONFIG_KEYS
from .config import DOCKER_CONFIG_KEYS, load
from .container import Container, get_container_name
from .progress_stream import stream_output, StreamOutputError

Expand All@@ -29,6 +29,7 @@

VALID_NAME_CHARS = '[a-zA-Z0-9]'

LABEL_PREFIX = ''

class BuildError(Exception):
def __init__(self, service, reason):
Expand All@@ -50,6 +51,9 @@ class ConfigError(ValueError):
ServiceName = namedtuple('ServiceName', 'project service number')


LabelSpec = namedtuple('LabelSpec', 'key value')


class Service(object):
def __init__(self, name, client=None, project='default', links=None, external_links=None, volumes_from=None, net=None, **options):
if not re.match('^%s+$' % VALID_NAME_CHARS, name):
Expand DownExpand Up@@ -181,7 +185,6 @@ def create_container(self,
self.can_be_built() and
not self.client.images(name=self.full_name)):
self.build()

try:
return Container.create(self.client, **container_options)
except APIError as e:
Expand DownExpand Up@@ -296,7 +299,8 @@ def start_or_create_containers(
self,
insecure_registry=False,
detach=False,
do_build=True):
do_build=True,
**override_options):
containers = self.containers(stopped=True)

if not containers:
Expand All@@ -305,6 +309,7 @@ def start_or_create_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build,
**override_options
)
return [self.start_container(new_container)]
else:
Expand DownExpand Up@@ -399,6 +404,51 @@ def _get_container_create_options(self, override_options, one_off=False):
self.containers(stopped=True, one_off=one_off),
one_off)

if 'labels' in container_options:
container_options['labels'] = dict(
(LABEL_PREFIX + key, container_options['labels'][key])
for key in container_options['labels'])

if 'remove_labels' in container_options:
service_cfg_lbls = container_options.get('labels')
if service_cfg_lbls is not None:
regex = container_options.get('remove_labels')
a = re.compile(regex)
container_options['labels'] = dict(
(k, v) for (k,v) in service_cfg_lbls.iteritems()
if not a.match(k))
del container_options['remove_labels']

if 'labels_file' in container_options:
# Merge labels from config with labels from --labels-file
label_cfg_file = container_options.get('labels_file')
label_list_cfg_file = load(label_cfg_file)
service_cmd_opts = [a_serv_lbl for a_serv_lbl in label_list_cfg_file
if a_serv_lbl.get('name') == self.name
and a_serv_lbl.get('labels') is not None]
if len(service_cmd_opts) == 0:
raise ConfigError ('No labels defined for service \'' +
self.name + '\' in ' +
container_options.get('labels_file'))
service_cmd_lbls = service_cmd_opts[0]['labels']
service_cfg_lbls = container_options.get('labels')
prefix_cmd = ""
prefix_cfg = ""
if 'prefix_labels' in container_options:
prefix_cmd = ".cmd:"
prefix_cfg = ".cfg:"
del container_options['prefix_labels']
temp_labels = dict()
if service_cfg_lbls is not None:
temp_labels.update(
(prefix_cfg + key, service_cfg_lbls[key]) for key in service_cfg_lbls)
temp_labels.update(
(prefix_cmd + key, service_cmd_lbls[key]) for key in service_cmd_lbls)

container_options['labels'] = temp_labels
del container_options['labels_file']


# If a qualified hostname was given, split it into an
# unqualified hostname and a domainname unless domainname
# was also given explicitly. This matches the behavior of
Expand DownExpand Up@@ -553,7 +603,6 @@ def parse_volume_spec(volume_config):

return VolumeSpec(external, internal, mode)


def parse_repository_tag(s):
if ":" not in s:
return s, ""
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 83 additions & 24 deletions compose/cli/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@
from .. import __version__
from ..project import NoSuchService, ConfigurationError
from ..service import BuildError, CannotBeScaledError
from ..config import parse_environment
from ..config import parse_environment, load
from .command import Command
from .docopt_command import NoSuchCommand
from .errors import UserError
Expand DownExpand Up@@ -270,19 +270,29 @@ def run(self, project, options):
Usage: run [options] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]

Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run container in the background, print
new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple times)
-u, --user="" Run as specified username or uid
--no-deps Don't start linked services.
--rm Remove container after run. Ignored in detached mode.
--service-ports Run command with the service's ports enabled and mapped
to the host.
-T Disable pseudo-tty allocation. By default `docker-compose run`
allocates a TTY.
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run container in the background,
print new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple
times)
--labels-file FILE Uses all labels, in the given yaml FILE for the
SERVICE to be run.
--no-deps Don't start linked services.
--no-prefix-labels Don't prefix '.cfg' and '.cmd' when --labels-file
is used.
--remove-labels [REGEX] Removes all labels. If REGEX is given, removes
labels that match the given REGular EXpression.
(Note: This option has less priority than the
given --labels-file)
--rm Remove container after run. Ignored in detached
mode.
--service-ports Run command with the service's ports enabled and
mapped to the host.
-T Disable pseudo-tty allocation. By default
`docker-compose run` allocates a TTY.
-u, --user="" Run as specified username or uid.
"""
service = project.get_service(options['SERVICE'])

Expand DownExpand Up@@ -314,8 +324,42 @@ def run(self, project, options):
'tty': tty,
'stdin_open': not options['-d'],
'detach': options['-d'],
'labels': dict(),
}

if options['--remove-labels']:
service_cfg_lbls = service.options.get('labels')
if service_cfg_lbls is not None:
regex = options.get('--remove-labels')
a = re.compile(regex)
service.options['labels'] = dict(
(k, v) for (k,v) in service_cfg_lbls.iteritems()
if not a.match(k))

if options['--labels-file']:
# Merge labels from config with labels from --labels-file
label_cfg_file = self.get_config_path(options.get('--labels-file'))
label_list_cfg_file = load(label_cfg_file)
service_cmd_opts = [a_serv_lbl for a_serv_lbl in label_list_cfg_file
if a_serv_lbl.get('name') == service.name
and a_serv_lbl.get('labels') is not None]
if len(service_cmd_opts) == 0:
raise UserError('No labels defined for service \'' + service.name +
'\' in ' + options.get('--labels-file'))
service_cmd_lbls = service_cmd_opts[0]['labels']
service_cfg_lbls = service.options.get('labels')
prefix_cmd = ""
prefix_cfg = ""
if not options['--no-prefix-labels']:
prefix_cmd = ".cmd:"
prefix_cfg = ".cfg:"

if service_cfg_lbls is not None:
container_options['labels'].update(
(prefix_cfg + key, service_cfg_lbls[key]) for key in service_cfg_lbls)
container_options['labels'].update(
(prefix_cmd + key, service_cmd_lbls[key]) for key in service_cmd_lbls)

if options['-e']:
# Merge environment from config with -e command line
container_options['environment'] = dict(
Expand DownExpand Up@@ -434,23 +478,35 @@ def up(self, project, options):
Usage: up [options] [SERVICE...]

Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run containers in the background,
print new container names.
--no-color Produce monochrome output.
--no-deps Don't start linked services.
--no-recreate If containers already exist, don't recreate them.
--no-build Don't build an image, even if it's missing
-t, --timeout TIMEOUT When attached, use this timeout in seconds
for the shutdown. (default: 10)
--allow-insecure-ssl Allow insecure connections to the docker registry.
-d Detached mode: Run containers in the background,
print new container names.
--labels-file FILE Uses all labels, in the given yaml FILE for the
SERVICEs to be up.
--no-build Don't build an image, even if it's missing.
--no-deps Don't start linked services.
--no-color Produce monochrome output.
--no-prefix-labels Don't prefix '.cfg' and '.cmd' when --labels-file
is used.
--no-recreate If containers already exist, don't recreate them.
--remove-labels [REGEX] Removes all labels. If REGEX is given, removes
labels that match the given REGular EXpression.
(Note: This option has less priority than the
given --labels-file).
-t, --timeout TIMEOUT When attached, use this timeout in seconds for the
shutdown. (default: 10)

"""
insecure_registry = options['--allow-insecure-ssl']
detached = options['-d']

monochrome = options['--no-color']

if options['--labels-file']:
labels_file = self.get_config_path(options['--labels-file'])
else:
labels_file = ''

start_deps = not options['--no-deps']
recreate = not options['--no-recreate']
service_names = options['SERVICE']
Expand All@@ -462,6 +518,9 @@ def up(self, project, options):
insecure_registry=insecure_registry,
detach=detached,
do_build=not options['--no-build'],
prefix_labels=not options['--no-prefix-labels'],
remove_labels=str(options['--remove-labels']),
labels_file=labels_file
)

to_attach = [c for s in project.get_services(service_names) for c in s.containers()]
Expand Down
3 changes: 1 addition & 2 deletions compose/config.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
'environment',
'hostname',
'image',
'labels',
'links',
'mem_limit',
'net',
Expand DownExpand Up@@ -138,7 +139,6 @@ def resolve_environment(service_dict, working_dir=None):
service_dict['environment'] = env
return service_dict


def parse_environment(environment):
if not environment:
return {}
Expand All@@ -154,7 +154,6 @@ def parse_environment(environment):
environment
)


def split_env(env):
if '=' in env:
return env.split('=', 1)
Expand Down
16 changes: 13 additions & 3 deletions compose/project.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -209,20 +209,30 @@ def up(self,
recreate=True,
insecure_registry=False,
detach=False,
do_build=True):
do_build=True,
prefix_labels=False,
remove_labels='',
labels_file=None
):
running_containers = []
for service in self.get_services(service_names, include_deps=start_deps):
if recreate:
for (_, container) in service.recreate_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
do_build=do_build,
prefix_labels=prefix_labels,
remove_labels=remove_labels,
labels_file=labels_file):
running_containers.append(container)
else:
for container in service.start_or_create_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
do_build=do_build,
prefix_labels=prefix_labels,
remove_labels=remove_labels,
labels_file=labels_file):
running_containers.append(container)

return running_containers
Expand Down
57 changes: 53 additions & 4 deletions compose/service.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@

from docker.errors import APIError

from .config import DOCKER_CONFIG_KEYS
from .config import DOCKER_CONFIG_KEYS, load
from .container import Container, get_container_name
from .progress_stream import stream_output, StreamOutputError

Expand All@@ -29,6 +29,7 @@

VALID_NAME_CHARS = '[a-zA-Z0-9]'

LABEL_PREFIX = ''

class BuildError(Exception):
def __init__(self, service, reason):
Expand All@@ -50,6 +51,9 @@ class ConfigError(ValueError):
ServiceName = namedtuple('ServiceName', 'project service number')


LabelSpec = namedtuple('LabelSpec', 'key value')


class Service(object):
def __init__(self, name, client=None, project='default', links=None, external_links=None, volumes_from=None, net=None, **options):
if not re.match('^%s+$' % VALID_NAME_CHARS, name):
Expand DownExpand Up@@ -181,7 +185,6 @@ def create_container(self,
self.can_be_built() and
not self.client.images(name=self.full_name)):
self.build()

try:
return Container.create(self.client, **container_options)
except APIError as e:
Expand DownExpand Up@@ -296,7 +299,8 @@ def start_or_create_containers(
self,
insecure_registry=False,
detach=False,
do_build=True):
do_build=True,
**override_options):
containers = self.containers(stopped=True)

if not containers:
Expand All@@ -305,6 +309,7 @@ def start_or_create_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build,
**override_options
)
return [self.start_container(new_container)]
else:
Expand DownExpand Up@@ -399,6 +404,51 @@ def _get_container_create_options(self, override_options, one_off=False):
self.containers(stopped=True, one_off=one_off),
one_off)

if 'labels' in container_options:
container_options['labels'] = dict(
(LABEL_PREFIX + key, container_options['labels'][key])
for key in container_options['labels'])

if 'remove_labels' in container_options:
service_cfg_lbls = container_options.get('labels')
if service_cfg_lbls is not None:
regex = container_options.get('remove_labels')
a = re.compile(regex)
container_options['labels'] = dict(
(k, v) for (k,v) in service_cfg_lbls.iteritems()
if not a.match(k))
del container_options['remove_labels']

if 'labels_file' in container_options:
# Merge labels from config with labels from --labels-file
label_cfg_file = container_options.get('labels_file')
label_list_cfg_file = load(label_cfg_file)
service_cmd_opts = [a_serv_lbl for a_serv_lbl in label_list_cfg_file
if a_serv_lbl.get('name') == self.name
and a_serv_lbl.get('labels') is not None]
if len(service_cmd_opts) == 0:
raise ConfigError ('No labels defined for service \'' +
self.name + '\' in ' +
container_options.get('labels_file'))
service_cmd_lbls = service_cmd_opts[0]['labels']
service_cfg_lbls = container_options.get('labels')
prefix_cmd = ""
prefix_cfg = ""
if 'prefix_labels' in container_options:
prefix_cmd = ".cmd:"
prefix_cfg = ".cfg:"
del container_options['prefix_labels']
temp_labels = dict()
if service_cfg_lbls is not None:
temp_labels.update(
(prefix_cfg + key, service_cfg_lbls[key]) for key in service_cfg_lbls)
temp_labels.update(
(prefix_cmd + key, service_cmd_lbls[key]) for key in service_cmd_lbls)

container_options['labels'] = temp_labels
del container_options['labels_file']


# If a qualified hostname was given, split it into an
# unqualified hostname and a domainname unless domainname
# was also given explicitly. This matches the behavior of
Expand DownExpand Up@@ -553,7 +603,6 @@ def parse_volume_spec(volume_config):

return VolumeSpec(external, internal, mode)


def parse_repository_tag(s):
if ":" not in s:
return s, ""
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 83 additions & 24 deletions compose/cli/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@
from .. import __version__
from ..project import NoSuchService, ConfigurationError
from ..service import BuildError, CannotBeScaledError
from ..config import parse_environment
from ..config import parse_environment, load
from .command import Command
from .docopt_command import NoSuchCommand
from .errors import UserError
Expand DownExpand Up@@ -270,19 +270,29 @@ def run(self, project, options):
Usage: run [options] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]

Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run container in the background, print
new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple times)
-u, --user="" Run as specified username or uid
--no-deps Don't start linked services.
--rm Remove container after run. Ignored in detached mode.
--service-ports Run command with the service's ports enabled and mapped
to the host.
-T Disable pseudo-tty allocation. By default `docker-compose run`
allocates a TTY.
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run container in the background,
print new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple
times)
--labels-file FILE Uses all labels, in the given yaml FILE for the
SERVICE to be run.
--no-deps Don't start linked services.
--no-prefix-labels Don't prefix '.cfg' and '.cmd' when --labels-file
is used.
--remove-labels [REGEX] Removes all labels. If REGEX is given, removes
labels that match the given REGular EXpression.
(Note: This option has less priority than the
given --labels-file)
--rm Remove container after run. Ignored in detached
mode.
--service-ports Run command with the service's ports enabled and
mapped to the host.
-T Disable pseudo-tty allocation. By default
`docker-compose run` allocates a TTY.
-u, --user="" Run as specified username or uid.
"""
service = project.get_service(options['SERVICE'])

Expand DownExpand Up@@ -314,8 +324,42 @@ def run(self, project, options):
'tty': tty,
'stdin_open': not options['-d'],
'detach': options['-d'],
'labels': dict(),
}

if options['--remove-labels']:
service_cfg_lbls = service.options.get('labels')
if service_cfg_lbls is not None:
regex = options.get('--remove-labels')
a = re.compile(regex)
service.options['labels'] = dict(
(k, v) for (k,v) in service_cfg_lbls.iteritems()
if not a.match(k))

if options['--labels-file']:
# Merge labels from config with labels from --labels-file
label_cfg_file = self.get_config_path(options.get('--labels-file'))
label_list_cfg_file = load(label_cfg_file)
service_cmd_opts = [a_serv_lbl for a_serv_lbl in label_list_cfg_file
if a_serv_lbl.get('name') == service.name
and a_serv_lbl.get('labels') is not None]
if len(service_cmd_opts) == 0:
raise UserError('No labels defined for service \'' + service.name +
'\' in ' + options.get('--labels-file'))
service_cmd_lbls = service_cmd_opts[0]['labels']
service_cfg_lbls = service.options.get('labels')
prefix_cmd = ""
prefix_cfg = ""
if not options['--no-prefix-labels']:
prefix_cmd = ".cmd:"
prefix_cfg = ".cfg:"

if service_cfg_lbls is not None:
container_options['labels'].update(
(prefix_cfg + key, service_cfg_lbls[key]) for key in service_cfg_lbls)
container_options['labels'].update(
(prefix_cmd + key, service_cmd_lbls[key]) for key in service_cmd_lbls)

if options['-e']:
# Merge environment from config with -e command line
container_options['environment'] = dict(
Expand DownExpand Up@@ -434,23 +478,35 @@ def up(self, project, options):
Usage: up [options] [SERVICE...]

Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run containers in the background,
print new container names.
--no-color Produce monochrome output.
--no-deps Don't start linked services.
--no-recreate If containers already exist, don't recreate them.
--no-build Don't build an image, even if it's missing
-t, --timeout TIMEOUT When attached, use this timeout in seconds
for the shutdown. (default: 10)
--allow-insecure-ssl Allow insecure connections to the docker registry.
-d Detached mode: Run containers in the background,
print new container names.
--labels-file FILE Uses all labels, in the given yaml FILE for the
SERVICEs to be up.
--no-build Don't build an image, even if it's missing.
--no-deps Don't start linked services.
--no-color Produce monochrome output.
--no-prefix-labels Don't prefix '.cfg' and '.cmd' when --labels-file
is used.
--no-recreate If containers already exist, don't recreate them.
--remove-labels [REGEX] Removes all labels. If REGEX is given, removes
labels that match the given REGular EXpression.
(Note: This option has less priority than the
given --labels-file).
-t, --timeout TIMEOUT When attached, use this timeout in seconds for the
shutdown. (default: 10)

"""
insecure_registry = options['--allow-insecure-ssl']
detached = options['-d']

monochrome = options['--no-color']

if options['--labels-file']:
labels_file = self.get_config_path(options['--labels-file'])
else:
labels_file = ''

start_deps = not options['--no-deps']
recreate = not options['--no-recreate']
service_names = options['SERVICE']
Expand All@@ -462,6 +518,9 @@ def up(self, project, options):
insecure_registry=insecure_registry,
detach=detached,
do_build=not options['--no-build'],
prefix_labels=not options['--no-prefix-labels'],
remove_labels=str(options['--remove-labels']),
labels_file=labels_file
)

to_attach = [c for s in project.get_services(service_names) for c in s.containers()]
Expand Down
3 changes: 1 addition & 2 deletions compose/config.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
'environment',
'hostname',
'image',
'labels',
'links',
'mem_limit',
'net',
Expand DownExpand Up@@ -138,7 +139,6 @@ def resolve_environment(service_dict, working_dir=None):
service_dict['environment'] = env
return service_dict


def parse_environment(environment):
if not environment:
return {}
Expand All@@ -154,7 +154,6 @@ def parse_environment(environment):
environment
)


def split_env(env):
if '=' in env:
return env.split('=', 1)
Expand Down
16 changes: 13 additions & 3 deletions compose/project.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -209,20 +209,30 @@ def up(self,
recreate=True,
insecure_registry=False,
detach=False,
do_build=True):
do_build=True,
prefix_labels=False,
remove_labels='',
labels_file=None
):
running_containers = []
for service in self.get_services(service_names, include_deps=start_deps):
if recreate:
for (_, container) in service.recreate_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
do_build=do_build,
prefix_labels=prefix_labels,
remove_labels=remove_labels,
labels_file=labels_file):
running_containers.append(container)
else:
for container in service.start_or_create_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
do_build=do_build,
prefix_labels=prefix_labels,
remove_labels=remove_labels,
labels_file=labels_file):
running_containers.append(container)

return running_containers
Expand Down
57 changes: 53 additions & 4 deletions compose/service.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@

from docker.errors import APIError

from .config import DOCKER_CONFIG_KEYS
from .config import DOCKER_CONFIG_KEYS, load
from .container import Container, get_container_name
from .progress_stream import stream_output, StreamOutputError

Expand All@@ -29,6 +29,7 @@

VALID_NAME_CHARS = '[a-zA-Z0-9]'

LABEL_PREFIX = ''

class BuildError(Exception):
def __init__(self, service, reason):
Expand All@@ -50,6 +51,9 @@ class ConfigError(ValueError):
ServiceName = namedtuple('ServiceName', 'project service number')


LabelSpec = namedtuple('LabelSpec', 'key value')


class Service(object):
def __init__(self, name, client=None, project='default', links=None, external_links=None, volumes_from=None, net=None, **options):
if not re.match('^%s+$' % VALID_NAME_CHARS, name):
Expand DownExpand Up@@ -181,7 +185,6 @@ def create_container(self,
self.can_be_built() and
not self.client.images(name=self.full_name)):
self.build()

try:
return Container.create(self.client, **container_options)
except APIError as e:
Expand DownExpand Up@@ -296,7 +299,8 @@ def start_or_create_containers(
self,
insecure_registry=False,
detach=False,
do_build=True):
do_build=True,
**override_options):
containers = self.containers(stopped=True)

if not containers:
Expand All@@ -305,6 +309,7 @@ def start_or_create_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build,
**override_options
)
return [self.start_container(new_container)]
else:
Expand DownExpand Up@@ -399,6 +404,51 @@ def _get_container_create_options(self, override_options, one_off=False):
self.containers(stopped=True, one_off=one_off),
one_off)

if 'labels' in container_options:
container_options['labels'] = dict(
(LABEL_PREFIX + key, container_options['labels'][key])
for key in container_options['labels'])

if 'remove_labels' in container_options:
service_cfg_lbls = container_options.get('labels')
if service_cfg_lbls is not None:
regex = container_options.get('remove_labels')
a = re.compile(regex)
container_options['labels'] = dict(
(k, v) for (k,v) in service_cfg_lbls.iteritems()
if not a.match(k))
del container_options['remove_labels']

if 'labels_file' in container_options:
# Merge labels from config with labels from --labels-file
label_cfg_file = container_options.get('labels_file')
label_list_cfg_file = load(label_cfg_file)
service_cmd_opts = [a_serv_lbl for a_serv_lbl in label_list_cfg_file
if a_serv_lbl.get('name') == self.name
and a_serv_lbl.get('labels') is not None]
if len(service_cmd_opts) == 0:
raise ConfigError ('No labels defined for service \'' +
self.name + '\' in ' +
container_options.get('labels_file'))
service_cmd_lbls = service_cmd_opts[0]['labels']
service_cfg_lbls = container_options.get('labels')
prefix_cmd = ""
prefix_cfg = ""
if 'prefix_labels' in container_options:
prefix_cmd = ".cmd:"
prefix_cfg = ".cfg:"
del container_options['prefix_labels']
temp_labels = dict()
if service_cfg_lbls is not None:
temp_labels.update(
(prefix_cfg + key, service_cfg_lbls[key]) for key in service_cfg_lbls)
temp_labels.update(
(prefix_cmd + key, service_cmd_lbls[key]) for key in service_cmd_lbls)

container_options['labels'] = temp_labels
del container_options['labels_file']


# If a qualified hostname was given, split it into an
# unqualified hostname and a domainname unless domainname
# was also given explicitly. This matches the behavior of
Expand DownExpand Up@@ -553,7 +603,6 @@ def parse_volume_spec(volume_config):

return VolumeSpec(external, internal, mode)


def parse_repository_tag(s):
if ":" not in s:
return s, ""
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 83 additions & 24 deletions compose/cli/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@
from .. import __version__
from ..project import NoSuchService, ConfigurationError
from ..service import BuildError, CannotBeScaledError
from ..config import parse_environment
from ..config import parse_environment, load
from .command import Command
from .docopt_command import NoSuchCommand
from .errors import UserError
Expand DownExpand Up@@ -270,19 +270,29 @@ def run(self, project, options):
Usage: run [options] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]

Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run container in the background, print
new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple times)
-u, --user="" Run as specified username or uid
--no-deps Don't start linked services.
--rm Remove container after run. Ignored in detached mode.
--service-ports Run command with the service's ports enabled and mapped
to the host.
-T Disable pseudo-tty allocation. By default `docker-compose run`
allocates a TTY.
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run container in the background,
print new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple
times)
--labels-file FILE Uses all labels, in the given yaml FILE for the
SERVICE to be run.
--no-deps Don't start linked services.
--no-prefix-labels Don't prefix '.cfg' and '.cmd' when --labels-file
is used.
--remove-labels [REGEX] Removes all labels. If REGEX is given, removes
labels that match the given REGular EXpression.
(Note: This option has less priority than the
given --labels-file)
--rm Remove container after run. Ignored in detached
mode.
--service-ports Run command with the service's ports enabled and
mapped to the host.
-T Disable pseudo-tty allocation. By default
`docker-compose run` allocates a TTY.
-u, --user="" Run as specified username or uid.
"""
service = project.get_service(options['SERVICE'])

Expand DownExpand Up@@ -314,8 +324,42 @@ def run(self, project, options):
'tty': tty,
'stdin_open': not options['-d'],
'detach': options['-d'],
'labels': dict(),
}

if options['--remove-labels']:
service_cfg_lbls = service.options.get('labels')
if service_cfg_lbls is not None:
regex = options.get('--remove-labels')
a = re.compile(regex)
service.options['labels'] = dict(
(k, v) for (k,v) in service_cfg_lbls.iteritems()
if not a.match(k))

if options['--labels-file']:
# Merge labels from config with labels from --labels-file
label_cfg_file = self.get_config_path(options.get('--labels-file'))
label_list_cfg_file = load(label_cfg_file)
service_cmd_opts = [a_serv_lbl for a_serv_lbl in label_list_cfg_file
if a_serv_lbl.get('name') == service.name
and a_serv_lbl.get('labels') is not None]
if len(service_cmd_opts) == 0:
raise UserError('No labels defined for service \'' + service.name +
'\' in ' + options.get('--labels-file'))
service_cmd_lbls = service_cmd_opts[0]['labels']
service_cfg_lbls = service.options.get('labels')
prefix_cmd = ""
prefix_cfg = ""
if not options['--no-prefix-labels']:
prefix_cmd = ".cmd:"
prefix_cfg = ".cfg:"

if service_cfg_lbls is not None:
container_options['labels'].update(
(prefix_cfg + key, service_cfg_lbls[key]) for key in service_cfg_lbls)
container_options['labels'].update(
(prefix_cmd + key, service_cmd_lbls[key]) for key in service_cmd_lbls)

if options['-e']:
# Merge environment from config with -e command line
container_options['environment'] = dict(
Expand DownExpand Up@@ -434,23 +478,35 @@ def up(self, project, options):
Usage: up [options] [SERVICE...]

Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run containers in the background,
print new container names.
--no-color Produce monochrome output.
--no-deps Don't start linked services.
--no-recreate If containers already exist, don't recreate them.
--no-build Don't build an image, even if it's missing
-t, --timeout TIMEOUT When attached, use this timeout in seconds
for the shutdown. (default: 10)
--allow-insecure-ssl Allow insecure connections to the docker registry.
-d Detached mode: Run containers in the background,
print new container names.
--labels-file FILE Uses all labels, in the given yaml FILE for the
SERVICEs to be up.
--no-build Don't build an image, even if it's missing.
--no-deps Don't start linked services.
--no-color Produce monochrome output.
--no-prefix-labels Don't prefix '.cfg' and '.cmd' when --labels-file
is used.
--no-recreate If containers already exist, don't recreate them.
--remove-labels [REGEX] Removes all labels. If REGEX is given, removes
labels that match the given REGular EXpression.
(Note: This option has less priority than the
given --labels-file).
-t, --timeout TIMEOUT When attached, use this timeout in seconds for the
shutdown. (default: 10)

"""
insecure_registry = options['--allow-insecure-ssl']
detached = options['-d']

monochrome = options['--no-color']

if options['--labels-file']:
labels_file = self.get_config_path(options['--labels-file'])
else:
labels_file = ''

start_deps = not options['--no-deps']
recreate = not options['--no-recreate']
service_names = options['SERVICE']
Expand All@@ -462,6 +518,9 @@ def up(self, project, options):
insecure_registry=insecure_registry,
detach=detached,
do_build=not options['--no-build'],
prefix_labels=not options['--no-prefix-labels'],
remove_labels=str(options['--remove-labels']),
labels_file=labels_file
)

to_attach = [c for s in project.get_services(service_names) for c in s.containers()]
Expand Down
3 changes: 1 addition & 2 deletions compose/config.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
'environment',
'hostname',
'image',
'labels',
'links',
'mem_limit',
'net',
Expand DownExpand Up@@ -138,7 +139,6 @@ def resolve_environment(service_dict, working_dir=None):
service_dict['environment'] = env
return service_dict


def parse_environment(environment):
if not environment:
return {}
Expand All@@ -154,7 +154,6 @@ def parse_environment(environment):
environment
)


def split_env(env):
if '=' in env:
return env.split('=', 1)
Expand Down
16 changes: 13 additions & 3 deletions compose/project.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -209,20 +209,30 @@ def up(self,
recreate=True,
insecure_registry=False,
detach=False,
do_build=True):
do_build=True,
prefix_labels=False,
remove_labels='',
labels_file=None
):
running_containers = []
for service in self.get_services(service_names, include_deps=start_deps):
if recreate:
for (_, container) in service.recreate_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
do_build=do_build,
prefix_labels=prefix_labels,
remove_labels=remove_labels,
labels_file=labels_file):
running_containers.append(container)
else:
for container in service.start_or_create_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
do_build=do_build,
prefix_labels=prefix_labels,
remove_labels=remove_labels,
labels_file=labels_file):
running_containers.append(container)

return running_containers
Expand Down
57 changes: 53 additions & 4 deletions compose/service.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@

from docker.errors import APIError

from .config import DOCKER_CONFIG_KEYS
from .config import DOCKER_CONFIG_KEYS, load
from .container import Container, get_container_name
from .progress_stream import stream_output, StreamOutputError

Expand All@@ -29,6 +29,7 @@

VALID_NAME_CHARS = '[a-zA-Z0-9]'

LABEL_PREFIX = ''

class BuildError(Exception):
def __init__(self, service, reason):
Expand All@@ -50,6 +51,9 @@ class ConfigError(ValueError):
ServiceName = namedtuple('ServiceName', 'project service number')


LabelSpec = namedtuple('LabelSpec', 'key value')


class Service(object):
def __init__(self, name, client=None, project='default', links=None, external_links=None, volumes_from=None, net=None, **options):
if not re.match('^%s+$' % VALID_NAME_CHARS, name):
Expand DownExpand Up@@ -181,7 +185,6 @@ def create_container(self,
self.can_be_built() and
not self.client.images(name=self.full_name)):
self.build()

try:
return Container.create(self.client, **container_options)
except APIError as e:
Expand DownExpand Up@@ -296,7 +299,8 @@ def start_or_create_containers(
self,
insecure_registry=False,
detach=False,
do_build=True):
do_build=True,
**override_options):
containers = self.containers(stopped=True)

if not containers:
Expand All@@ -305,6 +309,7 @@ def start_or_create_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build,
**override_options
)
return [self.start_container(new_container)]
else:
Expand DownExpand Up@@ -399,6 +404,51 @@ def _get_container_create_options(self, override_options, one_off=False):
self.containers(stopped=True, one_off=one_off),
one_off)

if 'labels' in container_options:
container_options['labels'] = dict(
(LABEL_PREFIX + key, container_options['labels'][key])
for key in container_options['labels'])

if 'remove_labels' in container_options:
service_cfg_lbls = container_options.get('labels')
if service_cfg_lbls is not None:
regex = container_options.get('remove_labels')
a = re.compile(regex)
container_options['labels'] = dict(
(k, v) for (k,v) in service_cfg_lbls.iteritems()
if not a.match(k))
del container_options['remove_labels']

if 'labels_file' in container_options:
# Merge labels from config with labels from --labels-file
label_cfg_file = container_options.get('labels_file')
label_list_cfg_file = load(label_cfg_file)
service_cmd_opts = [a_serv_lbl for a_serv_lbl in label_list_cfg_file
if a_serv_lbl.get('name') == self.name
and a_serv_lbl.get('labels') is not None]
if len(service_cmd_opts) == 0:
raise ConfigError ('No labels defined for service \'' +
self.name + '\' in ' +
container_options.get('labels_file'))
service_cmd_lbls = service_cmd_opts[0]['labels']
service_cfg_lbls = container_options.get('labels')
prefix_cmd = ""
prefix_cfg = ""
if 'prefix_labels' in container_options:
prefix_cmd = ".cmd:"
prefix_cfg = ".cfg:"
del container_options['prefix_labels']
temp_labels = dict()
if service_cfg_lbls is not None:
temp_labels.update(
(prefix_cfg + key, service_cfg_lbls[key]) for key in service_cfg_lbls)
temp_labels.update(
(prefix_cmd + key, service_cmd_lbls[key]) for key in service_cmd_lbls)

container_options['labels'] = temp_labels
del container_options['labels_file']


# If a qualified hostname was given, split it into an
# unqualified hostname and a domainname unless domainname
# was also given explicitly. This matches the behavior of
Expand DownExpand Up@@ -553,7 +603,6 @@ def parse_volume_spec(volume_config):

return VolumeSpec(external, internal, mode)


def parse_repository_tag(s):
if ":" not in s:
return s, ""
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 83 additions & 24 deletions compose/cli/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@
from .. import __version__
from ..project import NoSuchService, ConfigurationError
from ..service import BuildError, CannotBeScaledError
from ..config import parse_environment
from ..config import parse_environment, load
from .command import Command
from .docopt_command import NoSuchCommand
from .errors import UserError
Expand DownExpand Up@@ -270,19 +270,29 @@ def run(self, project, options):
Usage: run [options] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]

Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run container in the background, print
new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple times)
-u, --user="" Run as specified username or uid
--no-deps Don't start linked services.
--rm Remove container after run. Ignored in detached mode.
--service-ports Run command with the service's ports enabled and mapped
to the host.
-T Disable pseudo-tty allocation. By default `docker-compose run`
allocates a TTY.
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run container in the background,
print new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple
times)
--labels-file FILE Uses all labels, in the given yaml FILE for the
SERVICE to be run.
--no-deps Don't start linked services.
--no-prefix-labels Don't prefix '.cfg' and '.cmd' when --labels-file
is used.
--remove-labels [REGEX] Removes all labels. If REGEX is given, removes
labels that match the given REGular EXpression.
(Note: This option has less priority than the
given --labels-file)
--rm Remove container after run. Ignored in detached
mode.
--service-ports Run command with the service's ports enabled and
mapped to the host.
-T Disable pseudo-tty allocation. By default
`docker-compose run` allocates a TTY.
-u, --user="" Run as specified username or uid.
"""
service = project.get_service(options['SERVICE'])

Expand DownExpand Up@@ -314,8 +324,42 @@ def run(self, project, options):
'tty': tty,
'stdin_open': not options['-d'],
'detach': options['-d'],
'labels': dict(),
}

if options['--remove-labels']:
service_cfg_lbls = service.options.get('labels')
if service_cfg_lbls is not None:
regex = options.get('--remove-labels')
a = re.compile(regex)
service.options['labels'] = dict(
(k, v) for (k,v) in service_cfg_lbls.iteritems()
if not a.match(k))

if options['--labels-file']:
# Merge labels from config with labels from --labels-file
label_cfg_file = self.get_config_path(options.get('--labels-file'))
label_list_cfg_file = load(label_cfg_file)
service_cmd_opts = [a_serv_lbl for a_serv_lbl in label_list_cfg_file
if a_serv_lbl.get('name') == service.name
and a_serv_lbl.get('labels') is not None]
if len(service_cmd_opts) == 0:
raise UserError('No labels defined for service \'' + service.name +
'\' in ' + options.get('--labels-file'))
service_cmd_lbls = service_cmd_opts[0]['labels']
service_cfg_lbls = service.options.get('labels')
prefix_cmd = ""
prefix_cfg = ""
if not options['--no-prefix-labels']:
prefix_cmd = ".cmd:"
prefix_cfg = ".cfg:"

if service_cfg_lbls is not None:
container_options['labels'].update(
(prefix_cfg + key, service_cfg_lbls[key]) for key in service_cfg_lbls)
container_options['labels'].update(
(prefix_cmd + key, service_cmd_lbls[key]) for key in service_cmd_lbls)

if options['-e']:
# Merge environment from config with -e command line
container_options['environment'] = dict(
Expand DownExpand Up@@ -434,23 +478,35 @@ def up(self, project, options):
Usage: up [options] [SERVICE...]

Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run containers in the background,
print new container names.
--no-color Produce monochrome output.
--no-deps Don't start linked services.
--no-recreate If containers already exist, don't recreate them.
--no-build Don't build an image, even if it's missing
-t, --timeout TIMEOUT When attached, use this timeout in seconds
for the shutdown. (default: 10)
--allow-insecure-ssl Allow insecure connections to the docker registry.
-d Detached mode: Run containers in the background,
print new container names.
--labels-file FILE Uses all labels, in the given yaml FILE for the
SERVICEs to be up.
--no-build Don't build an image, even if it's missing.
--no-deps Don't start linked services.
--no-color Produce monochrome output.
--no-prefix-labels Don't prefix '.cfg' and '.cmd' when --labels-file
is used.
--no-recreate If containers already exist, don't recreate them.
--remove-labels [REGEX] Removes all labels. If REGEX is given, removes
labels that match the given REGular EXpression.
(Note: This option has less priority than the
given --labels-file).
-t, --timeout TIMEOUT When attached, use this timeout in seconds for the
shutdown. (default: 10)

"""
insecure_registry = options['--allow-insecure-ssl']
detached = options['-d']

monochrome = options['--no-color']

if options['--labels-file']:
labels_file = self.get_config_path(options['--labels-file'])
else:
labels_file = ''

start_deps = not options['--no-deps']
recreate = not options['--no-recreate']
service_names = options['SERVICE']
Expand All@@ -462,6 +518,9 @@ def up(self, project, options):
insecure_registry=insecure_registry,
detach=detached,
do_build=not options['--no-build'],
prefix_labels=not options['--no-prefix-labels'],
remove_labels=str(options['--remove-labels']),
labels_file=labels_file
)

to_attach = [c for s in project.get_services(service_names) for c in s.containers()]
Expand Down
3 changes: 1 addition & 2 deletions compose/config.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
'environment',
'hostname',
'image',
'labels',
'links',
'mem_limit',
'net',
Expand DownExpand Up@@ -138,7 +139,6 @@ def resolve_environment(service_dict, working_dir=None):
service_dict['environment'] = env
return service_dict


def parse_environment(environment):
if not environment:
return {}
Expand All@@ -154,7 +154,6 @@ def parse_environment(environment):
environment
)


def split_env(env):
if '=' in env:
return env.split('=', 1)
Expand Down
16 changes: 13 additions & 3 deletions compose/project.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -209,20 +209,30 @@ def up(self,
recreate=True,
insecure_registry=False,
detach=False,
do_build=True):
do_build=True,
prefix_labels=False,
remove_labels='',
labels_file=None
):
running_containers = []
for service in self.get_services(service_names, include_deps=start_deps):
if recreate:
for (_, container) in service.recreate_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
do_build=do_build,
prefix_labels=prefix_labels,
remove_labels=remove_labels,
labels_file=labels_file):
running_containers.append(container)
else:
for container in service.start_or_create_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
do_build=do_build,
prefix_labels=prefix_labels,
remove_labels=remove_labels,
labels_file=labels_file):
running_containers.append(container)

return running_containers
Expand Down
57 changes: 53 additions & 4 deletions compose/service.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@

from docker.errors import APIError

from .config import DOCKER_CONFIG_KEYS
from .config import DOCKER_CONFIG_KEYS, load
from .container import Container, get_container_name
from .progress_stream import stream_output, StreamOutputError

Expand All@@ -29,6 +29,7 @@

VALID_NAME_CHARS = '[a-zA-Z0-9]'

LABEL_PREFIX = ''

class BuildError(Exception):
def __init__(self, service, reason):
Expand All@@ -50,6 +51,9 @@ class ConfigError(ValueError):
ServiceName = namedtuple('ServiceName', 'project service number')


LabelSpec = namedtuple('LabelSpec', 'key value')


class Service(object):
def __init__(self, name, client=None, project='default', links=None, external_links=None, volumes_from=None, net=None, **options):
if not re.match('^%s+$' % VALID_NAME_CHARS, name):
Expand DownExpand Up@@ -181,7 +185,6 @@ def create_container(self,
self.can_be_built() and
not self.client.images(name=self.full_name)):
self.build()

try:
return Container.create(self.client, **container_options)
except APIError as e:
Expand DownExpand Up@@ -296,7 +299,8 @@ def start_or_create_containers(
self,
insecure_registry=False,
detach=False,
do_build=True):
do_build=True,
**override_options):
containers = self.containers(stopped=True)

if not containers:
Expand All@@ -305,6 +309,7 @@ def start_or_create_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build,
**override_options
)
return [self.start_container(new_container)]
else:
Expand DownExpand Up@@ -399,6 +404,51 @@ def _get_container_create_options(self, override_options, one_off=False):
self.containers(stopped=True, one_off=one_off),
one_off)

if 'labels' in container_options:
container_options['labels'] = dict(
(LABEL_PREFIX + key, container_options['labels'][key])
for key in container_options['labels'])

if 'remove_labels' in container_options:
service_cfg_lbls = container_options.get('labels')
if service_cfg_lbls is not None:
regex = container_options.get('remove_labels')
a = re.compile(regex)
container_options['labels'] = dict(
(k, v) for (k,v) in service_cfg_lbls.iteritems()
if not a.match(k))
del container_options['remove_labels']

if 'labels_file' in container_options:
# Merge labels from config with labels from --labels-file
label_cfg_file = container_options.get('labels_file')
label_list_cfg_file = load(label_cfg_file)
service_cmd_opts = [a_serv_lbl for a_serv_lbl in label_list_cfg_file
if a_serv_lbl.get('name') == self.name
and a_serv_lbl.get('labels') is not None]
if len(service_cmd_opts) == 0:
raise ConfigError ('No labels defined for service \'' +
self.name + '\' in ' +
container_options.get('labels_file'))
service_cmd_lbls = service_cmd_opts[0]['labels']
service_cfg_lbls = container_options.get('labels')
prefix_cmd = ""
prefix_cfg = ""
if 'prefix_labels' in container_options:
prefix_cmd = ".cmd:"
prefix_cfg = ".cfg:"
del container_options['prefix_labels']
temp_labels = dict()
if service_cfg_lbls is not None:
temp_labels.update(
(prefix_cfg + key, service_cfg_lbls[key]) for key in service_cfg_lbls)
temp_labels.update(
(prefix_cmd + key, service_cmd_lbls[key]) for key in service_cmd_lbls)

container_options['labels'] = temp_labels
del container_options['labels_file']


# If a qualified hostname was given, split it into an
# unqualified hostname and a domainname unless domainname
# was also given explicitly. This matches the behavior of
Expand DownExpand Up@@ -553,7 +603,6 @@ def parse_volume_spec(volume_config):

return VolumeSpec(external, internal, mode)


def parse_repository_tag(s):
if ":" not in s:
return s, ""
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 83 additions & 24 deletions compose/cli/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@
from .. import __version__
from ..project import NoSuchService, ConfigurationError
from ..service import BuildError, CannotBeScaledError
from ..config import parse_environment
from ..config import parse_environment, load
from .command import Command
from .docopt_command import NoSuchCommand
from .errors import UserError
Expand DownExpand Up@@ -270,19 +270,29 @@ def run(self, project, options):
Usage: run [options] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]

Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run container in the background, print
new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple times)
-u, --user="" Run as specified username or uid
--no-deps Don't start linked services.
--rm Remove container after run. Ignored in detached mode.
--service-ports Run command with the service's ports enabled and mapped
to the host.
-T Disable pseudo-tty allocation. By default `docker-compose run`
allocates a TTY.
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run container in the background,
print new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple
times)
--labels-file FILE Uses all labels, in the given yaml FILE for the
SERVICE to be run.
--no-deps Don't start linked services.
--no-prefix-labels Don't prefix '.cfg' and '.cmd' when --labels-file
is used.
--remove-labels [REGEX] Removes all labels. If REGEX is given, removes
labels that match the given REGular EXpression.
(Note: This option has less priority than the
given --labels-file)
--rm Remove container after run. Ignored in detached
mode.
--service-ports Run command with the service's ports enabled and
mapped to the host.
-T Disable pseudo-tty allocation. By default
`docker-compose run` allocates a TTY.
-u, --user="" Run as specified username or uid.
"""
service = project.get_service(options['SERVICE'])

Expand DownExpand Up@@ -314,8 +324,42 @@ def run(self, project, options):
'tty': tty,
'stdin_open': not options['-d'],
'detach': options['-d'],
'labels': dict(),
}

if options['--remove-labels']:
service_cfg_lbls = service.options.get('labels')
if service_cfg_lbls is not None:
regex = options.get('--remove-labels')
a = re.compile(regex)
service.options['labels'] = dict(
(k, v) for (k,v) in service_cfg_lbls.iteritems()
if not a.match(k))

if options['--labels-file']:
# Merge labels from config with labels from --labels-file
label_cfg_file = self.get_config_path(options.get('--labels-file'))
label_list_cfg_file = load(label_cfg_file)
service_cmd_opts = [a_serv_lbl for a_serv_lbl in label_list_cfg_file
if a_serv_lbl.get('name') == service.name
and a_serv_lbl.get('labels') is not None]
if len(service_cmd_opts) == 0:
raise UserError('No labels defined for service \'' + service.name +
'\' in ' + options.get('--labels-file'))
service_cmd_lbls = service_cmd_opts[0]['labels']
service_cfg_lbls = service.options.get('labels')
prefix_cmd = ""
prefix_cfg = ""
if not options['--no-prefix-labels']:
prefix_cmd = ".cmd:"
prefix_cfg = ".cfg:"

if service_cfg_lbls is not None:
container_options['labels'].update(
(prefix_cfg + key, service_cfg_lbls[key]) for key in service_cfg_lbls)
container_options['labels'].update(
(prefix_cmd + key, service_cmd_lbls[key]) for key in service_cmd_lbls)

if options['-e']:
# Merge environment from config with -e command line
container_options['environment'] = dict(
Expand DownExpand Up@@ -434,23 +478,35 @@ def up(self, project, options):
Usage: up [options] [SERVICE...]

Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run containers in the background,
print new container names.
--no-color Produce monochrome output.
--no-deps Don't start linked services.
--no-recreate If containers already exist, don't recreate them.
--no-build Don't build an image, even if it's missing
-t, --timeout TIMEOUT When attached, use this timeout in seconds
for the shutdown. (default: 10)
--allow-insecure-ssl Allow insecure connections to the docker registry.
-d Detached mode: Run containers in the background,
print new container names.
--labels-file FILE Uses all labels, in the given yaml FILE for the
SERVICEs to be up.
--no-build Don't build an image, even if it's missing.
--no-deps Don't start linked services.
--no-color Produce monochrome output.
--no-prefix-labels Don't prefix '.cfg' and '.cmd' when --labels-file
is used.
--no-recreate If containers already exist, don't recreate them.
--remove-labels [REGEX] Removes all labels. If REGEX is given, removes
labels that match the given REGular EXpression.
(Note: This option has less priority than the
given --labels-file).
-t, --timeout TIMEOUT When attached, use this timeout in seconds for the
shutdown. (default: 10)

"""
insecure_registry = options['--allow-insecure-ssl']
detached = options['-d']

monochrome = options['--no-color']

if options['--labels-file']:
labels_file = self.get_config_path(options['--labels-file'])
else:
labels_file = ''

start_deps = not options['--no-deps']
recreate = not options['--no-recreate']
service_names = options['SERVICE']
Expand All@@ -462,6 +518,9 @@ def up(self, project, options):
insecure_registry=insecure_registry,
detach=detached,
do_build=not options['--no-build'],
prefix_labels=not options['--no-prefix-labels'],
remove_labels=str(options['--remove-labels']),
labels_file=labels_file
)

to_attach = [c for s in project.get_services(service_names) for c in s.containers()]
Expand Down
3 changes: 1 addition & 2 deletions compose/config.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
'environment',
'hostname',
'image',
'labels',
'links',
'mem_limit',
'net',
Expand DownExpand Up@@ -138,7 +139,6 @@ def resolve_environment(service_dict, working_dir=None):
service_dict['environment'] = env
return service_dict


def parse_environment(environment):
if not environment:
return {}
Expand All@@ -154,7 +154,6 @@ def parse_environment(environment):
environment
)


def split_env(env):
if '=' in env:
return env.split('=', 1)
Expand Down
16 changes: 13 additions & 3 deletions compose/project.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -209,20 +209,30 @@ def up(self,
recreate=True,
insecure_registry=False,
detach=False,
do_build=True):
do_build=True,
prefix_labels=False,
remove_labels='',
labels_file=None
):
running_containers = []
for service in self.get_services(service_names, include_deps=start_deps):
if recreate:
for (_, container) in service.recreate_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
do_build=do_build,
prefix_labels=prefix_labels,
remove_labels=remove_labels,
labels_file=labels_file):
running_containers.append(container)
else:
for container in service.start_or_create_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
do_build=do_build,
prefix_labels=prefix_labels,
remove_labels=remove_labels,
labels_file=labels_file):
running_containers.append(container)

return running_containers
Expand Down
57 changes: 53 additions & 4 deletions compose/service.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@

from docker.errors import APIError

from .config import DOCKER_CONFIG_KEYS
from .config import DOCKER_CONFIG_KEYS, load
from .container import Container, get_container_name
from .progress_stream import stream_output, StreamOutputError

Expand All@@ -29,6 +29,7 @@

VALID_NAME_CHARS = '[a-zA-Z0-9]'

LABEL_PREFIX = ''

class BuildError(Exception):
def __init__(self, service, reason):
Expand All@@ -50,6 +51,9 @@ class ConfigError(ValueError):
ServiceName = namedtuple('ServiceName', 'project service number')


LabelSpec = namedtuple('LabelSpec', 'key value')


class Service(object):
def __init__(self, name, client=None, project='default', links=None, external_links=None, volumes_from=None, net=None, **options):
if not re.match('^%s+$' % VALID_NAME_CHARS, name):
Expand DownExpand Up@@ -181,7 +185,6 @@ def create_container(self,
self.can_be_built() and
not self.client.images(name=self.full_name)):
self.build()

try:
return Container.create(self.client, **container_options)
except APIError as e:
Expand DownExpand Up@@ -296,7 +299,8 @@ def start_or_create_containers(
self,
insecure_registry=False,
detach=False,
do_build=True):
do_build=True,
**override_options):
containers = self.containers(stopped=True)

if not containers:
Expand All@@ -305,6 +309,7 @@ def start_or_create_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build,
**override_options
)
return [self.start_container(new_container)]
else:
Expand DownExpand Up@@ -399,6 +404,51 @@ def _get_container_create_options(self, override_options, one_off=False):
self.containers(stopped=True, one_off=one_off),
one_off)

if 'labels' in container_options:
container_options['labels'] = dict(
(LABEL_PREFIX + key, container_options['labels'][key])
for key in container_options['labels'])

if 'remove_labels' in container_options:
service_cfg_lbls = container_options.get('labels')
if service_cfg_lbls is not None:
regex = container_options.get('remove_labels')
a = re.compile(regex)
container_options['labels'] = dict(
(k, v) for (k,v) in service_cfg_lbls.iteritems()
if not a.match(k))
del container_options['remove_labels']

if 'labels_file' in container_options:
# Merge labels from config with labels from --labels-file
label_cfg_file = container_options.get('labels_file')
label_list_cfg_file = load(label_cfg_file)
service_cmd_opts = [a_serv_lbl for a_serv_lbl in label_list_cfg_file
if a_serv_lbl.get('name') == self.name
and a_serv_lbl.get('labels') is not None]
if len(service_cmd_opts) == 0:
raise ConfigError ('No labels defined for service \'' +
self.name + '\' in ' +
container_options.get('labels_file'))
service_cmd_lbls = service_cmd_opts[0]['labels']
service_cfg_lbls = container_options.get('labels')
prefix_cmd = ""
prefix_cfg = ""
if 'prefix_labels' in container_options:
prefix_cmd = ".cmd:"
prefix_cfg = ".cfg:"
del container_options['prefix_labels']
temp_labels = dict()
if service_cfg_lbls is not None:
temp_labels.update(
(prefix_cfg + key, service_cfg_lbls[key]) for key in service_cfg_lbls)
temp_labels.update(
(prefix_cmd + key, service_cmd_lbls[key]) for key in service_cmd_lbls)

container_options['labels'] = temp_labels
del container_options['labels_file']


# If a qualified hostname was given, split it into an
# unqualified hostname and a domainname unless domainname
# was also given explicitly. This matches the behavior of
Expand DownExpand Up@@ -553,7 +603,6 @@ def parse_volume_spec(volume_config):

return VolumeSpec(external, internal, mode)


def parse_repository_tag(s):
if ":" not in s:
return s, ""
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 83 additions & 24 deletions compose/cli/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@
from .. import __version__
from ..project import NoSuchService, ConfigurationError
from ..service import BuildError, CannotBeScaledError
from ..config import parse_environment
from ..config import parse_environment, load
from .command import Command
from .docopt_command import NoSuchCommand
from .errors import UserError
Expand DownExpand Up@@ -270,19 +270,29 @@ def run(self, project, options):
Usage: run [options] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]

Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run container in the background, print
new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple times)
-u, --user="" Run as specified username or uid
--no-deps Don't start linked services.
--rm Remove container after run. Ignored in detached mode.
--service-ports Run command with the service's ports enabled and mapped
to the host.
-T Disable pseudo-tty allocation. By default `docker-compose run`
allocates a TTY.
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run container in the background,
print new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple
times)
--labels-file FILE Uses all labels, in the given yaml FILE for the
SERVICE to be run.
--no-deps Don't start linked services.
--no-prefix-labels Don't prefix '.cfg' and '.cmd' when --labels-file
is used.
--remove-labels [REGEX] Removes all labels. If REGEX is given, removes
labels that match the given REGular EXpression.
(Note: This option has less priority than the
given --labels-file)
--rm Remove container after run. Ignored in detached
mode.
--service-ports Run command with the service's ports enabled and
mapped to the host.
-T Disable pseudo-tty allocation. By default
`docker-compose run` allocates a TTY.
-u, --user="" Run as specified username or uid.
"""
service = project.get_service(options['SERVICE'])

Expand DownExpand Up@@ -314,8 +324,42 @@ def run(self, project, options):
'tty': tty,
'stdin_open': not options['-d'],
'detach': options['-d'],
'labels': dict(),
}

if options['--remove-labels']:
service_cfg_lbls = service.options.get('labels')
if service_cfg_lbls is not None:
regex = options.get('--remove-labels')
a = re.compile(regex)
service.options['labels'] = dict(
(k, v) for (k,v) in service_cfg_lbls.iteritems()
if not a.match(k))

if options['--labels-file']:
# Merge labels from config with labels from --labels-file
label_cfg_file = self.get_config_path(options.get('--labels-file'))
label_list_cfg_file = load(label_cfg_file)
service_cmd_opts = [a_serv_lbl for a_serv_lbl in label_list_cfg_file
if a_serv_lbl.get('name') == service.name
and a_serv_lbl.get('labels') is not None]
if len(service_cmd_opts) == 0:
raise UserError('No labels defined for service \'' + service.name +
'\' in ' + options.get('--labels-file'))
service_cmd_lbls = service_cmd_opts[0]['labels']
service_cfg_lbls = service.options.get('labels')
prefix_cmd = ""
prefix_cfg = ""
if not options['--no-prefix-labels']:
prefix_cmd = ".cmd:"
prefix_cfg = ".cfg:"

if service_cfg_lbls is not None:
container_options['labels'].update(
(prefix_cfg + key, service_cfg_lbls[key]) for key in service_cfg_lbls)
container_options['labels'].update(
(prefix_cmd + key, service_cmd_lbls[key]) for key in service_cmd_lbls)

if options['-e']:
# Merge environment from config with -e command line
container_options['environment'] = dict(
Expand DownExpand Up@@ -434,23 +478,35 @@ def up(self, project, options):
Usage: up [options] [SERVICE...]

Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run containers in the background,
print new container names.
--no-color Produce monochrome output.
--no-deps Don't start linked services.
--no-recreate If containers already exist, don't recreate them.
--no-build Don't build an image, even if it's missing
-t, --timeout TIMEOUT When attached, use this timeout in seconds
for the shutdown. (default: 10)
--allow-insecure-ssl Allow insecure connections to the docker registry.
-d Detached mode: Run containers in the background,
print new container names.
--labels-file FILE Uses all labels, in the given yaml FILE for the
SERVICEs to be up.
--no-build Don't build an image, even if it's missing.
--no-deps Don't start linked services.
--no-color Produce monochrome output.
--no-prefix-labels Don't prefix '.cfg' and '.cmd' when --labels-file
is used.
--no-recreate If containers already exist, don't recreate them.
--remove-labels [REGEX] Removes all labels. If REGEX is given, removes
labels that match the given REGular EXpression.
(Note: This option has less priority than the
given --labels-file).
-t, --timeout TIMEOUT When attached, use this timeout in seconds for the
shutdown. (default: 10)

"""
insecure_registry = options['--allow-insecure-ssl']
detached = options['-d']

monochrome = options['--no-color']

if options['--labels-file']:
labels_file = self.get_config_path(options['--labels-file'])
else:
labels_file = ''

start_deps = not options['--no-deps']
recreate = not options['--no-recreate']
service_names = options['SERVICE']
Expand All@@ -462,6 +518,9 @@ def up(self, project, options):
insecure_registry=insecure_registry,
detach=detached,
do_build=not options['--no-build'],
prefix_labels=not options['--no-prefix-labels'],
remove_labels=str(options['--remove-labels']),
labels_file=labels_file
)

to_attach = [c for s in project.get_services(service_names) for c in s.containers()]
Expand Down
3 changes: 1 addition & 2 deletions compose/config.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
'environment',
'hostname',
'image',
'labels',
'links',
'mem_limit',
'net',
Expand DownExpand Up@@ -138,7 +139,6 @@ def resolve_environment(service_dict, working_dir=None):
service_dict['environment'] = env
return service_dict


def parse_environment(environment):
if not environment:
return {}
Expand All@@ -154,7 +154,6 @@ def parse_environment(environment):
environment
)


def split_env(env):
if '=' in env:
return env.split('=', 1)
Expand Down
16 changes: 13 additions & 3 deletions compose/project.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -209,20 +209,30 @@ def up(self,
recreate=True,
insecure_registry=False,
detach=False,
do_build=True):
do_build=True,
prefix_labels=False,
remove_labels='',
labels_file=None
):
running_containers = []
for service in self.get_services(service_names, include_deps=start_deps):
if recreate:
for (_, container) in service.recreate_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
do_build=do_build,
prefix_labels=prefix_labels,
remove_labels=remove_labels,
labels_file=labels_file):
running_containers.append(container)
else:
for container in service.start_or_create_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
do_build=do_build,
prefix_labels=prefix_labels,
remove_labels=remove_labels,
labels_file=labels_file):
running_containers.append(container)

return running_containers
Expand Down
57 changes: 53 additions & 4 deletions compose/service.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@

from docker.errors import APIError

from .config import DOCKER_CONFIG_KEYS
from .config import DOCKER_CONFIG_KEYS, load
from .container import Container, get_container_name
from .progress_stream import stream_output, StreamOutputError

Expand All@@ -29,6 +29,7 @@

VALID_NAME_CHARS = '[a-zA-Z0-9]'

LABEL_PREFIX = ''

class BuildError(Exception):
def __init__(self, service, reason):
Expand All@@ -50,6 +51,9 @@ class ConfigError(ValueError):
ServiceName = namedtuple('ServiceName', 'project service number')


LabelSpec = namedtuple('LabelSpec', 'key value')


class Service(object):
def __init__(self, name, client=None, project='default', links=None, external_links=None, volumes_from=None, net=None, **options):
if not re.match('^%s+$' % VALID_NAME_CHARS, name):
Expand DownExpand Up@@ -181,7 +185,6 @@ def create_container(self,
self.can_be_built() and
not self.client.images(name=self.full_name)):
self.build()

try:
return Container.create(self.client, **container_options)
except APIError as e:
Expand DownExpand Up@@ -296,7 +299,8 @@ def start_or_create_containers(
self,
insecure_registry=False,
detach=False,
do_build=True):
do_build=True,
**override_options):
containers = self.containers(stopped=True)

if not containers:
Expand All@@ -305,6 +309,7 @@ def start_or_create_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build,
**override_options
)
return [self.start_container(new_container)]
else:
Expand DownExpand Up@@ -399,6 +404,51 @@ def _get_container_create_options(self, override_options, one_off=False):
self.containers(stopped=True, one_off=one_off),
one_off)

if 'labels' in container_options:
container_options['labels'] = dict(
(LABEL_PREFIX + key, container_options['labels'][key])
for key in container_options['labels'])

if 'remove_labels' in container_options:
service_cfg_lbls = container_options.get('labels')
if service_cfg_lbls is not None:
regex = container_options.get('remove_labels')
a = re.compile(regex)
container_options['labels'] = dict(
(k, v) for (k,v) in service_cfg_lbls.iteritems()
if not a.match(k))
del container_options['remove_labels']

if 'labels_file' in container_options:
# Merge labels from config with labels from --labels-file
label_cfg_file = container_options.get('labels_file')
label_list_cfg_file = load(label_cfg_file)
service_cmd_opts = [a_serv_lbl for a_serv_lbl in label_list_cfg_file
if a_serv_lbl.get('name') == self.name
and a_serv_lbl.get('labels') is not None]
if len(service_cmd_opts) == 0:
raise ConfigError ('No labels defined for service \'' +
self.name + '\' in ' +
container_options.get('labels_file'))
service_cmd_lbls = service_cmd_opts[0]['labels']
service_cfg_lbls = container_options.get('labels')
prefix_cmd = ""
prefix_cfg = ""
if 'prefix_labels' in container_options:
prefix_cmd = ".cmd:"
prefix_cfg = ".cfg:"
del container_options['prefix_labels']
temp_labels = dict()
if service_cfg_lbls is not None:
temp_labels.update(
(prefix_cfg + key, service_cfg_lbls[key]) for key in service_cfg_lbls)
temp_labels.update(
(prefix_cmd + key, service_cmd_lbls[key]) for key in service_cmd_lbls)

container_options['labels'] = temp_labels
del container_options['labels_file']


# If a qualified hostname was given, split it into an
# unqualified hostname and a domainname unless domainname
# was also given explicitly. This matches the behavior of
Expand DownExpand Up@@ -553,7 +603,6 @@ def parse_volume_spec(volume_config):

return VolumeSpec(external, internal, mode)


def parse_repository_tag(s):
if ":" not in s:
return s, ""
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 83 additions & 24 deletions compose/cli/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@
from .. import __version__
from ..project import NoSuchService, ConfigurationError
from ..service import BuildError, CannotBeScaledError
from ..config import parse_environment
from ..config import parse_environment, load
from .command import Command
from .docopt_command import NoSuchCommand
from .errors import UserError
Expand DownExpand Up@@ -270,19 +270,29 @@ def run(self, project, options):
Usage: run [options] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]

Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run container in the background, print
new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple times)
-u, --user="" Run as specified username or uid
--no-deps Don't start linked services.
--rm Remove container after run. Ignored in detached mode.
--service-ports Run command with the service's ports enabled and mapped
to the host.
-T Disable pseudo-tty allocation. By default `docker-compose run`
allocates a TTY.
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run container in the background,
print new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple
times)
--labels-file FILE Uses all labels, in the given yaml FILE for the
SERVICE to be run.
--no-deps Don't start linked services.
--no-prefix-labels Don't prefix '.cfg' and '.cmd' when --labels-file
is used.
--remove-labels [REGEX] Removes all labels. If REGEX is given, removes
labels that match the given REGular EXpression.
(Note: This option has less priority than the
given --labels-file)
--rm Remove container after run. Ignored in detached
mode.
--service-ports Run command with the service's ports enabled and
mapped to the host.
-T Disable pseudo-tty allocation. By default
`docker-compose run` allocates a TTY.
-u, --user="" Run as specified username or uid.
"""
service = project.get_service(options['SERVICE'])

Expand DownExpand Up@@ -314,8 +324,42 @@ def run(self, project, options):
'tty': tty,
'stdin_open': not options['-d'],
'detach': options['-d'],
'labels': dict(),
}

if options['--remove-labels']:
service_cfg_lbls = service.options.get('labels')
if service_cfg_lbls is not None:
regex = options.get('--remove-labels')
a = re.compile(regex)
service.options['labels'] = dict(
(k, v) for (k,v) in service_cfg_lbls.iteritems()
if not a.match(k))

if options['--labels-file']:
# Merge labels from config with labels from --labels-file
label_cfg_file = self.get_config_path(options.get('--labels-file'))
label_list_cfg_file = load(label_cfg_file)
service_cmd_opts = [a_serv_lbl for a_serv_lbl in label_list_cfg_file
if a_serv_lbl.get('name') == service.name
and a_serv_lbl.get('labels') is not None]
if len(service_cmd_opts) == 0:
raise UserError('No labels defined for service \'' + service.name +
'\' in ' + options.get('--labels-file'))
service_cmd_lbls = service_cmd_opts[0]['labels']
service_cfg_lbls = service.options.get('labels')
prefix_cmd = ""
prefix_cfg = ""
if not options['--no-prefix-labels']:
prefix_cmd = ".cmd:"
prefix_cfg = ".cfg:"

if service_cfg_lbls is not None:
container_options['labels'].update(
(prefix_cfg + key, service_cfg_lbls[key]) for key in service_cfg_lbls)
container_options['labels'].update(
(prefix_cmd + key, service_cmd_lbls[key]) for key in service_cmd_lbls)

if options['-e']:
# Merge environment from config with -e command line
container_options['environment'] = dict(
Expand DownExpand Up@@ -434,23 +478,35 @@ def up(self, project, options):
Usage: up [options] [SERVICE...]

Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
-d Detached mode: Run containers in the background,
print new container names.
--no-color Produce monochrome output.
--no-deps Don't start linked services.
--no-recreate If containers already exist, don't recreate them.
--no-build Don't build an image, even if it's missing
-t, --timeout TIMEOUT When attached, use this timeout in seconds
for the shutdown. (default: 10)
--allow-insecure-ssl Allow insecure connections to the docker registry.
-d Detached mode: Run containers in the background,
print new container names.
--labels-file FILE Uses all labels, in the given yaml FILE for the
SERVICEs to be up.
--no-build Don't build an image, even if it's missing.
--no-deps Don't start linked services.
--no-color Produce monochrome output.
--no-prefix-labels Don't prefix '.cfg' and '.cmd' when --labels-file
is used.
--no-recreate If containers already exist, don't recreate them.
--remove-labels [REGEX] Removes all labels. If REGEX is given, removes
labels that match the given REGular EXpression.
(Note: This option has less priority than the
given --labels-file).
-t, --timeout TIMEOUT When attached, use this timeout in seconds for the
shutdown. (default: 10)

"""
insecure_registry = options['--allow-insecure-ssl']
detached = options['-d']

monochrome = options['--no-color']

if options['--labels-file']:
labels_file = self.get_config_path(options['--labels-file'])
else:
labels_file = ''

start_deps = not options['--no-deps']
recreate = not options['--no-recreate']
service_names = options['SERVICE']
Expand All@@ -462,6 +518,9 @@ def up(self, project, options):
insecure_registry=insecure_registry,
detach=detached,
do_build=not options['--no-build'],
prefix_labels=not options['--no-prefix-labels'],
remove_labels=str(options['--remove-labels']),
labels_file=labels_file
)

to_attach = [c for s in project.get_services(service_names) for c in s.containers()]
Expand Down
3 changes: 1 addition & 2 deletions compose/config.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
'environment',
'hostname',
'image',
'labels',
'links',
'mem_limit',
'net',
Expand DownExpand Up@@ -138,7 +139,6 @@ def resolve_environment(service_dict, working_dir=None):
service_dict['environment'] = env
return service_dict


def parse_environment(environment):
if not environment:
return {}
Expand All@@ -154,7 +154,6 @@ def parse_environment(environment):
environment
)


def split_env(env):
if '=' in env:
return env.split('=', 1)
Expand Down
16 changes: 13 additions & 3 deletions compose/project.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -209,20 +209,30 @@ def up(self,
recreate=True,
insecure_registry=False,
detach=False,
do_build=True):
do_build=True,
prefix_labels=False,
remove_labels='',
labels_file=None
):
running_containers = []
for service in self.get_services(service_names, include_deps=start_deps):
if recreate:
for (_, container) in service.recreate_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
do_build=do_build,
prefix_labels=prefix_labels,
remove_labels=remove_labels,
labels_file=labels_file):
running_containers.append(container)
else:
for container in service.start_or_create_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
do_build=do_build,
prefix_labels=prefix_labels,
remove_labels=remove_labels,
labels_file=labels_file):
running_containers.append(container)

return running_containers
Expand Down
57 changes: 53 additions & 4 deletions compose/service.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,7 @@

from docker.errors import APIError

from .config import DOCKER_CONFIG_KEYS
from .config import DOCKER_CONFIG_KEYS, load
from .container import Container, get_container_name
from .progress_stream import stream_output, StreamOutputError

Expand All@@ -29,6 +29,7 @@

VALID_NAME_CHARS = '[a-zA-Z0-9]'

LABEL_PREFIX = ''

class BuildError(Exception):
def __init__(self, service, reason):
Expand All@@ -50,6 +51,9 @@ class ConfigError(ValueError):
ServiceName = namedtuple('ServiceName', 'project service number')


LabelSpec = namedtuple('LabelSpec', 'key value')


class Service(object):
def __init__(self, name, client=None, project='default', links=None, external_links=None, volumes_from=None, net=None, **options):
if not re.match('^%s+$' % VALID_NAME_CHARS, name):
Expand DownExpand Up@@ -181,7 +185,6 @@ def create_container(self,
self.can_be_built() and
not self.client.images(name=self.full_name)):
self.build()

try:
return Container.create(self.client, **container_options)
except APIError as e:
Expand DownExpand Up@@ -296,7 +299,8 @@ def start_or_create_containers(
self,
insecure_registry=False,
detach=False,
do_build=True):
do_build=True,
**override_options):
containers = self.containers(stopped=True)

if not containers:
Expand All@@ -305,6 +309,7 @@ def start_or_create_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build,
**override_options
)
return [self.start_container(new_container)]
else:
Expand DownExpand Up@@ -399,6 +404,51 @@ def _get_container_create_options(self, override_options, one_off=False):
self.containers(stopped=True, one_off=one_off),
one_off)

if 'labels' in container_options:
container_options['labels'] = dict(
(LABEL_PREFIX + key, container_options['labels'][key])
for key in container_options['labels'])

if 'remove_labels' in container_options:
service_cfg_lbls = container_options.get('labels')
if service_cfg_lbls is not None:
regex = container_options.get('remove_labels')
a = re.compile(regex)
container_options['labels'] = dict(
(k, v) for (k,v) in service_cfg_lbls.iteritems()
if not a.match(k))
del container_options['remove_labels']

if 'labels_file' in container_options:
# Merge labels from config with labels from --labels-file
label_cfg_file = container_options.get('labels_file')
label_list_cfg_file = load(label_cfg_file)
service_cmd_opts = [a_serv_lbl for a_serv_lbl in label_list_cfg_file
if a_serv_lbl.get('name') == self.name
and a_serv_lbl.get('labels') is not None]
if len(service_cmd_opts) == 0:
raise ConfigError ('No labels defined for service \'' +
self.name + '\' in ' +
container_options.get('labels_file'))
service_cmd_lbls = service_cmd_opts[0]['labels']
service_cfg_lbls = container_options.get('labels')
prefix_cmd = ""
prefix_cfg = ""
if 'prefix_labels' in container_options:
prefix_cmd = ".cmd:"
prefix_cfg = ".cfg:"
del container_options['prefix_labels']
temp_labels = dict()
if service_cfg_lbls is not None:
temp_labels.update(
(prefix_cfg + key, service_cfg_lbls[key]) for key in service_cfg_lbls)
temp_labels.update(
(prefix_cmd + key, service_cmd_lbls[key]) for key in service_cmd_lbls)

container_options['labels'] = temp_labels
del container_options['labels_file']


# If a qualified hostname was given, split it into an
# unqualified hostname and a domainname unless domainname
# was also given explicitly. This matches the behavior of
Expand DownExpand Up@@ -553,7 +603,6 @@ def parse_volume_spec(volume_config):

return VolumeSpec(external, internal, mode)


def parse_repository_tag(s):
if ":" not in s:
return s, ""
Expand Down