WebSSO support

* WebSSO support
* Run keystone under apache2
* Remove trailing whitespaces

Change-Id: I7ccd9dbf57571a03471bb06c961079f4bd099ff8
diff --git a/README.rst b/README.rst
index 54e3480..ff64b7b 100644
--- a/README.rst
+++ b/README.rst
@@ -21,7 +21,7 @@
 Sample pillars
 ==============
 
-.. caution:: 
+.. caution::
 
     When you use localhost as your database host (keystone:server:
     atabase:host), sqlalchemy will try to connect to /var/run/mysql/
@@ -270,6 +270,47 @@
         notification: true
         notification_format: cadf
 
+Run keystone under Apache
+
+.. code-block:: yaml
+
+    keystone:
+      server:
+        service_name: apache2
+    apache:
+      server:
+        enabled: true
+        default_mpm: event
+        site:
+          keystone:
+            enabled: true
+            type: keystone
+            name: wsgi
+            host:
+              name: ${linux:network:fqdn}
+        modules:
+          - wsgi
+
+Enable Federated keystone
+
+.. code-block:: yaml
+
+    keystone:
+      server:
+        websso:
+          protocol: saml2
+          remote_id_attribute: Shib-Identity-Provider
+          federation_driver: keystone.contrib.federation.backends.sql.Federation
+          trusted_dashboard:
+            - http://${_param:proxy_vip_address_public}/horizon/auth/websso/
+    apache:
+      server:
+        pkgs:
+          - apache2
+          - libapache2-mod-shib2
+        modules:
+          - wsgi
+          - shib2
 
 Keystone client
 ---------------
diff --git a/keystone/client/server.sls b/keystone/client/server.sls
index 7fafc04..38d8169 100644
--- a/keystone/client/server.sls
+++ b/keystone/client/server.sls
@@ -32,7 +32,7 @@
   keystone.role_present:
   - names: {{ server.roles }}
   {%- if server.admin.token is defined %}
-  - connection_token: {{ connection_args.token }} 
+  - connection_token: {{ connection_args.token }}
   - connection_endpoint: {{ connection_args.endpoint }}
   {%- else %}
   - connection_user: {{ connection_args.user }}
@@ -51,7 +51,7 @@
   - service_type: {{ service.type }}
   - description: {{ service.description }}
   {%- if server.admin.token is defined %}
-  - connection_token: {{ connection_args.token }} 
+  - connection_token: {{ connection_args.token }}
   - connection_endpoint: {{ connection_args.endpoint }}
   {%- else %}
   - connection_user: {{ connection_args.user }}
@@ -72,7 +72,7 @@
   - require:
     - keystone: keystone_{{ server_name }}_service_{{ service_name }}
   {%- if server.admin.token is defined %}
-  - connection_token: {{ connection_args.token }} 
+  - connection_token: {{ connection_args.token }}
   - connection_endpoint: {{ connection_args.endpoint }}
   {%- else %}
   - connection_user: {{ connection_args.user }}
@@ -91,10 +91,10 @@
   keystone.tenant_present:
   - name: {{ tenant_name }}
   {%- if tenant.description is defined %}
-  - description: {{ tenant.description }} 
+  - description: {{ tenant.description }}
   {%- endif %}
   {%- if server.admin.token is defined %}
-  - connection_token: {{ connection_args.token }} 
+  - connection_token: {{ connection_args.token }}
   - connection_endpoint: {{ connection_args.endpoint }}
   {%- else %}
   - connection_user: {{ connection_args.user }}
@@ -126,7 +126,7 @@
     - keystone: keystone_{{ server_name }}_tenant_{{ tenant_name }}
     - keystone: keystone_{{ server_name }}_roles
   {%- if server.admin.token is defined %}
-  - connection_token: {{ connection_args.token }} 
+  - connection_token: {{ connection_args.token }}
   - connection_endpoint: {{ connection_args.endpoint }}
   {%- else %}
   - connection_user: {{ connection_args.user }}
