SaltStack - Architect integration

Change-Id: I96e16a3b203f9b3c9c150db1cb85e966bd14b573
diff --git a/README.rst b/README.rst
index 454f465..705efd4 100644
--- a/README.rst
+++ b/README.rst
@@ -28,6 +28,21 @@
 .. literalinclude:: tests/pillar/master_single_reclass.sls
    :language: yaml
 
+Salt master with Architect ENC metadata backend
+
+.. code-block:: yaml
+
+    salt:
+      master:
+        enabled: true
+        pillar:
+          engine: architect
+          project: project-name
+          host: architect-api
+          port: 8181
+          username: salt
+          password: password
+
 Salt master with multiple ext_pillars
 
 .. literalinclude:: tests/pillar/master_single_extpillars.sls
@@ -150,7 +165,6 @@
               host: 127.0.0.1
               port: 9999
 
-
 Salt engine definition for saltgraph metadata collector
 
 .. code-block:: yaml
@@ -166,6 +180,21 @@
             password: salt
             database: salt
 
+Salt engine definition for Architect service
+
+.. code-block:: yaml
+
+    salt:
+      master:
+        engine:
+          architect:
+            engine: architect
+            project: project-name
+            host: architect-api
+            port: 8181
+            username: salt
+            password: password
+
 Salt engine definition for sending events from docker events
 
 .. code-block:: yaml
diff --git a/_engines/architect.py b/_engines/architect.py
new file mode 100644
index 0000000..4096a73
--- /dev/null
+++ b/_engines/architect.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+"""
+Salt engine for intercepting state jobs and forwarding to the Architect
+service.
+"""
+
+# Import python libs
+from __future__ import absolute_import
+import json
+import logging
+
+# Import salt libs
+import salt.utils.event
+import salt.utils.http
+
+log = logging.getLogger(__name__)
+
+
+def start(project='default',
+          host='127.0.0.1',
+          port=8181,
+          username=None,
+          password=None):
+    '''
+    Listen to state jobs events and forward Salt states
+    '''
+    url = "{}://{}:{}/salt/{}/event/{}".format('http',
+                                               host,
+                                               port,
+                                               'v1',
+                                               project)
+    target_functions = ['state.sls', 'state.apply', 'state.highstate']
+
+    if __opts__['__role'] == 'master':
+        event_bus = salt.utils.event.get_master_event(__opts__,
+                                                      __opts__['sock_dir'],
+                                                      listen=True)
+    else:
+        event_bus = salt.utils.event.get_event(
+            'minion',
+            transport=__opts__['transport'],
+            opts=__opts__,
+            sock_dir=__opts__['sock_dir'],
+            listen=True)
+
+    log.info('Salt Architect engine initialised')
+
+    while True:
+        event = event_bus.get_event()
+        if event and event.get('fun', None) in target_functions:
+            is_test_run = 'test=true' in [arg.lower() for arg in event.get('fun_args', [])]
+            if not is_test_run:
+                data = salt.utils.http.query(url=url,
+                                             method='POST',
+                                             decode=False,
+                                             data=json.dumps(event))
+                if 'OK' in data.get('body', ''):
+                    log.info("Architect Engine request to '{}'"
+                             " was successful".format(url))
+                else:
+                    log.warning("Problem with Architect Engine"
+                                " request to '{}' ({})".format(url, data))
diff --git a/salt/files/architect.yml b/salt/files/architect.yml
new file mode 100644
index 0000000..dcba23f
--- /dev/null
+++ b/salt/files/architect.yml
@@ -0,0 +1,8 @@
+{% from "salt/map.jinja" import master with context %}
+project: {{ master.pillar.project }}
+host: {{ master.pillar.host }}
+port: {{ master.pillar.port }}
+{%- if master.pillar.username is defined %}
+username: {{ master.pillar.username }}
+password: {{ master.pillar.password }}
+{%- endif %}
diff --git a/salt/files/master.conf b/salt/files/master.conf
index 1802d82..ddbbf18 100644
--- a/salt/files/master.conf
+++ b/salt/files/master.conf
@@ -60,6 +60,14 @@
   - {{ master.pillar.get('salt', {}).get('path', '/srv/salt/pillar') }}
 {%- endif %}
 
+{%- if master.pillar.engine == 'architect' %}
+ext_pillar:
+  - cmd_yaml: 'architect-salt-pillar %s'
+
+master_tops:
+  ext_nodes: architect-salt-top
+{%- endif %}
+
 {%- if master.pillar.engine == 'reclass' or (master.pillar.engine == 'composite' and master.pillar.reclass is defined) %}
 
 reclass: &reclass
diff --git a/salt/master/pillar.sls b/salt/master/pillar.sls
index 806511d..709cc8c 100644
--- a/salt/master/pillar.sls
+++ b/salt/master/pillar.sls
@@ -31,6 +31,25 @@
 
 {%- endif %}
 
+{%- elif master.pillar.engine == 'architect' %}
+
+salt_pillar_architect_package:
+  pip.installed:
+    - name: architect-client
+
+salt_pillar_architect_package_config_dir:
+  file.directory:
+  - name: /etc/architect
+
+salt_pillar_architect_package_config_file:
+  file.managed:
+  - name: /etc/architect/client.yml
+  - source: salt://salt/files/architect.yml
+  - user: root
+  - template: jinja
+  - require:
+    - file: salt_pillar_architect_package_config_dir
+
 {%- elif master.pillar.engine == 'reclass' %}
 
 include: