From 0014a80fb1f3618ef7e724a1d2b719ae3a1fc945 Mon Sep 17 00:00:00 2001 From: Feng Honglin Date: Wed, 15 Feb 2017 15:31:16 -0800 Subject: [PATCH] v1.6.3 (#166) * Adds option for turning off basic auth for selected services (#154) * Allow a service to be configured as failover for another service in the same backend (#120) * Added support for hot-failover mode * Updated README * Add ability to pass hashed password credentials to HAproxy. (#107) * Remove DHE-RSA-AES128-SHA from SSL_BIND_CIPHERS (#103) * fixed tests error by PRs * do not return haproxy ingress network (#160) * fix the error introduced by EXCLUDE_BASIC_AUTH * bump version --- Dockerfile | 2 +- Dockerfile-dev | 44 ++++++++++++++++++ README.md | 9 ++-- haproxy/__init__.py | 2 +- haproxy/config.py | 1 + haproxy/haproxycfg.py | 22 +++++---- haproxy/helper/backend_helper.py | 15 ++++--- haproxy/helper/swarm_mode_link_helper.py | 5 ++- haproxy/parser/base_parser.py | 8 ++++ tests/test_legacy_links.sh | 9 ++++ tests/unit/helper/test_backend_helper.py | 45 ++++++++++++++----- .../helper/test_swarm_mode_link_helper.py | 7 +-- tests/unit/parser/test_legacy_parser.py | 18 +++++--- tests/unit/parser/test_new_parser.py | 12 +++-- tests/unit/test_haproxycfg.py | 13 ++++-- 15 files changed, 162 insertions(+), 50 deletions(-) create mode 100644 Dockerfile-dev diff --git a/Dockerfile b/Dockerfile index 0266986..c85f368 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,7 +21,7 @@ ENV RSYSLOG_DESTINATION=127.0.0.1 \ STATS_PORT=1936 \ STATS_AUTH="stats:stats" \ SSL_BIND_OPTIONS=no-sslv3 \ - SSL_BIND_CIPHERS="ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA:DES-CBC3-SHA" \ + SSL_BIND_CIPHERS="ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA:DES-CBC3-SHA" \ HEALTH_CHECK="check inter 2000 rise 2 fall 3" \ NBPROC=1 diff --git a/Dockerfile-dev b/Dockerfile-dev new file mode 100644 index 0000000..808675c --- /dev/null +++ b/Dockerfile-dev @@ -0,0 +1,44 @@ +FROM alpine:3.4 + +RUN echo PyYAML==3.11 > requirements.txt && \ + echo cached-property==1.2.0 >> requirements.txt && \ + echo docker-py==1.10.3 >> requirements.txt && \ + echo dockerpty==0.4.1 >> requirements.txt && \ + echo docopt==0.6.1 >> requirements.txt && \ + venum34==1.0.4 >> requirements.txt && \ + echo jsonschema==2.5.1 >> requirements.txt && \ + vtexttable==0.8.4 >> requirements.txt && \ + echo future==0.15.0 >> requirements.txt && \ + echo requests==2.7.0 >> requirements.txt && \ + echo six==1.9.0 >> requirements.txt && \ + echo websocket-client==0.37.0 >> requirements.txt && \ + echo docker-compose==1.6.0 >> requirements.txt && \ + echo python-dockercloud==1.0.5 >> requirements.txt && \ + echo gevent==1.1.1 >> requirements.txt + +RUN apk update && \ + apk --no-cache add tini haproxy py-pip build-base python-dev ca-certificates && \ + pip install -r requirements.txt + +ENV RSYSLOG_DESTINATION=127.0.0.1 \ + MODE=http \ + BALANCE=roundrobin \ + MAXCONN=4096 \ + OPTION="redispatch, httplog, dontlognull, forwardfor" \ + TIMEOUT="connect 5000, client 50000, server 50000" \ + STATS_PORT=1936 \ + STATS_AUTH="stats:stats" \ + SSL_BIND_OPTIONS=no-sslv3 \ + SSL_BIND_CIPHERS="ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA:DES-CBC3-SHA" \ + HEALTH_CHECK="check inter 2000 rise 2 fall 3" \ + NBPROC=1 + +COPY . /haproxy-src +RUN cp /haproxy-src/reload.sh /reload.sh && \ + cd /haproxy-src && \ + pip install . + + +EXPOSE 80 443 1936 +ENTRYPOINT ["/sbin/tini", "--"] +CMD ["dockercloud-haproxy"] diff --git a/README.md b/README.md index 7a9c2a4..101df08 100644 --- a/README.md +++ b/README.md @@ -156,8 +156,8 @@ Similar to using legacy links, here list some differences that you need to notic Once the stack is up, you can scale the web service using `docker-compose scale web=3`. dockercloud/haproxy will automatically reload its configuration. #### Running with Docker Compose v2 and Swarm (using envvar) -When using links like previous section, the Docker Swarm scheduler can be too restrictive. -Even with overlay network, swarm (As of 1.1.0) will attempt to schedule haproxy on the same node as the linked service due to legacy links behavior. +When using links like previous section, the Docker Swarm scheduler can be too restrictive. +Even with overlay network, swarm (As of 1.1.0) will attempt to schedule haproxy on the same node as the linked service due to legacy links behavior. This can cause unwanted scheduling patterns or errors such as "Unable to find a node fulfilling all dependencies..." Since Compose V2 allows discovery through the service names, Dockercloud haproxy only needs the links to indentify which service should be load balanced. @@ -214,6 +214,7 @@ Settings in this part is immutable, you have to redeploy HAProxy service to make |FORCE_DEFAULT_BACKEND| True | set the default_service as a default backend. This is useful when you have more than one backend and you don't want your default_service as a default backend |HEALTH_CHECK|check|set health check on each backend route, possible value: "check inter 2000 rise 2 fall 3". See:[HAProxy:check](https://cbonte.github.io/haproxy-dconv/configuration-1.5.html#5.2-check)| |HTTP_BASIC_AUTH| |a comma-separated list of credentials(`:`) for HTTP basic auth, which applies to all the backend routes. To escape comma, use `\,`. *Attention:* DO NOT rely on this for authentication in production| +|HTTP_BASIC_AUTH_SECURE| |a comma-separated list of credentials(`:`) for HTTP basic auth, which applies to all the backend routes. To escape comma, use `\,`. See:[HAProxy:user](https://cbonte.github.io/haproxy-dconv/1.5/configuration.html#3.4-user) *Attention:* DO NOT rely on this for authentication in production| |MAXCONN|4096|sets the maximum per-process number of concurrent connections.| |MODE|http|mode of load balancing for HAProxy. Possible values include: `http`, `tcp`, `health`| |MONITOR_PORT| |the port number where monitor_uri should be added to. Use together with `MONTIOR_URI`. Possible value: `80`| @@ -238,9 +239,11 @@ Settings here can overwrite the settings in HAProxy, which are only applied to t |BALANCE|load balancing algorithm to use. Possible values include: `roundrobin`, `static-rr`, `source`, `leastconn`. See:[HAProxy:balance](https://cbonte.github.io/haproxy-dconv/configuration-1.5.html#4-balance)| |COOKIE|sticky session option. Possible value `SRV insert indirect nocache`. See:[HAProxy:cookie](http://cbonte.github.io/haproxy-dconv/configuration-1.5.html#4-cookie)| |DEFAULT_SSL_CERT|similar to SSL_CERT, but stores the pem file at `/certs/cert0.pem` as the default ssl certs. If multiple `DEFAULT_SSL_CERT` are specified in linked services and HAProxy, the behavior is undefined| -|EXCLUDE_PORTS|comma separated port numbers(e.g. 3306, 3307). By default, HAProxy will add all the ports exposed by the application services to the backend routes. You can exclude the ports that you don't want to be routed, like database port| +|EXCLUDE_BASIC_AUTH|if set, the application by the application services to the backend routes. You can exclude the ports that you don't want to be routed, like database port| +|EXCLUDE_PORTS|if set(any value) and `HTTP_BASIC_AUTH` global setting is set, no basic auth will be applied to this service.| |EXTRA_ROUTE_SETTINGS|a string which is append to the each backend route after the health check,possible value: "send-proxy"| |EXTRA_SETTINGS|comma-separated string of extra settings, and each part will be appended to either related backend section or listen session in the configuration file. To escape comma, use `\,`. Possible value: `balance source`| +|FAILOVER|if set(any value), it configures this service to be run as HAProxy `backup` for other configured service(s) in this backend| |FORCE_SSL|if set(any value) together with ssl termination enabled. HAProxy will redirect HTTP request to HTTPS request. |GZIP_COMPRESSION_TYPE|enable gzip compression. The value of this envvar is a list of MIME types that will be compressed. Some possible values: `text/html text/plain text/css application/javascript`. See:[HAProxy:compression](http://cbonte.github.io/haproxy-dconv/configuration-1.5.html#4-compression)| |HEALTH_CHECK|set health check on each backend route, possible value: "check inter 2000 rise 2 fall 3". See:[HAProxy:check](https://cbonte.github.io/haproxy-dconv/configuration-1.5.html#5.2-check)| diff --git a/haproxy/__init__.py b/haproxy/__init__.py index 51bbb3f..31e744e 100644 --- a/haproxy/__init__.py +++ b/haproxy/__init__.py @@ -1 +1 @@ -__version__ = "1.6.2" +__version__ = "1.6.3" diff --git a/haproxy/config.py b/haproxy/config.py index d8efc79..748e1ae 100644 --- a/haproxy/config.py +++ b/haproxy/config.py @@ -70,6 +70,7 @@ def parse_additional_backend_settings(envvars): HAPROXY_SERVICE_URI = os.getenv("DOCKERCLOUD_SERVICE_API_URI") HEALTH_CHECK = os.getenv("HEALTH_CHECK", "check inter 2000 rise 2 fall 3") HTTP_BASIC_AUTH = os.getenv("HTTP_BASIC_AUTH") +HTTP_BASIC_AUTH_SECURE = os.getenv("HTTP_BASIC_AUTH_SECURE") MAXCONN = os.getenv("MAXCONN", "4096") MODE = os.getenv("MODE", "http") MONITOR_PORT = os.getenv("MONITOR_PORT") diff --git a/haproxy/haproxycfg.py b/haproxy/haproxycfg.py index 5e6e3f1..de0d88d 100644 --- a/haproxy/haproxycfg.py +++ b/haproxy/haproxycfg.py @@ -163,7 +163,7 @@ def update(self): cfg_dict.update(self._config_global_section()) cfg_dict.update(self._config_defaults_section()) cfg_dict.update(self._config_stats_section()) - cfg_dict.update(self._config_userlist_section(HTTP_BASIC_AUTH)) + cfg_dict.update(self._config_userlist_section(HTTP_BASIC_AUTH, HTTP_BASIC_AUTH_SECURE)) cfg_dict.update(self._config_tcp_sections()) cfg_dict.update(self._config_frontend_sections()) cfg_dict.update(self._config_backend_sections()) @@ -290,10 +290,10 @@ def _config_defaults_section(): return cfg @staticmethod - def _config_userlist_section(basic_auth): - cfg = OrderedDict() - if basic_auth: - auth_list = re.split(r'(?&1 | grep -i curl --resolve web-a.org:80:${DOCKER_HOST_IP} http://web-a.org 2>&1 | grep -iF "My hostname is web-a" > /dev/null echo +echo "=> Testing Basic Auth and EXCLUDE_BASIC_AUTH" +rm_container web-a web-b lb +docker run -d --name web-a -e HOSTNAME=web-a -e VIRTUAL_HOST=web-a.org dockercloud/hello-world +docker run -d --name web-b -e HOSTNAME=web-b -e VIRTUAL_HOST=web-b.org -e EXCLUDE_BASIC_AUTH=true dockercloud/hello-world +docker run -d --name lb --link web-a:web-a --link web-b:web-b -e HTTP_BASIC_AUTH=abc:abc -p 80:80 haproxy +wait_for_startup http://${DOCKER_HOST_IP} +curl -sSfL --resolve web-a.org:80:${DOCKER_HOST_IP} http://web-a.org 2>&1 | grep -iF "401 Unauthorized" +curl -sSfL --resolve web-a.org:80:${DOCKER_HOST_IP} -u abc:abc http://web-a.org 2>&1 | grep -iF "My hostname is web-a" +curl -sSfL --resolve web-b.org:80:${DOCKER_HOST_IP} http://web-b.org 2>&1 | grep -iF "My hostname is web-b" echo "=> Clean up" cleanup echo "=> Done!" diff --git a/tests/unit/helper/test_backend_helper.py b/tests/unit/helper/test_backend_helper.py index 139359a..7f86bb0 100644 --- a/tests/unit/helper/test_backend_helper.py +++ b/tests/unit/helper/test_backend_helper.py @@ -11,45 +11,62 @@ def test_get_backend_routes(self): {'container_name': 'WEB_1', 'proto': 'tcp', 'port': '8080', 'addr': '10.7.0.5'}]} self.assertEqual(["server HW_1 10.7.0.3:80 check", "server HW_2 10.7.0.2:80 check"], get_backend_routes(route_setting="check", is_sticky=False, - routes=routes, routes_added=[], service_alias="HW")) + routes=routes, routes_added=[], service_alias="HW", details={})) self.assertEqual(["server WEB_1 10.7.0.5:8080", "server WEB_2 10.7.0.4:8080"], get_backend_routes(route_setting="", is_sticky=False, - routes=routes, routes_added=[], service_alias="WEB")) + routes=routes, routes_added=[], service_alias="WEB", details={})) self.assertEqual(["server WEB_1 10.7.0.5:8080 cookie WEB_1", "server WEB_2 10.7.0.4:8080 cookie WEB_2"], get_backend_routes(route_setting="", is_sticky=True, - routes=routes, routes_added=[], service_alias="WEB")) + routes=routes, routes_added=[], service_alias="WEB", details={})) self.assertEqual([], get_backend_routes(route_setting="", is_sticky=False, - routes={}, routes_added=[], service_alias="WEB")) + routes={}, routes_added=[], service_alias="WEB", details={})) self.assertEqual(["server WEB_2 10.7.0.4:8080"], get_backend_routes(route_setting="", is_sticky=False, routes=routes, routes_added=[ {'container_name': 'WEB_1', 'proto': 'tcp', 'port': '8080', 'addr': '10.7.0.5'}], - service_alias="WEB")) + service_alias="WEB", details={})) self.assertEqual(["server WEB_2 10.7.0.4:8080 cookie WEB_2"], get_backend_routes(route_setting="", is_sticky=True, routes=routes, routes_added=[ {'container_name': 'WEB_1', 'proto': 'tcp', 'port': '8080', 'addr': '10.7.0.5'}], - service_alias="WEB")) + service_alias="WEB", details={})) self.assertEqual(["server WEB_1 10.7.0.5:8080", "server WEB_2 10.7.0.4:8080"], get_backend_routes(route_setting="", is_sticky=False, routes=routes, routes_added=[ {'container_name': 'WEB_3', 'proto': 'tcp', 'port': '8080', 'addr': '10.7.0.5'}], - service_alias="WEB")) + service_alias="WEB", details={})) self.assertEqual([], get_backend_routes(route_setting="", is_sticky=False, routes=routes, routes_added=[ {'container_name': 'WEB_2', 'proto': 'tcp', 'port': '8080', 'addr': '10.7.0.4'}, {'container_name': 'WEB_1', 'proto': 'tcp', 'port': '8080', 'addr': '10.7.0.5'}], - service_alias="WEB")) + service_alias="WEB", details={})) self.assertEqual(["server HW_1 10.7.0.3:80 check", "server HW_2 10.7.0.2:80 check"], get_backend_routes(route_setting="check", is_sticky=False, routes=routes, routes_added=[ {'container_name': 'WEB_3', 'proto': 'tcp', 'port': '8080', 'addr': '10.7.0.5'}], - service_alias="HW")) + service_alias="HW", details={})) self.assertEqual([], get_backend_routes(route_setting="", is_sticky=False, - routes=routes, routes_added=[], service_alias="HELLO")) + routes=routes, routes_added=[], service_alias="HELLO", details={})) + + def test_get_backend_routes_with_failover(self): + routes = {'HW': [{'container_name': 'HW_1', 'proto': 'tcp', 'port': '80', 'addr': '10.7.0.3'}, + {'container_name': 'HW_2', 'proto': 'tcp', 'port': '80', 'addr': '10.7.0.2'}], + 'WEB': [{'container_name': 'WEB_2', 'proto': 'tcp', 'port': '8080', 'addr': '10.7.0.4'}, + {'container_name': 'WEB_1', 'proto': 'tcp', 'port': '8080', 'addr': '10.7.0.5'}]} + details = {'HW': {'failover': "true"}, + 'WEB': {'failover': ""}} + + self.assertEqual(["server HW_1 10.7.0.3:80 check backup", "server HW_2 10.7.0.2:80 check backup"], + get_backend_routes(route_setting="check", is_sticky=False, + routes=routes, routes_added=[ + {'container_name': 'WEB_3', 'proto': 'tcp', 'port': '8080', 'addr': '10.7.0.5'}], + service_alias="HW", details=details)) + self.assertEqual(["server WEB_1 10.7.0.5:8080", "server WEB_2 10.7.0.4:8080"], + get_backend_routes(route_setting="", is_sticky=False, + routes=routes, routes_added=[], service_alias="WEB", details=details)) def test_get_route_health(self): details = {'web-a': {'health_check': 'health_check_web_a'}, @@ -199,7 +216,11 @@ def test_get_extra_settings_setting(self): self.assertEqual([], get_extra_settings_setting(details, 'web-f')) def test_get_basic_auth_setting(self): + details = {'web-a': {'exclude_basic_auth': 'true'}, + 'web-b': {}} + + self.assertEqual([], get_basic_auth_setting(details, 'something', 'web-a')) self.assertEqual( ["acl need_auth http_auth(haproxy_userlist)", "http-request auth realm haproxy_basic_auth if !need_auth"], - get_basic_auth_setting('something')) - self.assertEqual([], get_basic_auth_setting("")) + get_basic_auth_setting(details, 'something', 'web-b')) + self.assertEqual([], get_basic_auth_setting(details, "", 'web-b')) diff --git a/tests/unit/helper/test_swarm_mode_link_helper.py b/tests/unit/helper/test_swarm_mode_link_helper.py index 53b4f0c..9708364 100644 --- a/tests/unit/helper/test_swarm_mode_link_helper.py +++ b/tests/unit/helper/test_swarm_mode_link_helper.py @@ -913,9 +913,9 @@ u'31b6fuwub6dcgdrvy0kivxvug': {'service_name': u'app', 'endpoints': {u'80/tcp': u'tcp://10.0.0.7:80'}, 'container_envvars': [{'value': u'80', 'key': u'SERVICE_PORTS'}], 'container_name': u'app.3.31b6fuwub6dcgdrvy0kivxvug'}} -expected_linked_tasks = {u'7y45xdhy929wzcq94wqdqb8d3':{}, u'bbref0yvjbix87pv6xz1jc3pr':{}, - u'31b6fuwub6dcgdrvy0kivxvug':{}} -expected_nets = {"b951j4at14qali5nxevshec93", "0l130ctu8xay6vfft1mb4tjv0"} +expected_linked_tasks = {u'7y45xdhy929wzcq94wqdqb8d3': {}, u'bbref0yvjbix87pv6xz1jc3pr': {}, + u'31b6fuwub6dcgdrvy0kivxvug': {}} +expected_nets = {"0l130ctu8xay6vfft1mb4tjv0"} expected_service_id = "07ql4q5a48seh1uhcr2m7ngar" @@ -945,6 +945,7 @@ def services(self): def tasks(self, filters): return [t for t in tasks if t.get("DesiredState", "") == "running"] + links, linked_tasks = get_swarm_mode_links(Docker(), expected_service_id, expected_nets) self.assertEquals(expected_links, links) self.assertEquals(expected_linked_tasks, linked_tasks) diff --git a/tests/unit/parser/test_legacy_parser.py b/tests/unit/parser/test_legacy_parser.py index 6a83c10..b51c73d 100644 --- a/tests/unit/parser/test_legacy_parser.py +++ b/tests/unit/parser/test_legacy_parser.py @@ -16,12 +16,14 @@ def setUp(self): 'gzip_compression_type': None, 'http_check': None, 'virtual_host_weight': 0, 'health_check': None, 'cookie': None, 'virtual_host': 'a.com', 'force_ssl': None, 'tcp_ports': [], 'balance': None, 'extra_settings': None, 'appsession': None, - 'option': [], "extra_route_settings": None}, + 'option': [], "extra_route_settings": None, "failover": None, + 'exclude_basic_auth': None}, 'HELLO': {'default_ssl_cert': '', 'ssl_cert': '', 'exclude_ports': [], 'hsts_max_age': None, 'gzip_compression_type': None, 'http_check': None, 'virtual_host_weight': 0, 'health_check': None, 'cookie': None, 'virtual_host': 'b.com', 'force_ssl': None, 'tcp_ports': [], 'balance': None, 'extra_settings': None, 'appsession': None, - 'option': [], "extra_route_settings": None}} + 'option': [], "extra_route_settings": None, "failover": None, + 'exclude_basic_auth': None}} self.routes = {'WORLD': [{'container_name': 'WORLD_1', 'proto': 'tcp', 'port': '80', 'addr': '10.7.0.3'}], 'HELLO': [{'container_name': 'HELLO_1', 'proto': 'tcp', 'port': '80', 'addr': '10.7.0.1'}]} self.vhosts = [{'path': '', 'host': 'a.com', 'scheme': 'http', 'port': '80', 'service_alias': 'WORLD'}, @@ -70,12 +72,14 @@ def test_parse_details(self): 'gzip_compression_type': None, 'http_check': None, 'virtual_host_weight': 0, 'health_check': None, 'cookie': None, 'virtual_host': None, 'force_ssl': None, 'tcp_ports': [], 'balance': None, 'extra_settings': None, 'appsession': None, - 'option': [], "extra_route_settings": None}, + 'option': [], "extra_route_settings": None, "failover": None, + 'exclude_basic_auth': None, 'exclude_basic_auth': None}, 'HELLO': {'default_ssl_cert': '', 'ssl_cert': '', 'exclude_ports': [], 'hsts_max_age': None, 'gzip_compression_type': None, 'http_check': None, 'virtual_host_weight': 0, 'health_check': None, 'cookie': None, 'virtual_host': None, 'force_ssl': None, 'tcp_ports': [], 'balance': None, 'extra_settings': None, 'appsession': None, - 'option': [], "extra_route_settings": None}} + 'option': [], "extra_route_settings": None, "failover": None, + 'exclude_basic_auth': None, 'exclude_basic_auth': None}} self.assertEqual({}, specs._parse_details([], {})) self.assertEqual(empty_details, specs._parse_details(self.service_aliases, {})) self.assertEqual({}, specs._parse_details([], self.envvars)) @@ -102,17 +106,17 @@ def test_parse_routes(self): 'gzip_compression_type': None, 'http_check': None, 'virtual_host_weight': 0, 'health_check': None, 'cookie': None, 'virtual_host': 'a.com', 'force_ssl': None, 'tcp_ports': [], 'balance': None, 'extra_settings': None, 'appsession': None, - 'option': [], "extra_route_settings": None}, + 'option': [], "extra_route_settings": None, 'exclude_basic_auth': None}, 'HELLO': {'default_ssl_cert': '', 'ssl_cert': '', 'exclude_ports': [], 'hsts_max_age': None, 'gzip_compression_type': None, 'http_check': None, 'virtual_host_weight': 0, 'health_check': None, 'cookie': None, 'virtual_host': 'b.com', 'force_ssl': None, 'tcp_ports': [], 'balance': None, 'extra_settings': None, 'appsession': None, - 'option': [], "extra_route_settings": None}, + 'option': [], "extra_route_settings": None, 'exclude_basic_auth': None}, 'DUPLICATED': {'default_ssl_cert': '', 'ssl_cert': '', 'exclude_ports': [], 'hsts_max_age': None, 'gzip_compression_type': None, 'http_check': None, 'virtual_host_weight': 0, 'health_check': None, 'cookie': None, 'virtual_host': 'b.com', 'force_ssl': None, 'tcp_ports': [], 'balance': None, 'extra_settings': None, 'appsession': None, - 'option': [], "extra_route_settings": None} + 'option': [], "extra_route_settings": None, 'exclude_basic_auth': None} } routes = {'WORLD': [{'container_name': 'WORLD_1', 'proto': 'tcp', 'port': '80', 'addr': '10.7.0.3'}], diff --git a/tests/unit/parser/test_new_parser.py b/tests/unit/parser/test_new_parser.py index 31c4fe1..e7eb1de 100644 --- a/tests/unit/parser/test_new_parser.py +++ b/tests/unit/parser/test_new_parser.py @@ -35,12 +35,14 @@ def setUp(self): 'gzip_compression_type': '', 'http_check': '', 'virtual_host_weight': 0, 'health_check': '', 'cookie': '', 'virtual_host': 'ab.com', 'force_ssl': '', 'tcp_ports': [], 'balance': '', 'extra_settings': '', 'appsession': '', - 'option': [], "extra_route_settings": ''}, + 'option': [], "extra_route_settings": '', "failover": '', + 'exclude_basic_auth': ''}, 'tmp_hello': {'default_ssl_cert': '', 'ssl_cert': '', 'exclude_ports': [], 'hsts_max_age': '', 'gzip_compression_type': '', 'http_check': '', 'virtual_host_weight': 0, 'health_check': '', 'cookie': '', 'virtual_host': 'a.com', 'force_ssl': '', 'tcp_ports': [], 'balance': '', 'extra_settings': '', 'appsession': '', - 'option': [], "extra_route_settings": ''}} + 'option': [], "extra_route_settings": '', "failover": '', + 'exclude_basic_auth': ''}} self.routes = {'tmp_hello': [{'addr': 'tmp_hello_1', 'container_name': 'tmp_hello_1', 'port': '80', @@ -85,12 +87,14 @@ def test_parse_details(self): 'gzip_compression_type': '', 'http_check': '', 'virtual_host_weight': 0, 'health_check': '', 'cookie': '', 'virtual_host': '', 'force_ssl': '', 'tcp_ports': [], 'balance': '', 'extra_settings': '', 'appsession': '', - 'option': [], "extra_route_settings": ''}, + 'option': [], "extra_route_settings": '', "failover": '', + 'exclude_basic_auth': ''}, 'tmp_hello': {'default_ssl_cert': '', 'ssl_cert': '', 'exclude_ports': [], 'hsts_max_age': '', 'gzip_compression_type': '', 'http_check': '', 'virtual_host_weight': 0, 'health_check': '', 'cookie': '', 'virtual_host': '', 'force_ssl': '', 'tcp_ports': [], 'balance': '', 'extra_settings': '', 'appsession': '', - 'option': [], "extra_route_settings": ''}} + 'option': [], "extra_route_settings": '', "failover": '', + 'exclude_basic_auth': ''}} self.assertEqual({}, NewSpecs._parse_details([], {})) self.assertEqual({}, NewSpecs._parse_details([], self.links)) diff --git a/tests/unit/test_haproxycfg.py b/tests/unit/test_haproxycfg.py index 6acad8f..16a95c8 100644 --- a/tests/unit/test_haproxycfg.py +++ b/tests/unit/test_haproxycfg.py @@ -153,18 +153,23 @@ def test_config_ssl_certs(self, mock_init, mock_save): class HaproxyConfigUserListTestCase(unittest.TestCase): def test_config_userlist_section(self): - self.assertEqual({}, Haproxy._config_userlist_section("")) + self.assertEqual({}, Haproxy._config_userlist_section("", "")) + self.assertEqual({'userlist haproxy_userlist': ['user user password hash']}, + Haproxy._config_userlist_section("", "user:hash")) self.assertEqual({'userlist haproxy_userlist': ['user user insecure-password pass']}, - Haproxy._config_userlist_section("user:pass")) + Haproxy._config_userlist_section("user:pass", "")) + self.assertEqual(OrderedDict([('userlist haproxy_userlist', ['user user1 insecure-password pass1', + 'user user2 password hash2'])]), + Haproxy._config_userlist_section("user1:pass1", "user2:hash2")) self.assertEqual(OrderedDict([('userlist haproxy_userlist', ['user user1 insecure-password pass1', 'user user2 insecure-password pass2', 'user user3 insecure-password pass3'])]), - Haproxy._config_userlist_section("user1:pass1, user2:pass2 ,user3:pass3")) + Haproxy._config_userlist_section("user1:pass1, user2:pass2 ,user3:pass3", "")) self.assertEqual(OrderedDict([('userlist haproxy_userlist', ['user us,er1 insecure-password pass,1', 'user user2 insecure-password ', 'user insecure-password pass3'])]), - Haproxy._config_userlist_section("us\,er1:pass\,1, user2:, :pass3")) + Haproxy._config_userlist_section("us\,er1:pass\,1, user2:, :pass3", "")) @mock.patch.object(Specs, 'get_routes') @mock.patch.object(Specs, 'get_service_aliases')