diff --git a/keystone/files/apache.conf b/keystone/files/apache.conf
new file mode 100644
index 0000000..b47d1ee
--- /dev/null
+++ b/keystone/files/apache.conf
@@ -0,0 +1,2 @@
+{%- from "keystone/map.jinja" import server with context %}
+{%- include "keystone/files/"+server.version+"/wsgi-keystone.conf" %}
diff --git a/keystone/files/juno/wsgi-keystone.conf b/keystone/files/juno/wsgi-keystone.conf
new file mode 100644
index 0000000..d542a87
--- /dev/null
+++ b/keystone/files/juno/wsgi-keystone.conf
@@ -0,0 +1,8 @@
+WSGIScriptAlias /keystone/main  /var/www/cgi-bin/keystone/main
+WSGIScriptAlias /keystone/admin  /var/www/cgi-bin/keystone/admin
+
+<Location "/keystone">
+ NSSRequireSSL
+ Authtype none
+</Location>
+
diff --git a/keystone/files/keystonerc_user b/keystone/files/keystonerc_user
index 62293bf..61f6e67 100644
--- a/keystone/files/keystonerc_user
+++ b/keystone/files/keystonerc_user
@@ -11,4 +11,3 @@
 export OS_SERVICE_ENDPOINT="http://{{ provider.host }}:{{ provider.port }}/{{ provider.get('version', 'v2.0') }}/"
 {%- endif %}
 export OS_AUTH_STRATEGY=keystone
-	
\ No newline at end of file
diff --git a/keystone/files/kilo/wsgi-keystone.conf b/keystone/files/kilo/wsgi-keystone.conf
new file mode 100644
index 0000000..c6a8214
--- /dev/null
+++ b/keystone/files/kilo/wsgi-keystone.conf
@@ -0,0 +1,38 @@
+{%- from "keystone/map.jinja" import server with context %}
+{%- set site = salt['pillar.get']('apache:server:site:'+site_name) %}
+Listen {% if server.bind.address is defined %}{{ server.bind.address }}{% else %}{{ server.bind.public_address }}{% endif %}:5000
+Listen {% if server.bind.address is defined %}{{ server.bind.address }}{% else %}{{ server.bind.public_address }}{% endif %}:35357
+
+<VirtualHost {% if server.bind.address is defined %}{{ server.bind.address }}{% else %}{{ server.bind.public_address }}{% endif %}:5000>
+{%- include "apache/files/_name.conf" %}
+{%- include "apache/files/_ssl.conf" %}
+{%- include "apache/files/_locations.conf" %}
+
+    WSGIDaemonProcess keystone-public processes=5 threads=1 user=keystone display-name=%{GROUP}
+    WSGIProcessGroup keystone-public
+    WSGIScriptAlias / /var/www/cgi-bin/keystone/main
+    WSGIApplicationGroup %{GLOBAL}
+    WSGIPassAuthorization On
+    <IfVersion >= 2.4>
+      ErrorLogFormat "%{cu}t %M"
+    </IfVersion>
+{%- include "apache/files/_log.conf" %}
+</VirtualHost>
+
+<VirtualHost {% if server.bind.address is defined %}{{ server.bind.address }}{% else %}{{ server.bind.public_address }}{% endif %}:35357>
+{%- include "apache/files/_name.conf" %}
+{%- include "apache/files/_ssl.conf" %}
+{%- include "apache/files/_locations.conf" %}
+
+    WSGIDaemonProcess keystone-admin processes=5 threads=1 user=keystone display-name=%{GROUP}
+    WSGIProcessGroup keystone-admin
+    WSGIScriptAlias / /var/www/cgi-bin/keystone/admin
+    WSGIApplicationGroup %{GLOBAL}
+    WSGIPassAuthorization On
+    <IfVersion >= 2.4>
+      ErrorLogFormat "%{cu}t %M"
+    </IfVersion>
+    ErrorLog /var/log/apache2/keystone.log
+    CustomLog /var/log/apache2/keystone_access.log combined
+{%- include "apache/files/_log.conf" %}
+</VirtualHost>
diff --git a/keystone/files/liberty/keystone.conf.Debian b/keystone/files/liberty/keystone.conf.Debian
index 48cb53f..e52f532 100644
--- a/keystone/files/liberty/keystone.conf.Debian
+++ b/keystone/files/liberty/keystone.conf.Debian
@@ -301,6 +301,11 @@
 # Allowed authentication methods. (list value)
 #methods = external,password,token,oauth1
 
