Merge pull request #5 from tcpcloud/logs
docker logs
diff --git a/Makefile b/Makefile
index fc83783..e4c5a0e 100644
--- a/Makefile
+++ b/Makefile
@@ -14,6 +14,7 @@
cp -a $(FORMULANAME) $(DESTDIR)/$(SALTENVDIR)/
[ ! -d _modules ] || cp -a _modules $(DESTDIR)/$(SALTENVDIR)/
[ ! -d _states ] || cp -a _states $(DESTDIR)/$(SALTENVDIR)/ || true
+ [ ! -d _grains ] || cp -a _grains $(DESTDIR)/$(SALTENVDIR)/ || true
# Metadata
[ -d $(DESTDIR)/$(RECLASSDIR)/service/$(FORMULANAME) ] || mkdir -p $(DESTDIR)/$(RECLASSDIR)/service/$(FORMULANAME)
cp -a metadata/service/* $(DESTDIR)/$(RECLASSDIR)/service/$(FORMULANAME)
diff --git a/README.rst b/README.rst
index 31dff43..5cea699 100644
--- a/README.rst
+++ b/README.rst
@@ -29,6 +29,43 @@
engine: json-file
size: 50m
+Swarm
+-----
+
+Role can be master, manager or worker. Where master is the first manager that
+will initialize the swarm.
+
+Metadata for manager (first node):
+
+.. code-block:: yaml
+
+ docker:
+ host:
+ enabled: true
+ swarm:
+ role: manager
+ advertise_addr: 192.168.1.5
+ bind:
+ address: 192.168.1.5
+ port: 2377
+
+Metadata for worker:
+
+.. code-block:: yaml
+
+ docker:
+ host:
+ enabled: true
+ swarm:
+ role: worker
+ master:
+ host: 192.168.1.5
+ port: 2377
+
+Token to join to master node is obtained from grains using salt.mine. In case
+of any ``join_token undefined`` issues, ensure you have ``docker_swarm_``
+grains available.
+
Client
------
@@ -93,6 +130,33 @@
depends_on:
- db
+Service
+-------
+
+To deploy service in Swarm mode, you can use ``docker.client.service``:
+
+.. code-block:: yaml
+
+ parameters:
+ docker:
+ client:
+ service:
+ postgresql:
+ environment:
+ POSTGRES_USER: user
+ POSTGRES_PASSWORD: password
+ POSTGRES_DB: mydb
+ restart:
+ condition: on-failure
+ image: "postgres:9.5"
+ ports:
+ - 5432:5432
+ volume:
+ data:
+ type: bind
+ source: /srv/volumes/postgresql/maas
+ destination: /var/lib/postgresql/data
+
Registry
--------
diff --git a/_grains/docker_swarm.py b/_grains/docker_swarm.py
new file mode 100644
index 0000000..8b8ecb7
--- /dev/null
+++ b/_grains/docker_swarm.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+import os
+import yaml
+import subprocess
+
+
+def main():
+ output = {}
+ if os.path.exists('/var/lib/docker/swarm/control.sock'):
+ try:
+ output["docker_swarm_tokens"] = {
+ 'worker': subprocess.check_output(["docker", "swarm", "join-token", "-q", "worker"]).strip(),
+ 'manager': subprocess.check_output(["docker", "swarm", "join-token", "-q", "manager"]).strip()
+ }
+ except subprocess.CalledProcessError:
+ pass
+
+ if os.path.exists('/var/lib/docker/swarm/state.json'):
+ with open('/var/lib/docker/swarm/state.json') as fh:
+ state = yaml.load(fh)
+ for key, value in state[0].iteritems():
+ output["docker_swarm_%s" % key] = value
+
+ if os.path.exists('/var/lib/docker/swarm/docker-state.json'):
+ with open('/var/lib/docker/swarm/docker-state.json') as fh:
+ state = yaml.load(fh)
+ for key, value in state.iteritems():
+ output["docker_swarm_%s" % key] = value
+
+ if output:
+ return output
+ else:
+ return None
diff --git a/docker/client.sls b/docker/client.sls
index 0ed5638..6de632b 100644
--- a/docker/client.sls
+++ b/docker/client.sls
@@ -6,6 +6,9 @@
{%- if client.compose is defined %}
- docker.client.compose
{%- endif %}
+ {%- if client.service is defined %}
+ - docker.client.service
+ {%- endif %}
docker_python:
pkg.installed:
diff --git a/docker/client/service.sls b/docker/client/service.sls
new file mode 100644
index 0000000..272ba71
--- /dev/null
+++ b/docker/client/service.sls
@@ -0,0 +1,64 @@
+{% from "docker/map.jinja" import client with context %}
+
+include:
+ - docker.client
+
+{%- for name, service in client.get('service', {}).iteritems() %}
+{%- if service.get('enabled', True) %}
+
+docker_service_{{ name }}_create:
+ cmd.run:
+ - name: >
+ docker service create
+ --name {{ name }}
+ --with-registry-auth
+ {%- for env, value in service.get('environment', {}).iteritems() %} -e {{ env }}="{{ value }}"{%- endfor %}
+ {%- for port in service.get('ports', []) %} -p {{ port }}{%- endfor %}
+ {%- for label, value in service.get('label', {}).iteritems() %} -l {{ label }}="{{ value }}"{%- endfor %}
+ {%- if service.network is defined %} --network {{ service.network }}{%- endif %}
+ {%- if service.replicas is defined %} --replicas {{ service.replicas }}{%- endif %}
+ {%- if service.user is defined %} --user {{ service.user }}{%- endif %}
+ {%- 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 %}
+ {%- 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 %}
+ {%- for param, value in service.get('log', {}).iteritems() %} --log-{{ param }} {{ value }}{%- endfor %}
+ {%- for param, value in service.get('limit', {}).iteritems() %} --limit-{{ param }} {{ value }}{%- endfor %}
+ {%- for param, value in service.get('reserve', {}).iteritems() %} --reserve-{{ param }} {{ value }}{%- endfor %}
+ {{ service.image }}
+ - unless: "docker service ls | grep {{ name }}"
+
+{%- if service.get('update_service', False) %}
+docker_service_{{ name }}_update:
+ cmd.run:
+ - name: >
+ docker service update
+ --name {{ name }}
+ --with-registry-auth
+ {%- if service.replicas is defined %} --replicas {{ service.replicas }}{%- endif %}
+ {%- if service.user is defined %} --user {{ service.user }}{%- endif %}
+ {%- if service.workdir is defined %} --workdir {{ service.workdir }}{%- endif %}
+ {%- if service.endpoint is defined %} --endpoint-mode {{ service.endpoint }}{%- endif %}
+ {%- for param, value in service.get('restart', {}).iteritems() %} --restart-{{ param }} {{ value }}{%- endfor %}
+ {%- for param, value in service.get('update', {}).iteritems() %} --update-{{ param }} {{ value }}{%- endfor %}
+ {%- for param, value in service.get('log', {}).iteritems() %} --log-{{ param }} {{ value }}{%- endfor %}
+ {%- for param, value in service.get('limit', {}).iteritems() %} --limit-{{ param }} {{ value }}{%- endfor %}
+ {%- for param, value in service.get('reserve', {}).iteritems() %} --reserve-{{ param }} {{ value }}{%- endfor %}
+ --image {{ service.image }}
+ {{ name }}
+ - require:
+ - cmd: docker_service_{{ name }}_create
+{%- endif %}
+
+{%- else %}
+
+docker_service_{{ name }}_rm:
+ cmd.run:
+ - name: docker service rm {{ name }}
+ - onlyif: "docker service ls | grep {{ name }}"
+
+{%- endif %}
+{%- endfor %}
diff --git a/docker/init.sls b/docker/init.sls
index 6256940..ac6eeca 100644
--- a/docker/init.sls
+++ b/docker/init.sls
@@ -3,6 +3,9 @@
{%- if pillar.docker.host is defined %}
- docker.host
{%- endif %}
+{%- if pillar.docker.swarm is defined %}
+- docker.swarm
+{%- endif %}
{%- if pillar.docker.client is defined %}
- docker.client
{%- endif %}
diff --git a/docker/map.jinja b/docker/map.jinja
index d475371..8644de5 100644
--- a/docker/map.jinja
+++ b/docker/map.jinja
@@ -18,6 +18,14 @@
},
}, grain='os', merge=salt['pillar.get']('docker:host')) %}
+{% set swarm = salt['grains.filter_by']({
+ 'default': {
+ 'master': {
+ 'port': 2377
+ }
+ }
+}, grain='os', merge=salt['pillar.get']('docker:swarm')) %}
+
{% set client = salt['grains.filter_by']({
'default': {
'pkgs': ['python-docker'],
diff --git a/docker/swarm.sls b/docker/swarm.sls
new file mode 100644
index 0000000..314c183
--- /dev/null
+++ b/docker/swarm.sls
@@ -0,0 +1,48 @@
+{% from "docker/map.jinja" import swarm with context %}
+{%- if swarm.enabled|default(True) %}
+
+include:
+ - docker.host
+
+{%- if swarm.role == 'master' %}
+
+docker_swarm_init:
+ cmd.run:
+ - name: >
+ docker swarm init
+ {%- if swarm.advertise_addr is defined %} --advertise-addr {{ swarm.advertise_addr }}{%- endif %}
+ {%- if swarm.get('bind', {}).get('address', None) %} --listen-addr {{ swarm.bind.address }}{% if swarm.bind.port is defined %}:{{ swarm.bind.port }}{% endif %}{%- endif %}
+ - unless: "test -e /var/lib/docker/swarm/control.sock"
+ - require:
+ - service: docker_service
+
+docker_swarm_grains_publish:
+ module.run:
+ - name: mine.update
+ - watch:
+ - cmd: docker_swarm_init
+
+{%- else %}
+
+{%- for node_name, node_grains in salt['mine.get']('*', 'grains.items').iteritems() %}
+{%- if node_grains.get("docker_swarm_AdvertiseAddr", None) == swarm.master.host+":"+swarm.master.port|string %}
+{%- set join_token = node_grains.get('docker_swarm_tokens').get(swarm.role, "unknown") %}
+
+docker_swarm_join:
+ cmd.run:
+ - name: >
+ docker swarm join
+ --token {{ join_token }}
+ {%- if swarm.advertise_addr is defined %} --advertise-addr {{ swarm.advertise_addr }}{%- endif %}
+ {%- if swarm.get('bind', {}).get('address', None) %} --listen-addr {{ swarm.bind.address }}{% if swarm.bind.port is defined %}:{{ swarm.bind.port }}{% endif %}{%- endif %}
+ {{ swarm.master.host }}:{{ swarm.master.port }}
+ - unless: "test -e /var/lib/docker/swarm/control.sock"
+ - require:
+ - service: docker_service
+
+{%- endif %}
+{%- endfor %}
+
+{%- endif %}
+
+{%- endif %}