Merge "Remove not used backend files"
diff --git a/.kitchen.yml b/.kitchen.yml
index dad90a5..2e0cae8 100644
--- a/.kitchen.yml
+++ b/.kitchen.yml
@@ -14,6 +14,10 @@
   formula: haproxy
   grains:
     noservices: True
+  dependencies:
+    - name: salt
+      repo: git
+      source: https://gerrit.mcp.mirantis.com/salt-formulas/salt
   state_top:
     base:
       "*":
@@ -29,16 +33,10 @@
   sudo: true
 
 docker_images:
-  - &xenial-20163 <%=ENV['IMAGE_XENIAL_20163'] || 'docker-dev-local.docker.mirantis.net/epcim/salt/saltstack-ubuntu-xenial-salt-2016.3/salt:2018_11_19'%>
   - &xenial-20177 <%=ENV['IMAGE_XENIAL_20177'] || 'docker-dev-local.docker.mirantis.net/epcim/salt/saltstack-ubuntu-xenial-salt-2017.7/salt:2018_11_19'%>
   - &xenial-stable <%=ENV['IMAGE_XENIAL_STABLE'] || 'docker-dev-local.docker.mirantis.net/epcim/salt/saltstack-ubuntu-xenial-salt-stable/salt:2018_11_19'%>
 
 platforms:
-  - name: xenial-2016.3
-    driver_config:
-      image: *xenial-20163
-      platform: ubuntu
-
   - name: xenial-2017.7
     driver_config:
       image: *xenial-20177
diff --git a/README.rst b/README.rst
index 94a1609..6fd2aa2 100644
--- a/README.rst
+++ b/README.rst
@@ -639,6 +639,62 @@
             track: connection
           servers:
             ...
+Implement rate limiting, to prevent excessive requests
+using 'format: listen' and acls/request/backend stick list
+
+.. code-block:: yaml
+
+  haproxy:
+    proxy:
+      listen:
+        nova_metadata_api:
+          options:
+          - httplog
+          rate_limit:
+            enabled: true
+            type: string
+            len: 36
+            size: 10m
+            duration: 60s
+            acls:
+              101:
+                enabled: true
+                value: acl too_many_requests_3 sc0_gpc0_rate() gt 3
+              102:
+                enabled: true
+                value: acl mark_seen sc0_inc_gpc0 gt 0
+              110:
+                enabled: true
+                value: acl x_instance_id hdr(x-instance-id) -i 4777e8e0-16e8-46ce-a3fe-0a1ad9b3ebdc
+              111:
+                enabled: true
+                value: acl x_instance_id hdr(x-instance-id) -i ca2395dd-f73f-4d43-8fe7-f7078a0920af
+              201:
+                enabled: true
+                value: acl too_many_requests_6 sc0_gpc0_rate() gt 6
+              202:
+                enabled: true
+                value: acl mark_seen sc0_inc_gpc0 gt 0
+              210:
+                enabled: true
+                value: acl x_tenant_id hdr(x-tenant-id) -i 2b76cc56a437404bb8cb6cb20dbb0ea4
+            tcp_request:
+              001:
+                enabled: true
+                value: tcp-request inspect-delay 5s
+              101:
+                enabled: true
+                value: tcp-request content track-sc0 hdr(x-instance-id) if ! too_many_requests_3
+              201:
+                enabled: true
+                value: tcp-request content track-sc0 hdr(x-tenant-id) if ! too_many_requests_6
+            use_backend:
+              101:
+                enabled: true
+                value: use_backend nova_metadata_api-rate_limit if mark_seen too_many_requests_3 x_instance_id
+              201:
+                enabled: true
+                value: use_backend nova_metadata_api-rate_limit if mark_seen too_many_requests_6 x_tenant_id
 
 
 Read more