+{% if server.websso is defined %}
+methods = external,password,token,{{ server.websso.protocol }}
+{{ server.websso.protocol }} = keystone.auth.plugins.mapped.Mapped
+{%- endif %}
+
 # Entrypoint for the password auth plugin module in the keystone.auth.password
 # namespace. (string value)
 #password = <None>
@@ -318,6 +323,10 @@
 # namespace. (string value)
 #oauth1 = <None>
 
+{% if server.websso is defined %}
+[{{ server.websso.protocol }}]
+remote_id_attribute = {{ server.websso.remote_id_attribute }}
+{%- endif %}
 
 [cache]
 
@@ -780,6 +789,9 @@
 # Entrypoint for the federation backend driver in the keystone.federation
 # namespace. (string value)
 #driver = sql
+{% if server.websso is defined %}
+driver = {{ server.websso.federation_driver }}
+{%- endif %}
 
 # Value to be used when filtering assertion parameters from the environment.
 # (string value)
@@ -802,6 +814,13 @@
 # example: trusted_dashboard=http://acme.com trusted_dashboard=http://beta.com
 # (multi valued)
 #trusted_dashboard =
+{%- if server.websso is defined %}
+{%- if server.websso.trusted_dashboard is defined %}
+{%- for dashboard in server.websso.trusted_dashboard %}
+trusted_dashboard = {{ dashboard }}
+{%- endfor %}
+{%- endif %}
+{%- endif %}
 
 # Location of Single Sign-On callback handler, will return a token to a trusted
 # dashboard host. (string value)
diff --git a/keystone/files/liberty/wsgi-keystone.conf b/keystone/files/liberty/wsgi-keystone.conf
new file mode 100644
index 0000000..beaf74b
--- /dev/null
+++ b/keystone/files/liberty/wsgi-keystone.conf
@@ -0,0 +1,92 @@
+{%- from "keystone/map.jinja" import server with context %}
+{%- set site = salt['pillar.get']('apache:server:site:'+site_name) %}
+Listen {% if server.bind.address is defined %}{{ server.bind.address }}{% else %}{{ server.bind.public_address }}{% endif %}:5000
+Listen {% if server.bind.address is defined %}{{ server.bind.address }}{% else %}{{ server.bind.public_address }}{% endif %}:35357
+
+<VirtualHost {% if server.bind.address is defined %}{{ server.bind.address }}{% else %}{{ server.bind.public_address }}{% endif %}:5000>
+{%- include "apache/files/_name.conf" %}
+{%- include "apache/files/_ssl.conf" %}
+{%- include "apache/files/_locations.conf" %}
+
+    WSGIDaemonProcess keystone-public processes=5 threads=1 user=keystone group=keystone display-name=%{GROUP}
+    WSGIProcessGroup keystone-public
+    WSGIScriptAlias / /usr/bin/keystone-wsgi-public
+    WSGIApplicationGroup %{GLOBAL}
+    WSGIPassAuthorization On
+    ErrorLogFormat "%{cu}t %M"
+{%- include "apache/files/_log.conf" %}
+
+    <Directory /usr/bin>
+      Require all granted
+    </Directory>
+
+    {% if server.websso is defined %}
+    WSGIScriptAliasMatch ^(/v3/OS-FEDERATION/identity_providers/.*?/protocols/.*?/auth)$ /usr/bin/keystone-wsgi-public/$1
+    <Location /Shibboleth.sso>
+      SetHandler shib
+    </Location>
+    <LocationMatch /v3/auth/OS-FEDERATION/identity_providers/.*?/protocols/saml2/websso>
+      ShibRequestSetting requireSession 1
+      AuthType shibboleth
+      ShibExportAssertion Off
+      Require valid-user
+    </LocationMatch>
+    <LocationMatch /v3/auth/OS-FEDERATION/websso/saml2>
+      ShibRequestSetting requireSession 1
+      AuthType shibboleth
+      ShibExportAssertion Off
+      Require valid-user
+    </LocationMatch>
+    <LocationMatch /v3/OS-FEDERATION/identity_providers/.*?/protocols/saml2/auth>
+      ShibRequestSetting requireSession 1
+      AuthType shibboleth
+      ShibExportAssertion Off
+      Require valid-user
+    </LocationMatch>
+    {%- endif %}
+
+</VirtualHost>
+
+<VirtualHost {% if server.bind.address is defined %}{{ server.bind.address }}{% else %}{{ server.bind.public_address }}{% endif %}:35357>
+{%- include "apache/files/_name.conf" %}
+{%- include "apache/files/_ssl.conf" %}
+{%- include "apache/files/_locations.conf" %}
+
+    WSGIDaemonProcess keystone-admin processes=5 threads=1 user=keystone group=keystone display-name=%{GROUP}
+    WSGIProcessGroup keystone-admin
+    WSGIScriptAlias / /usr/bin/keystone-wsgi-admin
+    WSGIApplicationGroup %{GLOBAL}
+    WSGIPassAuthorization On
+    ErrorLogFormat "%{cu}t %M"
+{%- include "apache/files/_log.conf" %}
+
+    <Directory /usr/bin>
+      Require all granted
+    </Directory>
+
+    {% if server.websso is defined %}
+    WSGIScriptAliasMatch ^(/v3/OS-FEDERATION/identity_providers/.*?/protocols/.*?/auth)$ /usr/bin/keystone-wsgi-admin/$1
+    <Location /Shibboleth.sso>
+      SetHandler shib
+    </Location>
+    <LocationMatch /v3/auth/OS-FEDERATION/identity_providers/.*?/protocols/saml2/websso>
+      ShibRequestSetting requireSession 1
+      AuthType shibboleth
+      ShibExportAssertion Off
+      Require valid-user
+    </LocationMatch>
+    <LocationMatch /v3/auth/OS-FEDERATION/websso/saml2>
+      ShibRequestSetting requireSession 1
+      AuthType shibboleth
+      ShibExportAssertion Off
+      Require valid-user
+    </LocationMatch>
+    <LocationMatch /v3/OS-FEDERATION/identity_providers/.*?/protocols/saml2/auth>
+      ShibRequestSetting requireSession 1
+      AuthType shibboleth
+      ShibExportAssertion Off
+      Require valid-user
+    </LocationMatch>
+    {%- endif %}
+
+</VirtualHost>
diff --git a/keystone/files/mitaka/keystone.conf.Debian b/keystone/files/mitaka/keystone.conf.Debian
index e07fdc8..06b84c2 100644
--- a/keystone/files/mitaka/keystone.conf.Debian
+++ b/keystone/files/mitaka/keystone.conf.Debian
@@ -355,6 +355,11 @@
 # Allowed authentication methods. (list value)
 #methods = external,password,token,oauth1
 
+{% if server.websso is defined %}
+methods = external,password,token,{{ server.websso.protocol }}
+{{ server.websso.protocol }} = keystone.auth.plugins.mapped.Mapped
+{%- endif %}
+
 # Entrypoint for the password auth plugin module in the keystone.auth.password
 # namespace. (string value)
 #password = <None>
@@ -372,6 +377,10 @@
 # namespace. (string value)
 #oauth1 = <None>
 
