Compare commits
2 Commits
3cd66c54a7
...
83569c59ee
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83569c59ee | ||
|
|
6c340c5111 |
@@ -23,3 +23,6 @@ vhost_redirect_preserve_path: false
|
|||||||
vhost_redirect_preserve_query: "{{ vhost_redirect_preserve_path }}"
|
vhost_redirect_preserve_query: "{{ vhost_redirect_preserve_path }}"
|
||||||
|
|
||||||
vhost_respond_content_type: plain
|
vhost_respond_content_type: plain
|
||||||
|
vhost_respond_status: 200
|
||||||
|
|
||||||
|
vhost_matchers: []
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ argument_specs:
|
|||||||
default: []
|
default: []
|
||||||
vhost_proxy_pass_host_header:
|
vhost_proxy_pass_host_header:
|
||||||
description: Whether to pass the host header unchanged (true) or change it to the proxy target host (false)
|
description: Whether to pass the host header unchanged (true) or change it to the proxy target host (false)
|
||||||
trpe: bool
|
type: bool
|
||||||
required: false
|
required: false
|
||||||
default: true
|
default: true
|
||||||
|
|
||||||
@@ -155,6 +155,178 @@ argument_specs:
|
|||||||
choices:
|
choices:
|
||||||
- plain
|
- plain
|
||||||
- json
|
- json
|
||||||
|
vhost_respond_status:
|
||||||
|
description: Status code of response
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
default: 200
|
||||||
|
|
||||||
|
vhost_matchers:
|
||||||
|
description: >
|
||||||
|
List of matchers to handle differently from the default for vhost.
|
||||||
|
A matcher matches if all of its conditions match
|
||||||
|
type: list
|
||||||
|
elements: dict
|
||||||
|
required: false
|
||||||
|
default: []
|
||||||
|
options:
|
||||||
|
name:
|
||||||
|
description: Name of the matcher used to reference it
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
|
||||||
|
match_methods:
|
||||||
|
description: HTTP methods to match against. Matching one method is enough.
|
||||||
|
type: list
|
||||||
|
elements: str
|
||||||
|
choices:
|
||||||
|
- GET
|
||||||
|
- HEAD
|
||||||
|
- OPTIONS
|
||||||
|
- TRACE
|
||||||
|
- PUT
|
||||||
|
- DELETE
|
||||||
|
- POST
|
||||||
|
- PATCH
|
||||||
|
- CONNECT
|
||||||
|
required: false
|
||||||
|
default: []
|
||||||
|
match_headers:
|
||||||
|
description: >-
|
||||||
|
Headers to match against.
|
||||||
|
If the value begins with ^ and end with $, the value is matched as regex.
|
||||||
|
type: dict
|
||||||
|
required: false
|
||||||
|
default: {}
|
||||||
|
|
||||||
|
type:
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_type }}"
|
||||||
|
choices:
|
||||||
|
- reverse_proxy
|
||||||
|
- redirect
|
||||||
|
- respond
|
||||||
|
headers:
|
||||||
|
description: Dict of response headers and their values
|
||||||
|
type: dict
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_headers }}"
|
||||||
|
delete_headers:
|
||||||
|
description: List of response headers to delete
|
||||||
|
type: list
|
||||||
|
elements: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_delete_headers }}"
|
||||||
|
|
||||||
|
basicauth:
|
||||||
|
description: Whether to require basic auth for the location
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_basicauth }}"
|
||||||
|
basicauth_users:
|
||||||
|
description: A dict of basic auth users and their password hashes. Required if basicauth is true
|
||||||
|
type: dict
|
||||||
|
default: "{{ vhost_basicauth_users }}"
|
||||||
|
|
||||||
|
proxy_target_netproto:
|
||||||
|
description:
|
||||||
|
- Network protocol to use for proxy requests.
|
||||||
|
- Only applicable if type is reverse_proxy.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_target_netproto }}"
|
||||||
|
choices:
|
||||||
|
- tcp
|
||||||
|
- unix
|
||||||
|
proxy_target_protocol:
|
||||||
|
description:
|
||||||
|
- Transport protocol (scheme) to use for proxy requests.
|
||||||
|
- Only applicable if type is reverse_proxy.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_target_protocol }}"
|
||||||
|
choices:
|
||||||
|
- http
|
||||||
|
- https
|
||||||
|
proxy_target_host:
|
||||||
|
description: Host where to proxy requests to. Only applicable if type is reverse_proxy
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_target_host }}"
|
||||||
|
proxy_target_port:
|
||||||
|
description: Port where to proxy requests to. Only applicable if type is reverse_proxy.
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_target_port if vhost_type == 'reverse_proxy' and vhost_proxy_target_netproto == 'tcp' else 0 }}"
|
||||||
|
proxy_target_socket:
|
||||||
|
description:
|
||||||
|
- Unix socket path to proxy requests to.
|
||||||
|
- Only applicable if type is reverse_proxy and proxy_target_netproto is unix.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_target_socket if vhost_type == 'reverse_proxy' and vhost_proxy_target_netproto == 'unix' else '' }}"
|
||||||
|
proxy_headers:
|
||||||
|
description: Dict of request headers and their values to set for proxied requests
|
||||||
|
type: dict
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_headers }}"
|
||||||
|
proxy_delete_headers:
|
||||||
|
description: List of request headers to delete from proxied requests
|
||||||
|
type: list
|
||||||
|
elements: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_delete_headers }}"
|
||||||
|
proxy_pass_host_header:
|
||||||
|
description: Whether to pass the host header unchanged (true) or change it to the proxy target host (false)
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_proxy_pass_host_header }}"
|
||||||
|
|
||||||
|
redirect_target:
|
||||||
|
description: "Only applicable if vhost_type is redirect. Example: https://www.domain.tld/location"
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_redirect_target if vhost_type == 'redirect' else '' }}"
|
||||||
|
redirect_preserve_path:
|
||||||
|
description: Whether to keep the original request path
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_redirect_preserve_path }}"
|
||||||
|
redirect_preserve_query:
|
||||||
|
description: Whether to keep the original request query string
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_redirect_preserve_query }}"
|
||||||
|
redirect_type:
|
||||||
|
description: Only applicable if vhost_type is redirect
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_redirect_type }}"
|
||||||
|
choices:
|
||||||
|
- temporary
|
||||||
|
- permanent
|
||||||
|
|
||||||
|
respond_content:
|
||||||
|
description: >-
|
||||||
|
Content to respond with.
|
||||||
|
Json content can be set as yaml as long as respond_content_type is set to json.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_respond_content if vhost_type == 'respond' else '' }}"
|
||||||
|
respond_content_type:
|
||||||
|
description: Type of the respond content
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_respond_content_type }}"
|
||||||
|
choices:
|
||||||
|
- plain
|
||||||
|
- json
|
||||||
|
respond_status:
|
||||||
|
description: Status code of response
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_respond_status }}"
|
||||||
|
|
||||||
vhost_locations:
|
vhost_locations:
|
||||||
description: List of locations to handle differently from the default for vhost
|
description: List of locations to handle differently from the default for vhost
|
||||||
@@ -247,7 +419,7 @@ argument_specs:
|
|||||||
default: "{{ vhost_proxy_delete_headers }}"
|
default: "{{ vhost_proxy_delete_headers }}"
|
||||||
proxy_pass_host_header:
|
proxy_pass_host_header:
|
||||||
description: Whether to pass the host header unchanged (true) or change it to the proxy target host (false)
|
description: Whether to pass the host header unchanged (true) or change it to the proxy target host (false)
|
||||||
trpe: bool
|
type: bool
|
||||||
required: false
|
required: false
|
||||||
default: "{{ vhost_proxy_pass_host_header }}"
|
default: "{{ vhost_proxy_pass_host_header }}"
|
||||||
|
|
||||||
@@ -276,7 +448,9 @@ argument_specs:
|
|||||||
- permanent
|
- permanent
|
||||||
|
|
||||||
respond_content:
|
respond_content:
|
||||||
description: Content to respond with. Json content can be set as yaml as long as respond_content_type is set to json
|
description: >-
|
||||||
|
Content to respond with.
|
||||||
|
Json content can be set as yaml as long as respond_content_type is set to json.
|
||||||
type: str
|
type: str
|
||||||
required: false
|
required: false
|
||||||
default: "{{ vhost_respond_content if vhost_type == 'respond' else '' }}"
|
default: "{{ vhost_respond_content if vhost_type == 'respond' else '' }}"
|
||||||
@@ -288,3 +462,157 @@ argument_specs:
|
|||||||
choices:
|
choices:
|
||||||
- plain
|
- plain
|
||||||
- json
|
- json
|
||||||
|
respond_status:
|
||||||
|
description: Status code of response
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_respond_status }}"
|
||||||
|
|
||||||
|
matchers:
|
||||||
|
description: >
|
||||||
|
List of matchers to handle differently from the default for vhost.
|
||||||
|
A matcher matches if all of its conditions match.
|
||||||
|
Options without a specified default will default to location's corresponding option.
|
||||||
|
type: list
|
||||||
|
elements: dict
|
||||||
|
required: false
|
||||||
|
default: "{{ vhost_matchers }}"
|
||||||
|
options:
|
||||||
|
name:
|
||||||
|
description: Name of the matcher used to reference it
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
|
||||||
|
match_methods:
|
||||||
|
description: HTTP methods to match against. Matching one method is enough.
|
||||||
|
type: list
|
||||||
|
elements: str
|
||||||
|
choices:
|
||||||
|
- GET
|
||||||
|
- HEAD
|
||||||
|
- OPTIONS
|
||||||
|
- TRACE
|
||||||
|
- PUT
|
||||||
|
- DELETE
|
||||||
|
- POST
|
||||||
|
- PATCH
|
||||||
|
- CONNECT
|
||||||
|
required: false
|
||||||
|
default: []
|
||||||
|
match_headers:
|
||||||
|
description: >-
|
||||||
|
Headers to match against.
|
||||||
|
The value is matched as regex.
|
||||||
|
^ and $ are implied, so don't add them yourself.
|
||||||
|
type: dict
|
||||||
|
required: false
|
||||||
|
default: {}
|
||||||
|
|
||||||
|
type:
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
choices:
|
||||||
|
- reverse_proxy
|
||||||
|
- redirect
|
||||||
|
- respond
|
||||||
|
headers:
|
||||||
|
description: Dict of response headers and their values
|
||||||
|
type: dict
|
||||||
|
required: false
|
||||||
|
delete_headers:
|
||||||
|
description: List of response headers to delete
|
||||||
|
type: list
|
||||||
|
elements: str
|
||||||
|
required: false
|
||||||
|
|
||||||
|
basicauth:
|
||||||
|
description: Whether to require basic auth for the location
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
basicauth_users:
|
||||||
|
description: A dict of basic auth users and their password hashes. Required if basicauth is true
|
||||||
|
type: dict
|
||||||
|
|
||||||
|
proxy_target_netproto:
|
||||||
|
description:
|
||||||
|
- Network protocol to use for proxy requests.
|
||||||
|
- Only applicable if type is reverse_proxy.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
choices:
|
||||||
|
- tcp
|
||||||
|
- unix
|
||||||
|
proxy_target_protocol:
|
||||||
|
description:
|
||||||
|
- Transport protocol (scheme) to use for proxy requests.
|
||||||
|
- Only applicable if type is reverse_proxy.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
choices:
|
||||||
|
- http
|
||||||
|
- https
|
||||||
|
proxy_target_host:
|
||||||
|
description: Host where to proxy requests to. Only applicable if type is reverse_proxy
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
proxy_target_port:
|
||||||
|
description: Port where to proxy requests to. Only applicable if type is reverse_proxy.
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
proxy_target_socket:
|
||||||
|
description:
|
||||||
|
- Unix socket path to proxy requests to.
|
||||||
|
- Only applicable if type is reverse_proxy and proxy_target_netproto is unix.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
proxy_headers:
|
||||||
|
description: Dict of request headers and their values to set for proxied requests
|
||||||
|
type: dict
|
||||||
|
required: false
|
||||||
|
proxy_delete_headers:
|
||||||
|
description: List of request headers to delete from proxied requests
|
||||||
|
type: list
|
||||||
|
elements: str
|
||||||
|
required: false
|
||||||
|
proxy_pass_host_header:
|
||||||
|
description: Whether to pass the host header unchanged (true) or change it to the proxy target host (false)
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
|
||||||
|
redirect_target:
|
||||||
|
description: "Only applicable if vhost_type is redirect. Example: https://www.domain.tld/location"
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
redirect_preserve_path:
|
||||||
|
description: Whether to keep the original request path
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
redirect_preserve_query:
|
||||||
|
description: Whether to keep the original request query string
|
||||||
|
type: bool
|
||||||
|
required: false
|
||||||
|
redirect_type:
|
||||||
|
description: Only applicable if vhost_type is redirect
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
choices:
|
||||||
|
- temporary
|
||||||
|
- permanent
|
||||||
|
|
||||||
|
respond_content:
|
||||||
|
description: >-
|
||||||
|
Content to respond with.
|
||||||
|
Json content can be set as yaml as long as respond_content_type is set to json.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
respond_content_type:
|
||||||
|
description: Type of the respond content
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
choices:
|
||||||
|
- plain
|
||||||
|
- json
|
||||||
|
respond_status:
|
||||||
|
description: Status code of response
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
|||||||
@@ -2,53 +2,67 @@
|
|||||||
{{ vhost_domains | join(' ') }} {
|
{{ vhost_domains | join(' ') }} {
|
||||||
{% for location in _vhost_locations_complete %}
|
{% for location in _vhost_locations_complete %}
|
||||||
handle {{ location.path }} {
|
handle {{ location.path }} {
|
||||||
{% for header in location.delete_headers %}
|
{% for matcher in location.matchers %}
|
||||||
header -{{ header }}
|
{% if matcher.name != '' %}
|
||||||
{% endfor %}
|
@{{ matcher.name }} {
|
||||||
{% for header in location.headers | dict2items %}
|
{% if matcher.match_methods | length > 0 %}
|
||||||
header {{ header.key }} `{{ header.value }}`
|
method {{ matcher.match_methods | join(' ') }}
|
||||||
{% endfor %}
|
{% endif %}
|
||||||
{% if location.basicauth %}
|
{% for header in matcher.match_headers | dict2items %}
|
||||||
basicauth {
|
header{{ '_regexp' if header.value.startswith('^') and header.value.endswith('$') else '' }} {{ header.key }} {{ header.value }}
|
||||||
{% for user in location.basicauth_users | dict2items %}
|
|
||||||
{{ user.key }} {{ user.value }}
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
}
|
}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if location.type == 'reverse_proxy' %}
|
handle{{ ' @' ~ matcher.name if matcher.name != '' else '' }} {
|
||||||
reverse_proxy {
|
{% for header in matcher.delete_headers %}
|
||||||
{% if location.proxy_target_netproto == 'tcp' %}
|
header -{{ header }}
|
||||||
to tcp/{{ location.proxy_target_host }}:{{ location.proxy_target_port }}
|
{% endfor %}
|
||||||
{% else %}
|
{% for header in matcher.headers | dict2items %}
|
||||||
to unix/{{ location.proxy_target_socket }}
|
header {{ header.key }} `{{ header.value }}`
|
||||||
{% endif %}
|
{% endfor %}
|
||||||
{% if location.proxy_target_protocol == 'https' %}
|
{% if matcher.basicauth %}
|
||||||
transport http {
|
basicauth {
|
||||||
tls
|
{% for user in matcher.basicauth_users | dict2items %}
|
||||||
{% if location.proxy_target_host == 'localhost' %}
|
{{ user.key }} {{ user.value }}
|
||||||
tls_insecure_skip_verify
|
{% endfor %}
|
||||||
{% endif %}
|
|
||||||
}
|
}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for header in location.proxy_delete_headers %}
|
{% if matcher.type == 'reverse_proxy' %}
|
||||||
header_up -{{ header }}
|
reverse_proxy {
|
||||||
{% endfor %}
|
{% if matcher.proxy_target_netproto == 'tcp' %}
|
||||||
{% for header in location.proxy_headers | dict2items %}
|
to tcp/{{ matcher.proxy_target_host }}:{{ matcher.proxy_target_port }}
|
||||||
header_up {{ header.key }} `{{ header.value }}`
|
{% else %}
|
||||||
{% endfor %}
|
to unix/{{ matcher.proxy_target_socket }}
|
||||||
{% if (not location.proxy_pass_host_header) and ('host' not in location.proxy_headers | map('lower')) %}
|
{% endif %}
|
||||||
header_up Host {upstream_hostport}
|
{% if matcher.proxy_target_protocol == 'https' %}
|
||||||
|
transport http {
|
||||||
|
tls
|
||||||
|
{% if matcher.proxy_target_host == 'localhost' %}
|
||||||
|
tls_insecure_skip_verify
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% for header in matcher.proxy_delete_headers %}
|
||||||
|
header_up -{{ header }}
|
||||||
|
{% endfor %}
|
||||||
|
{% for header in matcher.proxy_headers | dict2items %}
|
||||||
|
header_up {{ header.key }} `{{ header.value }}`
|
||||||
|
{% endfor %}
|
||||||
|
{% if (not matcher.proxy_pass_host_header) and ('host' not in matcher.proxy_headers | map('lower')) %}
|
||||||
|
header_up Host {upstream_hostport}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{% elif matcher.type == 'redirect' %}
|
||||||
|
redir * {{ matcher.redirect_target }}{{ '{path}' if matcher.redirect_preserve_path }}{{ '?{query}' if matcher.redirect_preserve_query }} {{ matcher.redirect_type }}
|
||||||
|
{% elif matcher.type == 'respond' %}
|
||||||
|
{% if matcher.respond_content_type == 'json' %}
|
||||||
|
respond `{{ matcher.respond_content | to_json }}`
|
||||||
|
{% else %}
|
||||||
|
respond `{{ matcher.respond_content }}` {{ matcher.respond_status }}
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
}
|
}
|
||||||
{% elif location.type == 'redirect' %}
|
{% endfor %}
|
||||||
redir * {{ location.redirect_target }}{{ '{path}' if location.redirect_preserve_path }}{{ '?{query}' if location.redirect_preserve_query }} {{ location.redirect_type }}
|
|
||||||
{% elif location.type == 'respond' %}
|
|
||||||
{% if location.respond_content_type == 'json' %}
|
|
||||||
respond `{{ location.respond_content | to_json }}`
|
|
||||||
{% else %}
|
|
||||||
respond `{{ location.respond_content }}`
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
}
|
}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,15 @@
|
|||||||
---
|
---
|
||||||
|
_vhost_matcher_defaults:
|
||||||
|
match_headers: {}
|
||||||
|
match_method: []
|
||||||
|
_vhost_matchers: >-
|
||||||
|
{{
|
||||||
|
vhost_matchers
|
||||||
|
| map('combine', _vhost_matcher_defaults)
|
||||||
|
| zip(vhost_matchers)
|
||||||
|
| map('combine')
|
||||||
|
}}
|
||||||
|
|
||||||
_vhost_location_defaults:
|
_vhost_location_defaults:
|
||||||
type: "{{ vhost_type }}"
|
type: "{{ vhost_type }}"
|
||||||
headers: "{{ vhost_headers }}"
|
headers: "{{ vhost_headers }}"
|
||||||
@@ -25,12 +36,36 @@ _vhost_location_defaults:
|
|||||||
|
|
||||||
respond_content: "{{ vhost_respond_content if vhost_type == 'respond' else '' }}"
|
respond_content: "{{ vhost_respond_content if vhost_type == 'respond' else '' }}"
|
||||||
respond_content_type: "{{ vhost_respond_content_type }}"
|
respond_content_type: "{{ vhost_respond_content_type }}"
|
||||||
|
respond_status: "{{ vhost_respond_status }}"
|
||||||
|
|
||||||
|
matchers: "{{ _vhost_matchers }}"
|
||||||
|
|
||||||
_vhost_locations: "{{ vhost_locations + [{'path': ''}] }}"
|
_vhost_locations: "{{ vhost_locations + [{'path': ''}] }}"
|
||||||
|
|
||||||
_vhost_locations_complete: "{{
|
_vhost_locations_withdefaults: >-
|
||||||
_vhost_locations
|
{{
|
||||||
| map('combine', _vhost_location_defaults)
|
_vhost_locations
|
||||||
| zip(_vhost_locations)
|
| map('combine', _vhost_location_defaults)
|
||||||
| map('combine')
|
| zip(
|
||||||
}}"
|
_vhost_locations
|
||||||
|
)
|
||||||
|
| map('combine')
|
||||||
|
| map('combine', {'matchers': [{'name': ''}]}, list_merge='append')
|
||||||
|
}}
|
||||||
|
|
||||||
|
_vhost_locations_complete: >-
|
||||||
|
{{
|
||||||
|
_vhost_locations_withdefaults |
|
||||||
|
sort(attribute='path') |
|
||||||
|
zip(
|
||||||
|
_vhost_locations_withdefaults |
|
||||||
|
subelements('matchers') |
|
||||||
|
map('combine') |
|
||||||
|
groupby('path') |
|
||||||
|
map('last') |
|
||||||
|
map('community.general.remove_keys', ['matchers', 'path']) |
|
||||||
|
map('community.general.dict_kv', 'matchers')
|
||||||
|
) |
|
||||||
|
map('combine') |
|
||||||
|
reverse
|
||||||
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user