diff --git a/haproxy/files/_rate_limit.cfg b/haproxy/files/_rate_limit.cfg
index 499c6e4..dce4572 100644
--- a/haproxy/files/_rate_limit.cfg
+++ b/haproxy/files/_rate_limit.cfg
@@ -1,17 +1,39 @@
 
-  tcp-request inspect-delay 5s
-  acl too_many_requests sc0_gpc0_rate() gt {{ listen.rate_limit.get('requests', 100) }}
-  acl mark_seen sc0_inc_gpc0 gt 0
+  {%- set _data=listen.get('rate_limit', {}) %}
   {%- set stick_table_found = { 'val': false } %}
   {%- for item in listen.get('sticks', []) if item.startswith('stick-table ') %}
   {%- do stick_table_found.update({'val': true}) %}
   {%- endfor %}
   {%- if not stick_table_found.val %}
-  stick-table type string size {{ listen.rate_limit.get('size', '100k') }} store gpc0_rate({{ listen.rate_limit.get('duration', '60s') }})
+  stick-table type {{ _data.get('type', 'string') }} {%- if _data.len is defined and _data.type in ['string', 'binary'] %} len {{ _data.len }}{%- endif %} size {{ _data.get('size', '100k') }} store gpc0_rate({{ _data.get('duration', '60s') }})
   {%- endif %}
-  {%- if listen.rate_limit.get('track', 'content') == 'content' %}
-  tcp-request content track-sc0 {{ listen.rate_limit.get('header', 'hdr(X-Forwarded-For)') }} if ! too_many_requests
+  {%- if _data.acls is defined and _data.tcp_request is defined and _data.use_backend is defined %}
+    {%- set acl_filters_dict_inted = salt['sharedlib.call']('misc.cast_dict_keys_to_int', _data.acls ) %}
+    {%- for id, _acl in acl_filters_dict_inted|dictsort -%}
+      {%- if _acl.get('enabled', False) %}
+  {{ _acl.value }}
+      {%- endif %}
+    {%- endfor %}
+    {%- set tcp_request_filters_dict_inted = salt['sharedlib.call']('misc.cast_dict_keys_to_int', _data.tcp_request ) %}
+    {%- for id, _request in tcp_request_filters_dict_inted|dictsort -%}
+      {%- if _request.get('enabled', False) %}
+  {{ _request.value }}
+      {%- endif %}
+    {%- endfor %}
+    {%- set use_backend_filters_dict_inted = salt['sharedlib.call']('misc.cast_dict_keys_to_int', _data.use_backend ) %}
+    {%- for id, _backend in use_backend_filters_dict_inted|dictsort -%}
+      {%- if _backend.get('enabled', False) %}
+  {{ _backend.value }}
+      {%- endif %}
+    {%- endfor %}
   {%- else %}
-  tcp-request connection track-sc0 {{ listen.rate_limit.get('tracking_key', 'src') }} if ! too_many_requests
+  tcp-request inspect-delay 5s
+  acl too_many_requests sc0_gpc0_rate() gt {{ _data.get('requests', 100) }}
+  acl mark_seen sc0_inc_gpc0 gt 0
+  {%- if _data.get('track', 'content') == 'content' %}
+  tcp-request content track-sc0 {{ _data.get('header', 'hdr(X-Forwarded-For)') }} if ! too_many_requests
+  {%- else %}
+  tcp-request connection track-sc0 {{ _data.get('tracking_key', 'src') }} {%- if stick_table_found.val %} table {{ listen_name }}-rate_limit {%- endif %} if ! too_many_requests
   {%- endif %}
   use_backend {{ listen_name }}-rate_limit if mark_seen too_many_requests
+  {%- endif %}
diff --git a/haproxy/files/haproxy.cfg b/haproxy/files/haproxy.cfg
index 7dd405e..f0896a3 100644
--- a/haproxy/files/haproxy.cfg
+++ b/haproxy/files/haproxy.cfg
@@ -266,6 +266,13 @@
   {%- endfor %}
   {%- if listen.rate_limit is defined and listen.rate_limit.get('enabled', False) %}
 backend {{ listen_name }}-rate_limit