+{% if server.websso is defined %}
+[{{ server.websso.protocol }}]
+remote_id_attribute = {{ server.websso.remote_id_attribute }}
+{%- endif %}
 
 [cache]
 
@@ -834,6 +843,9 @@
 # Entrypoint for the federation backend driver in the keystone.federation
 # namespace. (string value)
 #driver = sql
+{% if server.websso is defined %}
+driver = {{ server.websso.federation_driver }}
+{%- endif %}
 
 # Value to be used when filtering assertion parameters from the environment.
 # (string value)
@@ -856,6 +868,13 @@
 # example: trusted_dashboard=http://acme.com/auth/websso
 # trusted_dashboard=http://beta.com/auth/websso (multi valued)
 #trusted_dashboard =
+{%- if server.websso is defined %}
+{%- if server.websso.trusted_dashboard is defined %}
+{%- for dashboard in server.websso.trusted_dashboard %}
+trusted_dashboard = {{ dashboard }}
+{%- endfor %}
+{%- endif %}
+{%- endif %}
 
 # Location of Single Sign-On callback handler, will return a token to a trusted
 # dashboard host. (string value)
diff --git a/keystone/files/mitaka/wsgi-keystone.conf b/keystone/files/mitaka/wsgi-keystone.conf
new file mode 100644
index 0000000..b82c820
--- /dev/null
+++ b/keystone/files/mitaka/wsgi-keystone.conf
@@ -0,0 +1,130 @@
+{%- from "keystone/map.jinja" import server with context %}
+{%- set site = salt['pillar.get']('apache:server:site:'+site_name) %}
+Listen {% if server.bind.address is defined %}{{ server.bind.address }}{% else %}{{ server.bind.public_address }}{% endif %}:5000
+Listen {% if server.bind.address is defined %}{{ server.bind.address }}{% else %}{{ server.bind.public_address }}{% endif %}:35357
+
+<VirtualHost {% if server.bind.address is defined %}{{ server.bind.address }}{% else %}{{ server.bind.public_address }}{% endif %}:5000>
+{%- include "apache/files/_name.conf" %}
+{%- include "apache/files/_ssl.conf" %}
+{%- include "apache/files/_locations.conf" %}
+
+    WSGIDaemonProcess keystone-public processes=5 threads=1 user=keystone group=keystone display-name=%{GROUP}
+    WSGIProcessGroup keystone-public
+    WSGIScriptAlias / /usr/local/bin/keystone-wsgi-public
+    WSGIApplicationGroup %{GLOBAL}
+    WSGIPassAuthorization On
+    LimitRequestBody 114688
+    <IfVersion >= 2.4>
+      ErrorLogFormat "%{cu}t %M"
+    </IfVersion>
+{%- include "apache/files/_log.conf" %}
+
+    <Directory /usr/local/bin>
+        <IfVersion >= 2.4>
+            Require all granted
+        </IfVersion>
+        <IfVersion < 2.4>
+            Order allow,deny
+            Allow from all
+        </IfVersion>
+    </Directory>
+
+    {% if server.websso is defined %}
+    WSGIScriptAliasMatch ^(/v3/OS-FEDERATION/identity_providers/.*?/protocols/.*?/auth)$ /usr/bin/keystone-wsgi-public/$1
+    <Location /Shibboleth.sso>
+      SetHandler shib
+    </Location>
+    <LocationMatch /v3/auth/OS-FEDERATION/identity_providers/.*?/protocols/saml2/websso>
+      ShibRequestSetting requireSession 1
+      AuthType shibboleth
+      ShibExportAssertion Off
+      Require valid-user
+    </LocationMatch>
+    <LocationMatch /v3/auth/OS-FEDERATION/websso/saml2>
+      ShibRequestSetting requireSession 1
+      AuthType shibboleth
+      ShibExportAssertion Off
+      Require valid-user
+    </LocationMatch>
+    <LocationMatch /v3/OS-FEDERATION/identity_providers/.*?/protocols/saml2/auth>
+      ShibRequestSetting requireSession 1
+      AuthType shibboleth
+      ShibExportAssertion Off
+      Require valid-user
+    </LocationMatch>
+    {%- endif %}
+
+</VirtualHost>
+
+<VirtualHost {% if server.bind.address is defined %}{{ server.bind.address }}{% else %}{{ server.bind.public_address }}{% endif %}:35357>
+{%- include "apache/files/_name.conf" %}
+{%- include "apache/files/_ssl.conf" %}
+{%- include "apache/files/_locations.conf" %}
+
+    WSGIDaemonProcess keystone-admin processes=5 threads=1 user=keystone group=keystone display-name=%{GROUP}
+    WSGIProcessGroup keystone-admin
+    WSGIScriptAlias / /usr/local/bin/keystone-wsgi-admin
+    WSGIApplicationGroup %{GLOBAL}
+    WSGIPassAuthorization On
+    LimitRequestBody 114688
+    <IfVersion >= 2.4>
+      ErrorLogFormat "%{cu}t %M"
+    </IfVersion>
+{%- include "apache/files/_log.conf" %}
+
+    <Directory /usr/local/bin>
+        <IfVersion >= 2.4>
+            Require all granted
+        </IfVersion>
+        <IfVersion < 2.4>
+            Order allow,deny
+            Allow from all
+        </IfVersion>
+    </Directory>
+
+    {% if server.websso is defined %}
+    WSGIScriptAliasMatch ^(/v3/OS-FEDERATION/identity_providers/.*?/protocols/.*?/auth)$ /usr/bin/keystone-wsgi-admin/$1
+    <Location /Shibboleth.sso>
+      SetHandler shib
+    </Location>
+    <LocationMatch /v3/auth/OS-FEDERATION/identity_providers/.*?/protocols/saml2/websso>
+      ShibRequestSetting requireSession 1
+      AuthType shibboleth
+      ShibExportAssertion Off
+      Require valid-user
+    </LocationMatch>
+    <LocationMatch /v3/auth/OS-FEDERATION/websso/saml2>
+      ShibRequestSetting requireSession 1
+      AuthType shibboleth
+      ShibExportAssertion Off
+      Require valid-user
+    </LocationMatch>
+    <LocationMatch /v3/OS-FEDERATION/identity_providers/.*?/protocols/saml2/auth>
+      ShibRequestSetting requireSession 1
+      AuthType shibboleth
+      ShibExportAssertion Off
+      Require valid-user
+    </LocationMatch>
+    {%- endif %}
+
+</VirtualHost>
+
+Alias /identity /usr/local/bin/keystone-wsgi-public
+<Location /identity>
+    SetHandler wsgi-script
+    Options +ExecCGI
+
+    WSGIProcessGroup keystone-public
+    WSGIApplicationGroup %{GLOBAL}
+    WSGIPassAuthorization On
+</Location>
+
+Alias /identity_admin /usr/local/bin/keystone-wsgi-admin
+<Location /identity_admin>
+    SetHandler wsgi-script
+    Options +ExecCGI
+
+    WSGIProcessGroup keystone-admin
+    WSGIApplicationGroup %{GLOBAL}
+    WSGIPassAuthorization On
+</Location>
diff --git a/keystone/files/sso_callback_template.html b/keystone/files/sso_callback_template.html
new file mode 100644
index 0000000..c6997dc
--- /dev/null
+++ b/keystone/files/sso_callback_template.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>Keystone WebSSO redirect</title>
+  </head>
+  <body>
+     <form id="sso" name="sso" action="$host" method="post">
+       Please wait...
+       <br/>
+       <input type="hidden" name="token" id="token" value="$token"/>
+       <noscript>
+         <input type="submit" name="submit_no_javascript" id="submit_no_javascript"
+            value="If your JavaScript is disabled, please click to continue"/>
+       </noscript>
+     </form>
+     <script type="text/javascript">
+       window.onload = function() {
+         document.forms['sso'].submit();
+       }
+     </script>
+  </body>
+</html>
\ No newline at end of file
diff --git a/keystone/server.sls b/keystone/server.sls
index 5055d96..cf54fb4 100644
--- a/keystone/server.sls
+++ b/keystone/server.sls
@@ -5,6 +5,23 @@
   pkg.installed:
   - names: {{ server.pkgs }}
 
