mirror of
https://github.com/valitydev/salt.git
synced 2024-11-08 17:33:54 +00:00
Merge pull request #45231 from obeleh/compose_services
Compose services
This commit is contained in:
commit
39442c617c
@ -95,6 +95,11 @@ Functions
|
||||
- :py:func:`dockercompose.build <salt.modules.dockercompose.build>`
|
||||
- Gather information about containers:
|
||||
- :py:func:`dockercompose.ps <salt.modules.dockercompose.ps>`
|
||||
- Manage service definitions:
|
||||
- :py:func:`dockercompose.service_create <salt.modules.dockercompose.ps>`
|
||||
- :py:func:`dockercompose.service_upsert <salt.modules.dockercompose.ps>`
|
||||
- :py:func:`dockercompose.service_remove <salt.modules.dockercompose.ps>`
|
||||
- :py:func:`dockercompose.service_set_tag <salt.modules.dockercompose.ps>`
|
||||
|
||||
Detailed Function Documentation
|
||||
-------------------------------
|
||||
@ -113,6 +118,10 @@ import salt.utils.stringutils
|
||||
from salt.ext import six
|
||||
|
||||
from operator import attrgetter
|
||||
|
||||
from salt.serializers import json
|
||||
from salt.utils import yaml
|
||||
|
||||
try:
|
||||
import compose
|
||||
from compose.cli.command import get_project
|
||||
@ -214,9 +223,66 @@ def __read_docker_compose_file(file_path):
|
||||
result, None)
|
||||
|
||||
|
||||
def __write_docker_compose(path, docker_compose):
|
||||
def __load_docker_compose(path):
|
||||
'''
|
||||
Write docker-compose to a temp directory
|
||||
Read the compose file and load its' contents
|
||||
|
||||
:param path:
|
||||
:return:
|
||||
'''
|
||||
file_path = __get_docker_file_path(path)
|
||||
if file_path is None:
|
||||
msg = 'Could not find docker-compose file at {0}'.format(path)
|
||||
return None, __standardize_result(False, msg,
|
||||
None, None)
|
||||
if not os.path.isfile(file_path):
|
||||
return None, __standardize_result(False,
|
||||
'Path {} is not present'.format(file_path),
|
||||
None, None)
|
||||
try:
|
||||
with salt.utils.files.fopen(file_path, 'r') as fl:
|
||||
loaded = yaml.load(fl)
|
||||
except EnvironmentError:
|
||||
return None, __standardize_result(False,
|
||||
'Could not read {0}'.format(file_path),
|
||||
None, None)
|
||||
except yaml.YAMLError as yerr:
|
||||
msg = 'Could not parse {0} {1}'.format(file_path, yerr)
|
||||
return None, __standardize_result(False, msg,
|
||||
None, None)
|
||||
if not loaded:
|
||||
msg = 'Got empty compose file at {0}'.format(file_path)
|
||||
return None, __standardize_result(False, msg,
|
||||
None, None)
|
||||
if 'services' not in loaded:
|
||||
loaded['services'] = {}
|
||||
result = {
|
||||
'compose_content': loaded,
|
||||
'file_name': os.path.basename(file_path)
|
||||
}
|
||||
return result, None
|
||||
|
||||
|
||||
def __dump_docker_compose(path, content, already_existed):
|
||||
'''
|
||||
Dumps
|
||||
|
||||
:param path:
|
||||
:param content: the not-yet dumped content
|
||||
:return:
|
||||
'''
|
||||
try:
|
||||
dumped = yaml.safe_dump(content, indent=2, default_flow_style=False)
|
||||
return __write_docker_compose(path, dumped, already_existed)
|
||||
except TypeError as t_err:
|
||||
msg = 'Could not dump {0} {1}'.format(content, t_err)
|
||||
return __standardize_result(False, msg,
|
||||
None, None)
|
||||
|
||||
|
||||
def __write_docker_compose(path, docker_compose, already_existed):
|
||||
'''
|
||||
Write docker-compose to a path
|
||||
in order to use it with docker-compose ( config check )
|
||||
|
||||
:param path:
|
||||
@ -243,7 +309,8 @@ def __write_docker_compose(path, docker_compose):
|
||||
None, None)
|
||||
project = __load_project_from_file_path(file_path)
|
||||
if isinstance(project, dict):
|
||||
os.remove(file_path)
|
||||
if not already_existed:
|
||||
os.remove(file_path)
|
||||
return project
|
||||
return file_path
|
||||
|
||||
@ -279,6 +346,57 @@ def __load_project_from_file_path(file_path):
|
||||
return project
|
||||
|
||||
|
||||
def __load_compose_definitions(path, definition):
|
||||
'''
|
||||
Will load the compose file located at path
|
||||
Then determines the format/contents of the sent definition
|
||||
|
||||
err or results are only set if there were any
|
||||
|
||||
:param path:
|
||||
:param definition:
|
||||
:return tuple(compose_result, loaded_definition, err):
|
||||
'''
|
||||
compose_result, err = __load_docker_compose(path)
|
||||
if err:
|
||||
return None, None, err
|
||||
if isinstance(definition, dict):
|
||||
return compose_result, definition, None
|
||||
elif definition.strip().startswith('{'):
|
||||
try:
|
||||
loaded_definition = json.deserialize(definition)
|
||||
except json.DeserializationError as jerr:
|
||||
msg = 'Could not parse {0} {1}'.format(definition, jerr)
|
||||
return None, None, __standardize_result(False, msg,
|
||||
None, None)
|
||||
else:
|
||||
try:
|
||||
loaded_definition = yaml.load(definition)
|
||||
except yaml.YAMLError as yerr:
|
||||
msg = 'Could not parse {0} {1}'.format(definition, yerr)
|
||||
return None, None, __standardize_result(False, msg,
|
||||
None, None)
|
||||
return compose_result, loaded_definition, None
|
||||
|
||||
|
||||
def __dump_compose_file(path, compose_result, success_msg, already_existed):
|
||||
'''
|
||||
Utility function to dump the compose result to a file.
|
||||
|
||||
:param path:
|
||||
:param compose_result:
|
||||
:param success_msg: the message to give upon success
|
||||
:return:
|
||||
'''
|
||||
ret = __dump_docker_compose(path,
|
||||
compose_result['compose_content'],
|
||||
already_existed)
|
||||
if isinstance(ret, dict):
|
||||
return ret
|
||||
return __standardize_result(True, success_msg,
|
||||
compose_result['compose_content'], None)
|
||||
|
||||
|
||||
def __handle_except(inst):
|
||||
'''
|
||||
Handle exception and return a standard result
|
||||
@ -362,7 +480,9 @@ def create(path, docker_compose):
|
||||
salt myminion dockercompose.create /path/where/docker-compose/stored content
|
||||
'''
|
||||
if docker_compose:
|
||||
ret = __write_docker_compose(path, docker_compose)
|
||||
ret = __write_docker_compose(path,
|
||||
docker_compose,
|
||||
already_existed=False)
|
||||
if isinstance(ret, dict):
|
||||
return ret
|
||||
else:
|
||||
@ -769,3 +889,137 @@ def up(path, service_names=None):
|
||||
except Exception as inst:
|
||||
return __handle_except(inst)
|
||||
return __standardize_result(True, 'Adding containers via docker-compose', result, debug_ret)
|
||||
|
||||
|
||||
def service_create(path, service_name, definition):
|
||||
'''
|
||||
Create the definition of a docker-compose service
|
||||
This fails when the service already exists
|
||||
This does not pull or up the service
|
||||
This wil re-write your yaml file. Comments will be lost. Indentation is set to 2 spaces
|
||||
|
||||
path
|
||||
Path where the docker-compose file is stored on the server
|
||||
service_name
|
||||
Name of the service to create
|
||||
definition
|
||||
Service definition as yaml or json string
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt myminion dockercompose.service_create /path/where/docker-compose/stored service_name definition
|
||||
'''
|
||||
compose_result, loaded_definition, err = __load_compose_definitions(path, definition)
|
||||
if err:
|
||||
return err
|
||||
services = compose_result['compose_content']['services']
|
||||
if service_name in services:
|
||||
msg = 'Service {0} already exists'.format(service_name)
|
||||
return __standardize_result(False, msg, None, None)
|
||||
services[service_name] = loaded_definition
|
||||
return __dump_compose_file(path, compose_result,
|
||||
'Service {0} created'.format(service_name),
|
||||
already_existed=True)
|
||||
|
||||
|
||||
def service_upsert(path, service_name, definition):
|
||||
'''
|
||||
Create or update the definition of a docker-compose service
|
||||
This does not pull or up the service
|
||||
This wil re-write your yaml file. Comments will be lost. Indentation is set to 2 spaces
|
||||
|
||||
path
|
||||
Path where the docker-compose file is stored on the server
|
||||
service_name
|
||||
Name of the service to create
|
||||
definition
|
||||
Service definition as yaml or json string
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt myminion dockercompose.service_upsert /path/where/docker-compose/stored service_name definition
|
||||
'''
|
||||
compose_result, loaded_definition, err = __load_compose_definitions(path, definition)
|
||||
if err:
|
||||
return err
|
||||
services = compose_result['compose_content']['services']
|
||||
if service_name in services:
|
||||
msg = 'Service {0} already exists'.format(service_name)
|
||||
return __standardize_result(False, msg, None, None)
|
||||
services[service_name] = loaded_definition
|
||||
return __dump_compose_file(path, compose_result,
|
||||
'Service definition for {0} is set'.format(service_name),
|
||||
already_existed=True)
|
||||
|
||||
|
||||
def service_remove(path, service_name):
|
||||
'''
|
||||
Remove the definition of a docker-compose service
|
||||
This does not rm the container
|
||||
This wil re-write your yaml file. Comments will be lost. Indentation is set to 2 spaces
|
||||
|
||||
path
|
||||
Path where the docker-compose file is stored on the server
|
||||
service_name
|
||||
Name of the service to remove
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt myminion dockercompose.service_remove /path/where/docker-compose/stored service_name
|
||||
'''
|
||||
compose_result, err = __load_docker_compose(path)
|
||||
if err:
|
||||
return err
|
||||
services = compose_result['compose_content']['services']
|
||||
if service_name not in services:
|
||||
return __standardize_result(False,
|
||||
'Service {0} did not exists'.format(service_name),
|
||||
None, None)
|
||||
del services[service_name]
|
||||
return __dump_compose_file(path, compose_result,
|
||||
'Service {0} is removed from {1}'.format(service_name, path),
|
||||
already_existed=True)
|
||||
|
||||
|
||||
def service_set_tag(path, service_name, tag):
|
||||
'''
|
||||
Change the tag of a docker-compose service
|
||||
This does not pull or up the service
|
||||
This wil re-write your yaml file. Comments will be lost. Indentation is set to 2 spaces
|
||||
|
||||
path
|
||||
Path where the docker-compose file is stored on the server
|
||||
service_name
|
||||
Name of the service to remove
|
||||
tag
|
||||
Name of the tag (often used as version) that the service image should have
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt myminion dockercompose.service_create /path/where/docker-compose/stored service_name tag
|
||||
'''
|
||||
compose_result, err = __load_docker_compose(path)
|
||||
if err:
|
||||
return err
|
||||
services = compose_result['compose_content']['services']
|
||||
if service_name not in services:
|
||||
return __standardize_result(False,
|
||||
'Service {0} did not exists'.format(service_name),
|
||||
None, None)
|
||||
if 'image' not in services[service_name]:
|
||||
return __standardize_result(False,
|
||||
'Service {0} did not contain the variable "image"'.format(service_name),
|
||||
None, None)
|
||||
image = services[service_name]['image'].split(':')[0]
|
||||
services[service_name]['image'] = '{0}:{1}'.format(image, tag)
|
||||
return __dump_compose_file(path, compose_result,
|
||||
'Service {0} is set to tag "{1}"'.format(service_name, tag),
|
||||
already_existed=True)
|
||||
|
Loading…
Reference in New Issue
Block a user