+  {%- set stick_table_found = { 'val': false } %}
+  {%- for item in listen.get('sticks', []) if item.startswith('stick-table ') %}
+  {%- do stick_table_found.update({'val': true}) %}
+  {%- endfor %}
+  {%- if stick_table_found.val %}
+  stick-table type {{ listen.rate_limit.get('type', 'string') }} {%- if listen.rate_limit.len is defined and listen.rate_limit.type in ['string', 'binary'] %} len {{ listen.rate_limit.len }}{%- endif %} size {{ listen.rate_limit.get('size', '100k') }} store gpc0_rate({{ listen.rate_limit.get('duration', '60s') }})
+  {%- endif %}
   timeout tarpit {{ listen.rate_limit.get('tarpit_timeout', '2s') }}
   errorfile 500 /etc/haproxy/errors/429.http11
   http-request tarpit
diff --git a/tests/pillar/single_rate_limiting.sls b/tests/pillar/single_rate_limiting.sls
index 921bc0d..0b6ae8b 100644
--- a/tests/pillar/single_rate_limiting.sls
+++ b/tests/pillar/single_rate_limiting.sls
@@ -61,6 +61,73 @@
           params: check inter 10s fastinter 2s downinter 3s rise 3 fall 3
           port: 8776
         type: http
+      nova_metadata_api2:
+        binds:
+        - address: 127.0.0.1
+          port: 8777
+        format: listen
+        options:
+        - httplog
+        rate_limit:
+          enabled: true
+          type: string
+          len: 36
+          size: 12m
+          duration: 10
+          acls:
+            101:
+              enabled: true
+              value: acl too_many_requests_3 sc0_gpc0_rate() gt 3
+            102:
+              enabled: true
+              value: acl mark_seen sc0_inc_gpc0 gt 0
+            110:
+              enabled: true
+              value: acl x_instance_id hdr(x-instance-id) -i 4777e8e0-16e8-46ce-a3fe-0a1ad9b3ebdc
+            111:
+              enabled: true
+              value: acl x_instance_id hdr(x-instance-id) -i ca2395dd-f73f-4d43-8fe7-f7078a0920af
+            201:
+              enabled: true
+              value: acl too_many_requests_6 sc0_gpc0_rate() gt 6
+            202:
+              enabled: true
+              value: acl mark_seen sc0_inc_gpc0 gt 0
+            210:
+              enabled: true
+              value: acl x_tenant_id hdr(x-tenant-id) -i 2b76cc56a437404bb8cb6cb20dbb0ea4
+          tcp_request:
+            001:
+              enabled: true
+              value: tcp-request inspect-delay 5s
+            101:
+              enabled: true
+              value: tcp-request content track-sc0 hdr(x-instance-id) if ! too_many_requests_3
+            201:
+              enabled: true
+              value: tcp-request content track-sc0 hdr(x-tenant-id) if ! too_many_requests_6
+          use_backend:
+            101:
+              enabled: true
+              value: use_backend nova_metadata_api2-rate_limit if mark_seen too_many_requests_3 x_instance_id
+            201:
+              enabled: true
+              value: use_backend nova_metadata_api2-rate_limit if mark_seen too_many_requests_6 x_tenant_id
+        servers:
+        - host: 127.0.0.1
+          name: ctl01
+          params: check inter 10s fastinter 2s downinter 3s rise 3 fall 3
+          port: 8777
+        - host: 127.0.0.1
+          name: ctl02
+          params: check inter 10s fastinter 2s downinter 3s rise 3 fall 3
+          port: 8777
+        - host: 127.0.0.1
+          name: ctl03
+          params: check inter 10s fastinter 2s downinter 3s rise 3 fall 3
+          port: 8777
+        type: http
+
 # For haproxy/meta/sensu.yml
 linux:
   network: