Repository URL to install this package:
|
Version:
0.3.30 ▾
|
from ... import utils
class BaseCommand(object):
'''
Base class for boss subcommands
'''
help = None
arguments = ()
def run(self):
raise NotImplementedError
@classmethod
def get_arg_spec(cls, thing):
if isinstance(thing, tuple):
args, kwargs = thing
else:
args, kwargs = (), thing
return args, kwargs
@classmethod
def configure_parser(cls, app, parser_factory):
names = getattr(cls, 'names', [])
if not names:
names = [getattr(cls, 'name', cls.__name__.lower())]
parsers = [
parser_factory(name, help=cls.help)
for name in utils.remove_dupes(names)
]
parser_func = lambda **kw: cls(app, **kw).run()
for parser in parsers:
for args, kwargs in map(cls.get_arg_spec, cls.arguments):
action = parser.add_argument(*args, **kwargs)
kwargs.setdefault('dest', action.dest)
parser.set_defaults(func=parser_func)
def __init__(self, app, **kwargs):
object.__init__(self)
self.app = app
self.kwargs = kwargs
self.kwargs.pop('func')
self.validate()
self.__dict__.update(self.kwargs)
def validate(self):
'''
Determine if arguments provided are valid; else throw an Exception.
relies on validate_{FIELD} methods to do the actual validation.
'''
failed = []
for key in self.kwargs.keys():
validator = getattr(self, 'validate_{}'.format(key), None)
if validator is None:
continue
try:
setattr(self, '_orig_' + key, self.kwargs.get(key))
self.kwargs[key] = validator(self.kwargs.get(key))
except ValueError as exc:
failed.append((key, exc.message))
if failed:
raise Exception("Some invalid values were found:\n{}".format(
'\n'.join('{}: {}'.format(key, msg) for key, msg in failed)
))
def validate_hosts(self, hosts):
'''
A "host" can be provided as a named host entry from the config,
a substring that matches the hostname, or the name of a group of
hosts. This function resolves substrings and groups into named host
entries and makes sure they are all present in the config (also includes
dynamic targets e.g. aws_prod).
Raises a ValueError if an unknown host is found.
Returns a list of hostnames and also assigns a 'host_objs'
attribute which contains boss.Host objects (TODO: only use
boss.Host objects).
'''
hosts_or_groups = hosts[:]
hosts = []
missing_hosts = []
all_real_hosts = sum(self.app.config.hosts.values(), [])
while hosts_or_groups:
host_or_group = hosts_or_groups.pop(0)
group = self.app.config.default_get('host_groups', host_or_group, None)
if group is None:
host = utils.get_host(host_or_group, all_real_hosts)
if host is None:
missing_hosts.append(host_or_group)
else:
hosts.append(host)
else:
hosts.extend(
self.app.config.get_hosts_for_group(host_or_group)
)
if missing_hosts:
raise ValueError("Hosts or groups not found: {}".format(
', '.join(sorted(missing_hosts)))
)
# TODO: all commands should switch to just using host objects
self.host_objs = hosts
return [x.name for x in hosts]
def validate_image(self, image):
'''
An 'image' is a name of a docker image, specified as [namespace/]name:tag.
Note that this is different from an image-ish accepted by docker proper in
the following ways:
- Hashes are not accepted
- Registry (host) is not accepted
- Tags are required
Note that there is currently a failure in registry parsing as the following images are
equivalent:
- docker.io/debian:latest
- docker.io/library/debian:latest
'docker.io/debian:latest' will be validated as a proper image for hub-boss as it is
indistingushable from a namespace of 'docker.io' (with canonical name:
'docker.io/docker.io/debian:latest'). All images deployed with hub-boss at this time
are from the official Docker registry, so this shouldn't be a problem.
If a hash or registry is detected, or a tag is not provided, a value error is raised.
This returns the validated image name as well as sets the "image_repo" and "image_tag"
attributes.
'''
parts = image.split(':')
has_hash = '@' in image
name_parts = parts[0].split('/')
if has_hash or len(parts) != 2 or len(name_parts) > 2:
raise ValueError("Invalid image name: {}. Must match [namespace/]name:tag".format(image))
self.image_repo = parts[0]
self.image_tag = parts[1]
return image
def __repr__(self):
arg_names = [self.get_arg_spec(arg)[1]['dest'] for arg in self.arguments]
return '{cls}({props})'.format(
cls=type(self).__name__,
props=', '.join('{k}={v!r}'.format(k=k, v=getattr(self, k, None)) for k in arg_names)
)
def _spread(self, function, *args, **kwargs):
operation = utils.ParallelHostOperation(self.host_objs)
operation.start(function, args, kwargs)
return operation.finish()