Add support for docker stack deploy
diff --git a/README.rst b/README.rst
index 40c293f..2718b40 100644
--- a/README.rst
+++ b/README.rst
@@ -95,6 +95,43 @@
 Using Docker Compose
 ~~~~~~~~~~~~~~~~~~~~
 
+There are two states that provides this functionality:
+
+- docker.client.stack
+- docker.client.compose
+
+Stack is new and works with Docker Swarm Mode. Compose is legacy and works
+only if node isn't member of Swarm.
+Metadata for both states are similar and differs only in implementation.
+
+Stack
+^^^^^
+
+.. code-block:: yaml
+
+    docker:
+      client:
+        stack:
+          django_web:
+            enabled: true
+            update: true
+            environment:
+              SOMEVAR: somevalue
+            service:
+              db:
+                image: postgres
+              web:
+                image: djangoapp
+                volumes:
+                  - /srv/volumes/django:/srv/django
+                ports:
+                  - 8000:8000
+                depends_on:
+                  - db
+
+Compose
+^^^^^^^
+
 There are three options how to install docker-compose:
 
 - distribution package (default)
@@ -132,6 +169,9 @@
                 depends_on:
                   - db
 
+Service
+-------
+
 To deploy service in Swarm mode, you can use ``docker.client.service``:
 
 .. code-block:: yaml
diff --git a/docker/client/compose.sls b/docker/client/compose.sls
index ccebf4d..3a35649 100644
--- a/docker/client/compose.sls
+++ b/docker/client/compose.sls
@@ -1,5 +1,10 @@
 {% from "docker/map.jinja" import client with context %}
 
+{#-
+  This state is used for old-way docker-compose that doesn't work with Docker
+  Swarm mode. For Docker Swarm, use docker.client.stack
+#}
+
 include:
   - docker.client
   {%- if client.compose.source.engine == 'docker' %}
diff --git a/docker/client.sls b/docker/client/init.sls
similarity index 84%
rename from docker/client.sls
rename to docker/client/init.sls
index b7a87eb..2286f9d 100644
--- a/docker/client.sls
+++ b/docker/client/init.sls
@@ -9,6 +9,9 @@
   {%- if client.compose is defined %}
   - docker.client.compose
   {%- endif %}
+  {%- if client.stack is defined %}
+  - docker.client.stack
+  {%- endif %}
   {%- if client.service is defined %}
   - docker.client.service
   {%- endif %}
diff --git a/docker/client/stack.sls b/docker/client/stack.sls
new file mode 100644
index 0000000..896ed4b
--- /dev/null
+++ b/docker/client/stack.sls
@@ -0,0 +1,89 @@
+{% from "docker/map.jinja" import client with context %}
+
+include:
+  - docker.client
+
+{%- for app, compose in client.stack.iteritems() %}
+  {%- if compose.service is defined %}
+
+docker_{{ app }}_dir:
+  file.directory:
+    - name: {{ client.compose.base }}/{{ app }}
+    - makedirs: true
+
+docker_{{ app }}_compose:
+  file.managed:
+    - name: {{ client.compose.base }}/{{ app }}/docker-compose.yml
+    - source: salt://docker/files/docker-compose.yml
+    - template: jinja
+    - defaults:
+        compose: {{ compose }}
+        service: {{ compose.service }}
+        network: {{ compose.network|default({}) }}
+    - require:
+        - file: docker_{{ app }}_dir
+
+    {%- if compose.environment is defined %}
+
+docker_{{ app }}_env:
+  file.managed:
+    - name: {{ client.compose.base }}/{{ app }}/.env
+    - source: salt://docker/files/docker-env
+    - template: jinja
+    - user: {{ compose.user|default("root") }}
+    - mode: 640
+    - defaults:
+        env: {{ compose.environment }}
+    - require:
+        - file: docker_{{ app }}_dir
+
+    {%- else %}
+
+docker_{{ app }}_env:
+  file.absent:
+    - name: {{ client.compose.base }}/{{ app }}/.env
+
+    {%- endif %}
+
+    {%- if compose.enabled|default(True) %}
+
+docker_stack_{{ app }}:
+  cmd.run:
+    - name: docker stack deploy --compose-file docker-compose.yml {{ app }}
+    - cwd: {{ client.compose.base }}/{{ app }}
+    - user: {{ compose.user|default("root") }}
+    - unless: "docker stack ls | grep '{{ app }}'"
+    - require:
+      - file: docker_{{ app }}_env
+      - file: docker_{{ app }}_compose
+
+docker_stack_{{ app }}_update:
+  cmd.wait:
+    - name: docker stack deploy --compose-file docker-compose.yml {{ app }}
+    - cwd: {{ client.compose.base }}/{{ app }}
+    - user: {{ compose.user|default("root") }}
+    - require:
+      - cmd: docker_stack_{{ app }}
+    - watch:
+      - file: docker_{{ app }}_env
+      - file: docker_{{ app }}_compose
+
+    {%- else %}
+
+docker_remove_{{ app }}:
+  cmd.run:
+    - name: docker stack rm {{ app }}
+    - user: {{ compose.user|default("root") }}
+    - onlyif: "docker stack ls | grep '{{ app }}'"
+
+    {%- endif %}
+
+  {%- else %}
+
+    {#-
+      No stack.service is defined so we can add support for deploying using
+      bundle file, etc.
+    #}
+
+  {%- endif %}
+{%- endfor %}
diff --git a/docker/files/docker-compose.yml b/docker/files/docker-compose.yml
index 81de7ed..6e03b18 100644
--- a/docker/files/docker-compose.yml
+++ b/docker/files/docker-compose.yml
@@ -1,4 +1,4 @@
-version: '2'
+version: '3'
 services:
   {%- for name, srv in service.iteritems() %}
   {%- set env_file_set = False %}
@@ -12,6 +12,16 @@
     {%- endif %}
   {%- endfor %}
 
+{%- if network|default({}) %}
+networks:
+{%- for name, net in network.iteritems() %}
+  {{ name }}:
+    {%- for key, value in net.iteritems() %}
+    {{ key }}: {{ value }}
+    {%- endfor %}
+{%- endfor %}
+{%- endif %}
+
 {#-
   vim: syntax=jinja
 -#}
diff --git a/tests/pillar/client_deploy.sls b/tests/pillar/client_deploy.sls
new file mode 100644
index 0000000..860531a
--- /dev/null
+++ b/tests/pillar/client_deploy.sls
@@ -0,0 +1,19 @@
+docker:
+  client:
+    enabled: true
+    stack:
+      django_web:
+        enabled: true
+        environment:
+          SOMEVAR: somevalue
+        service:
+          db:
+            image: postgres
+          web:
+            image: djangoapp
+            volumes:
+              - /srv/volumes/django:/srv/django
+            ports:
+              - 8000:8000
+            depends_on:
+              - db