+{%- if server.service_name in ['apache2', 'httpd'] %}
+include:
+- apache
+
+{%- if grains.os_family == "Debian" %}
+keystone:
+{%- endif %}
+{%- if grains.os_family == "RedHat" %}
+openstack-keystone:
+{%- endif %}
+  service.dead:
+    - enable: False
+    - watch:
+      - pkg: keystone_packages
+
+{%- endif %}
+
 keystone_salt_config:
   file.managed:
     - name: /etc/salt/minion.d/keystone.conf
@@ -42,7 +59,20 @@
   - template: jinja
   - require:
     - pkg: keystone_packages
+  - watch_in:
+    - service: keystone_service
 
+{% if server.websso is defined %}
+
+/etc/keystone/sso_callback_template.html:
+  file.managed:
+  - source: salt://keystone/files/sso_callback_template.html
+  - require:
+    - pkg: keystone_packages
+  - watch_in:
+    - service: keystone_service
+
+{%- endif %}
 
 /etc/keystone/keystone-paste.ini:
   file.managed:
diff --git a/metadata.yml b/metadata.yml
index 1cafe29..1057d20 100644
--- a/metadata.yml
+++ b/metadata.yml
@@ -1,3 +1,6 @@
 name: "keystone"
 version: "2016.4.1"
 source: "https://github.com/openstack/salt-formula-keystone"
