Merge "Fix flapping DockerServiceWarning alert"
diff --git a/.kitchen.travis.yml b/.kitchen.travis.yml
deleted file mode 100644
index f847543..0000000
--- a/.kitchen.travis.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-suites:
-
- - name: <%= ENV['SUITE'] %>
- provisioner:
- pillars-from-files:
- neutron.sls: tests/pillar/<%= ENV['SUITE'] %>.sls
diff --git a/.travis.yml b/.travis.yml
index fa7c5f9..64743ee 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -17,15 +17,16 @@
- bundle install
env:
- - PLATFORM=trevorj/salty-whales:xenial SUITE=client_compose
- - PLATFORM=trevorj/salty-whales:xenial SUITE=client_container
- - PLATFORM=trevorj/salty-whales:xenial SUITE=host_single
+ - PLATFORM=trevorj/salty-whales:xenial SUITE=client-compose
+ - PLATFORM=trevorj/salty-whales:xenial SUITE=client-container
+ - PLATFORM=trevorj/salty-whales:xenial SUITE=host-single
before_script:
- make test | tail
script:
- - KITCHEN_LOCAL_YAML=.kitchen.travis.yml bundle exec kitchen test -t tests/integration
+ - test ! -e .kitchen.yml || bundle exec kitchen converge ${SUITE} || true
+ - test ! -e .kitchen.yml || bundle exec kitchen verify ${SUITE} -t tests/integration
notifications:
webhooks:
diff --git a/README.rst b/README.rst
index 81cb038..4cbdb11 100644
--- a/README.rst
+++ b/README.rst
@@ -279,6 +279,65 @@
password: password2
+Docker container service management
+-----------------------------------
+
+Enforce the service in container is started
+
+.. code-block:: yaml
+
+ contrail_control_started:
+ dockerng_service.start:
+ - container: f020d0d3efa8
+ - service: contrail-control
+
+or
+
+.. code-block:: yaml
+
+ contrail_control_started:
+ dockerng_service.start:
+ - container: contrail_controller
+ - service: contrail-control
+
+
+Enforce the service in container is stoped
+
+.. code-block:: yaml
+
+ contrail_control_stoped:
+ dockerng_service.stop:
+ - container: f020d0d3efa8
+ - service: contrail-control
+
+Enforce the service in container will be restarted
+
+.. code-block:: yaml
+
+ contrail_control_restart:
+ dockerng_service.restart:
+ - container: f020d0d3efa8
+ - service: contrail-control
+
+Enforce the service in container is enabled
+
+.. code-block:: yaml
+
+ contrail_control_enable:
+ dockerng_service.enable:
+ - container: f020d0d3efa8
+ - service: contrail-control
+
+Enforce the service in container is disabled
+
+.. code-block:: yaml
+
+ contrail_control_disable:
+ dockerng_service.disable:
+ - container: f020d0d3efa8
+ - service: contrail-control
+
+
More Information
================
diff --git a/_modules/dockerng_service.py b/_modules/dockerng_service.py
new file mode 100644
index 0000000..081b7e0
--- /dev/null
+++ b/_modules/dockerng_service.py
@@ -0,0 +1,99 @@
+#!/usr/bin/python
+# Copyright 2017 Mirantis, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+try:
+ import docker
+ HAS_DOCKER = True
+except ImportError:
+ HAS_DOCKER = False
+
+__opts__ = {}
+__virtualname__ = 'dockerng_service'
+
+
+def __virtual__():
+ '''
+ Only load this module if docker library is installed.
+ '''
+ if HAS_DOCKER:
+ return __virtualname__
+ return (False, 'dockerio execution module not loaded: docker python library not available.')
+
+
+def status(container, service):
+ cmd = "systemctl show " + service + " -p ActiveState,SubState,UnitFileState"
+ data = __salt__['dockerng.run'](container, cmd)
+ data = data.splitlines()
+ result = dict(s.split('=') for s in data)
+ return result
+
+
+def status_retcode(container, service):
+ cmd = "systemctl show " + service + " -p ActiveState,SubState,UnitFileState"
+ data = __salt__['dockerng.run'](container, cmd)
+ data = data.splitlines()
+ result = dict(s.split('=') for s in data)
+ if result['ActiveState'] == "active" and result['SubState'] == "running":
+ return True
+ return False
+
+
+def restart(container, service):
+ cmd = "systemctl restart " + service
+ data = __salt__['dockerng.run'](container, cmd)
+ if len(data) > 0:
+ return False
+ return True
+
+
+def stop(container, service):
+ cmd = "systemctl stop " + service
+ data = __salt__['dockerng.run'](container, cmd)
+ if len(data) > 0:
+ return False
+ return True
+
+
+def start(container, service):
+ cmd = "systemctl start " + service
+ data = __salt__['dockerng.run'](container, cmd)
+ if len(data) > 0:
+ return False
+ return True
+
+
+def enable(container, service):
+ cmd = "systemctl enable " + service
+ data = __salt__['dockerng.run'](container, cmd)
+ if len(data) > 0:
+ return False
+ return True
+
+
+def reload(container, service):
+ cmd = "systemctl reload " + service
+ data = __salt__['dockerng.run'](container, cmd)
+ if len(data) > 0:
+ return False
+ return True
+
+
+def disable(container, service):
+ cmd = "systemctl disable " + service
+ data = __salt__['dockerng.run'](container, cmd)
+ if len(data) > 0:
+ return False
+ return True
diff --git a/_states/dockerng_service.py b/_states/dockerng_service.py
new file mode 100644
index 0000000..ed13798
--- /dev/null
+++ b/_states/dockerng_service.py
@@ -0,0 +1,297 @@
+#!/usr/bin/python
+# Copyright 2017 Mirantis, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+'''
+Management of Contrail resources
+================================
+
+:depends: - vnc_api Python module
+
+
+Enforce the service in container is running
+-------------------------------------------
+
+.. code-block:: yaml
+
+ contrail_control_running:
+ dockerng_service.running:
+ - container: f020d0d3efa8
+ - service: contrail-control
+
+or
+
+.. code-block:: yaml
+
+ contrail_control_running:
+ dockerng_service.running:
+ - container: contrail_controller
+ - service: contrail-control
+
+
+Enforce the service in container is dead
+------------------------------------------
+
+.. code-block:: yaml
+
+ contrail_control_dead:
+ dockerng_service.dead:
+ - container: f020d0d3efa8
+ - service: contrail-control
+
+Enforce the service in container will be restarted
+--------------------------------------------------
+
+.. code-block:: yaml
+
+ contrail_control_restarted:
+ dockerng_service.restarted:
+ - container: f020d0d3efa8
+ - service: contrail-control
+
+Enforce the service in container is enabled
+-------------------------------------------
+
+.. code-block:: yaml
+
+ contrail_control_enabled:
+ dockerng_service.enabled:
+ - container: f020d0d3efa8
+ - service: contrail-control
+
+Enforce the service in container is disabled
+--------------------------------------------
+
+.. code-block:: yaml
+
+ contrail_control_disabled:
+ dockerng_service.disabled:
+ - container: f020d0d3efa8
+ - service: contrail-control
+
+'''
+
+
+def __virtual__():
+ '''
+ Load Contrail module
+ '''
+ return 'dockerng_service'
+
+
+def running(container, service=None, services=None, **kwargs):
+ '''
+ Ensures that the service in the container is running
+
+ :param container: ID or name of the container
+ :param services: List of services
+ :param service: Service name
+ '''
+ ret = {'name': kwargs.get('name', 'dockerng_service.running'),
+ 'changes': {},
+ 'result': True,
+ 'comment': {}
+ }
+
+ if service and not services:
+ services = [service, ]
+
+ for service in services:
+ status = __salt__['dockerng_service.status'](container, service)
+
+ if status['ActiveState'] != "active" and status['SubState'] != "running":
+ if __opts__['test']:
+ ret['result'] = None
+ ret['comment'][service] = " will be started"
+ else:
+ __salt__['dockerng_service.start'](container, service)
+ ret['comment'] = service + " in " + container + " has been started"
+ ret['changes'] = {service: "started"}
+
+ return ret
+
+
+def dead(container, service, **kwargs):
+ '''
+ Ensures that the service in the container is dead
+
+ :param container: ID or name of the container
+ :param service: Service name
+ '''
+ ret = {'name': service + " in " + container,
+ 'changes': {},
+ 'result': True,
+ 'comment': ''}
+
+ status = __salt__['dockerng_service.status'](container, service)
+
+ if status['ActiveState'] != "inactive" and status['SubState'] != "dead":
+ if __opts__['test']:
+ ret['result'] = None
+ ret['comment'] = service + " in " + container + " will be stoped"
+ return ret
+
+ __salt__['dockerng_service.stop'](container, service)
+ ret['comment'] = service + " in " + container + " has been stoped"
+ ret['changes'] = {"new": "stoped", "old": "started"}
+ return ret
+
+ return ret
+
+
+def restarted(container, service, **kwargs):
+ '''
+ Service in the container will be restarted
+
+ :param container: ID or name of the container
+ :param service: Service name
+ '''
+ ret = {'name': service + " in " + container,
+ 'changes': {},
+ 'result': True,
+ 'comment': ''}
+
+ if __opts__['test']:
+ ret['result'] = None
+ ret['comment'] = service + " in " + container + " will be restarted"
+ return ret
+
+ res = __salt__['dockerng_service.restart'](container, service)
+ ret['comment'] = service + " in " + container + " has been restarted"
+ ret['changes'] = {"status": "restarted"}
+ return ret
+
+
+def enabled(container, service, **kwargs):
+ '''
+ Ensures that the service in the container is enabled
+
+ :param container: ID or name of the container
+ :param service: Service name
+ '''
+ ret = {'name': service + " in " + container,
+ 'changes': {},
+ 'result': True,
+ 'comment': ''}
+
+ status = __salt__['dockerng_service.status'](container, service)
+
+ if status['UnitFileState'] != "enabled":
+ if __opts__['test']:
+ ret['result'] = None
+ ret['comment'] = service + " in " + container + " will be enabled"
+ return ret
+
+ __salt__['dockerng_service.enable'](container, service)
+ ret['comment'] = service + " in " + container + " has been enabled"
+ ret['changes'] = {"new": "enabled", "old": "disabled"}
+ return ret
+
+ return ret
+
+
+def disabled(container, service, **kwargs):
+ '''
+ Ensures that the service in the container is disabled
+
+ :param container: ID or name of the container
+ :param service: Service name
+ '''
+ ret = {'name': service + " in " + container,
+ 'changes': {},
+ 'result': True,
+ 'comment': ''}
+
+ status = __salt__['dockerng_service.status'](container, service)
+
+ if status['UnitFileState'] != "disabled":
+ if __opts__['test']:
+ ret['result'] = None
+ ret['comment'] = service + " in " + container + " will be disabled"
+ return ret
+
+ __salt__['dockerng_service.disable'](container, service)
+ ret['comment'] = service + " in " + container + " has been disabled"
+ ret['changes'] = {"old": "enabled", "new": "disabled"}
+ return ret
+
+ return ret
+
+
+def mod_watch(name,
+ contrainer=None,
+ sfun=None,
+ sig=None,
+ reload=False,
+ full_restart=False,
+ init_delay=None,
+ force=False,
+ **kwargs):
+ '''
+ The service watcher, called to invoke the watch command.
+
+ :param name: The name of the init or rc script used to manage the
+ service
+ :param sfun: The original function which triggered the mod_watch
+ call (`service.running`, for example).
+ :param sig: The string to search for when looking for the service
+ process with ps
+ :param reload: Use reload instead of the default restart (exclusive
+ option with full_restart, defaults to reload if both
+ are used)
+ :param full_restart: Use service.full_restart instead of restart
+ (exclusive option with reload)
+ :param force: Use service.force_reload instead of reload
+ (needs reload to be set to True)
+ :param init_delay: Add a sleep command (in seconds) before the service is
+ restarted/reloaded
+ '''
+ ret = {'name': name,
+ 'changes': {},
+ 'result': True,
+ 'comment': {}}
+
+ service = kwargs.get('service')
+ services = kwargs.get('services')
+ if not services and service:
+ services = [service, ]
+ elif not services and not service:
+ ret['result'] = False
+ ret['comment'] = "Service was not defined"
+ return ret
+
+ container = kwargs.get('container', None)
+ if not container:
+ ret['result'] = False
+ ret['comment'] = "Container was not defined"
+ return ret
+
+ ret['comment'] = {}
+ if sfun == 'running':
+
+ for service in services:
+ status = __salt__['dockerng_service.status'](container, service)
+
+
+ if __opts__['test']:
+ ret['result'] = None
+ ret['comment'][service] = "Services will be restarted"
+ ret['changes'][service] = "will be restarted"
+ else:
+ res = __salt__['dockerng_service.restart'](container, service)
+ ret['comment'] = "Services has been restarted"
+ ret['changes'][service] = "restarted"
+ else:
+ ret['comment'] = 'Unable to trigger watch for dockerng_service.{0}'.format(sfun)
+ ret['result'] = False
+ return ret
diff --git a/debian/control b/debian/control
index d73ee38..d6faf77 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,7 @@
Maintainer: Michael Kuty <michael.kuty@tcpcloud.eu>
Section: admin
Priority: optional
-Build-Depends: salt-master, python, python-yaml, debhelper (>= 9), salt-master, python, python-yaml
+Build-Depends: salt-master, python, python-yaml, debhelper (>= 9), salt-master, python, python-yaml, salt-formula-linux
Standards-Version: 3.9.6
Homepage: http://www.tcpcloud.eu
Vcs-Browser: https://github.com/tcpcloud/salt-formula-docker
@@ -10,6 +10,6 @@
Package: salt-formula-docker
Architecture: all
-Depends: ${misc:Depends}, salt-master, reclass
+Depends: ${misc:Depends}
Description: docker salt formula
Install and configure docker system.
diff --git a/docker/client/init.sls b/docker/client/init.sls
index 8e0ca58..f628ddc 100644
--- a/docker/client/init.sls
+++ b/docker/client/init.sls
@@ -2,20 +2,22 @@
{%- if client.get('enabled') %}
include:
- {%- if client.network is defined %}
+ {%- if pillar.docker.client.network is defined %}
- docker.client.network
{%- endif %}
+ {%- if pillar.docker.client.container is defined %}
- docker.client.container
- {%- if client.compose is defined %}
+ {%- endif %}
+ {%- if pillar.docker.client.compose is defined %}
- docker.client.compose
{%- endif %}
- {%- if client.stack is defined %}
+ {%- if pillar.docker.client.stack is defined %}
- docker.client.stack
{%- endif %}
- {%- if client.registry is defined %}
+ {%- if pillar.docker.client.registry is defined %}
- docker.client.registry
{%- endif %}
- {%- if client.service is defined %}
+ {%- if pillar.docker.client.service is defined %}
- docker.client.service
{%- endif %}
diff --git a/docker/client/service.sls b/docker/client/service.sls
index dbe6de8..18a925a 100644
--- a/docker/client/service.sls
+++ b/docker/client/service.sls
@@ -35,8 +35,9 @@
{%- if service.workdir is defined %} --workdir {{ service.workdir }}{%- endif %}
{%- if service.mode is defined %} --mode {{ service.mode }}{%- endif %}
{%- if service.endpoint is defined %} --endpoint-mode {{ service.endpoint }}{%- endif %}
- {%- if service.constraint is defined %} --constraint {{ service.constraint }}{%- endif %}
{%- if service.hostname is defined %} --hostname {{ service.hostname }}{%- endif %}
+ {%- if service.constraint is defined %} --constraint {{ service.constraint }}{%- endif %}
+ {%- for constraint in service.get('constraints', []) %} --constraint {{ constraint }}{%- endfor %}
{%- for name, volume in service.get('volume', {}).iteritems() %} --mount {% for key, value in volume.iteritems() %}{{ key }}={{ value }}{% if not loop.last %},{% endif %}{% endfor %}{%- endfor %}
{%- for param, value in service.get('restart', {}).iteritems() %} --restart-{{ param }} {{ value }}{%- endfor %}
{%- for param, value in service.get('update', {}).iteritems() %} --update-{{ param }} {{ value }}{%- endfor %}
diff --git a/docker/client/stack.sls b/docker/client/stack.sls
index 8533f9a..8bf7ae8 100644
--- a/docker/client/stack.sls
+++ b/docker/client/stack.sls
@@ -52,9 +52,11 @@
{%- set path = volume.split(':')[0] %}
{%- elif volume is mapping and volume.get('type', 'bind') == 'bind' %}
{%- set path = volume.source %}
+ {%- else %}
+ {%- set path = None %}
{%- endif %}
- {%- if path is defined %}
+ {%- if path != None and path not in compose.get('volume', {}).keys() %}
docker_{{ app }}_{{ name }}_volume_{{ path }}:
file.directory:
- name: {{ path }}
diff --git a/docker/files/default b/docker/files/default
index 42f9bfb..db37876 100644
--- a/docker/files/default
+++ b/docker/files/default
@@ -6,7 +6,6 @@
# Use DOCKER_OPTS to modify the daemon startup options.
#DOCKER_OPTS="--dns 8.8.8.8 --dns 8.8.4.4"
-DOCKER_OPTS=" --log-driver={{ host.get('options', {}).get('log-driver','json-file') }} --log-opt max-size={{ host.get('options').get('log-opt', {}).get('max-size', '50m') }}"
# If you need Docker to use an HTTP proxy, it can also be specified here.
#export http_proxy="http://127.0.0.1:3128/"
diff --git a/docker/files/docker-compose.yml b/docker/files/docker-compose.yml
index f120327..f1685f0 100644
--- a/docker/files/docker-compose.yml
+++ b/docker/files/docker-compose.yml
@@ -1,5 +1,10 @@
version: '{{ compose.version|default("3") }}'
+{%- if compose.config|default({}) %}
+configs:
+ {{ compose.config|yaml(False)|indent(2) }}
+{%- endif %}
+
services:
{%- for name, srv in service.iteritems() %}
{%- set env_file_set = False %}
diff --git a/docker/files/http-proxy.conf b/docker/files/http-proxy.conf
index 13d494d..9ae5c00 100644
--- a/docker/files/http-proxy.conf
+++ b/docker/files/http-proxy.conf
@@ -1,5 +1,11 @@
{%- from "docker/map.jinja" import host with context -%}
[Service]
+{%- if host.proxy.get('http') %}
Environment="HTTP_PROXY={{ host.proxy.http }}"
+{%- endif -%}
+{%- if host.proxy.get('https') %}
Environment="HTTPS_PROXY={{ host.proxy.https }}"
+{%- endif -%}
+{%- if host.proxy.get('no_proxy') %}
Environment="NO_PROXY={{ host.proxy.no_proxy|join(',') }}"
+{%- endif -%}
diff --git a/docker/host.sls b/docker/host.sls
index dbd2cc0..60b3cca 100644
--- a/docker/host.sls
+++ b/docker/host.sls
@@ -48,6 +48,8 @@
- makedirs: True
- require_in:
- service: docker_service
+ - watch_in:
+ - service: docker_service
{% else %}
@@ -79,10 +81,11 @@
{%- for name,registry in host.registry.iteritems() %}
-docker_{{ registry.address }}_login:
+docker_{{ registry.get('address', name) }}_login:
cmd.run:
- - name: 'docker login -u {{ registry.user }} -p {{ registry.password }} {{ registry.address }}'
- - unless: grep {{ registry.address }} /root/.docker/config.json
+ - name: 'docker login -u {{ registry.user }} -p {{ registry.password }}{% if registry.get('address') %} {{ registry.address }}{% endif %}'
+ - user: {{ registry.get('system_user', 'root') }}
+ - unless: grep {{ registry.address|default('https://index.docker.io/v1/') }} {{ salt['user.info'](registry.get('system_user', 'root')).home }}/.docker/config.json
{%- endfor %}
diff --git a/docker/meta/fluentd.yml b/docker/meta/fluentd.yml
new file mode 100644
index 0000000..5e41d2a
--- /dev/null
+++ b/docker/meta/fluentd.yml
@@ -0,0 +1,45 @@
+{%- if pillar.get('fluentd', {}).get('agent', {}).get('enabled', False) %}
+{%- set positiondb = pillar.fluentd.agent.dir.positiondb %}
+agent:
+ config:
+ label:
+ docker:
+ input:
+ container:
+ type: tail
+ tag: temp.docker.container.*
+ path: /var/lib/docker/containers/*/*-json.log
+ path_key: log_path
+ pos_file: {{ positiondb }}/docker.container.pos
+ parser:
+ type: json
+ time_format: '%Y-%m-%dT%H:%M:%S.%NZ'
+ keep_time_key: false
+ filter:
+ enrich:
+ tag: 'temp.docker.container.**'
+ type: record_transformer
+ enable_ruby: true
+ remove_keys: log
+ record:
+ - name: severity_label
+ value: INFO
+ - name: Severity
+ value: 6
+ - name: programname
+ value: docker
+ - name: Payload
+ value: ${record['log']}
+ match:
+ cast_service_tag:
+ tag: 'temp.docker.container.**'
+ type: rewrite_tag_filter
+ rule:
+ - name: log_path
+ regexp: '^.*\/(.*)-json\.log$'
+ result: docker.container.$1
+ push_to_default:
+ tag: 'docker.container.*'
+ type: relabel
+ label: default_output
+{%- endif %}
diff --git a/docker/meta/sphinx.yml b/docker/meta/sphinx.yml
index 203eb0c..a12c200 100644
--- a/docker/meta/sphinx.yml
+++ b/docker/meta/sphinx.yml
@@ -41,4 +41,13 @@
- "{{ name }} (image {{ service.image }})"
{%- endfor %}
{%- endif %}
+ {%- if client.get('stack', {}) %}
+ stacks:
+ value:
+ {%- for name, stack in client.stack.iteritems() %}
+ {%- for svc_name, service in stack.service.iteritems() %}
+ - "{{ name }}-{{ svc_name }} (image {{ service.image }})"
+ {%- endfor %}
+ {%- endfor %}
+ {%- endif %}
{%- endif %}
diff --git a/metadata/service/support.yml b/metadata/service/support.yml
index a1c25d8..2127589 100644
--- a/metadata/service/support.yml
+++ b/metadata/service/support.yml
@@ -1,6 +1,8 @@
parameters:
docker:
_support:
+ fluentd:
+ enabled: true
telegraf:
enabled: true
collectd:
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 3f42101..9451611 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -110,7 +110,7 @@
}
salt_run() {
- [ -e ${VEN_DIR}/bin/activate ] && source ${VENV_DIR}/bin/activate
+ [ -e ${VENV_DIR}/bin/activate ] && source ${VENV_DIR}/bin/activate
salt-call ${SALT_OPTS} $*
}