+dependencies:
+- name: apache
+  source: "https://github.com/tcpcloud/salt-formula-apache.git"
diff --git a/metadata/service/server/cluster.yml b/metadata/service/server/cluster.yml
index b63419c..5038cf3 100644
--- a/metadata/service/server/cluster.yml
+++ b/metadata/service/server/cluster.yml
@@ -40,7 +40,7 @@
         ha_queues: true
       cache:
         engine: memcached
-        members: 
+        members:
         - host: ${_param:cluster_node01_address}
           port: 11211
         - host: ${_param:cluster_node02_address}
diff --git a/metadata/service/server/single.yml b/metadata/service/server/single.yml
index 3e72a88..5269121 100644
--- a/metadata/service/server/single.yml
+++ b/metadata/service/server/single.yml
@@ -40,6 +40,6 @@
         ha_queues: true
       cache:
         engine: memcached
-        members: 
+        members:
         - host: localhost
-          port: 11211
\ No newline at end of file
+          port: 11211
diff --git a/tests/pillar/cluster.sls b/tests/pillar/cluster.sls
index b8b0aa5..d3538b9 100644
--- a/tests/pillar/cluster.sls
+++ b/tests/pillar/cluster.sls
@@ -37,7 +37,7 @@
       ha_queues: true
     cache:
       engine: memcached
-      members: 
+      members:
       - host: 127.0.0.1
         port: 11211
       - host: 127.0.0.1
diff --git a/tests/pillar/single.sls b/tests/pillar/single.sls
index e81487c..3e7c49d 100644
--- a/tests/pillar/single.sls
+++ b/tests/pillar/single.sls
@@ -36,6 +36,6 @@
       location: /etc/keystone/fernet-keys/
     cache:
       engine: memcached
-      members: 
+      members:
       - host: localhost
-        port: 11211
\ No newline at end of file
+        port: 11211
diff --git a/tests/pillar/single_fernet.sls b/tests/pillar/single_fernet.sls
index e9f90eb..7077876 100644
--- a/tests/pillar/single_fernet.sls
+++ b/tests/pillar/single_fernet.sls
@@ -28,6 +28,6 @@
       max_active_keys: 4
     cache:
       engine: memcached
-      members: 
+      members:
       - host: localhost
         port: 11211