Skip to content
This repository has been archived by the owner on Dec 13, 2018. It is now read-only.

Commit

Permalink
add a label to turn on/off a service balanced in haproxy in swarm mode (
Browse files Browse the repository at this point in the history
#150)

* add a label to switch services on blue/green testing

* bump ver
  • Loading branch information
Feng Honglin committed Dec 16, 2016
1 parent a341a3f commit b881015
Show file tree
Hide file tree
Showing 8 changed files with 31 additions and 20 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,14 @@ Settings here can overwrite the settings in HAProxy, which are only applied to t
|TCP_PORTS|comma separated ports(e.g. 9000, 9001, 2222/ssl). The port listed in `TCP_PORTS` will be load-balanced in TCP mode. Port ends with `/ssl` indicates that port needs SSL termination.
|VIRTUAL_HOST_WEIGHT|an integer of the weight of an virtual host, used together with `VIRTUAL_HOST`, default:0. It affects the order of acl rules of the virtual hosts. The higher weight one virtual host has, the more priority that acl rules applies.|
|VIRTUAL_HOST|specify virtual host and virtual path. Format: `[scheme://]domain[:port][/path], ...`. wildcard `*` can be used in `domain` and `path` part|
|SERVICE_PORTS|comma separated ports(e.g. 80, 8080), which are the ports you would like to expose in your application service. This envvar is swarm mode only, and it is **MUST** be set in swarm mode|

Swarm Mode only settings:

|Name|Type|Description|
|:--:|:--:|:---------:|
|SERVICE_PORTS|envvar|comma separated ports(e.g. 80, 8080), which are the ports you would like to expose in your application service. This envvar is swarm mode only, and it is **MUST** be set in swarm mode|
|`com.docker.dockercloud.haproxy.deactivate=<true|false>`|label|when this label is set to true, haproxy will ignore the service. Could be useful for switching services on blue/green testing|


Check [the HAProxy configuration manual](http://cbonte.github.io/haproxy-dconv/configuration-1.5.html) for more information on the above.

Expand Down
2 changes: 1 addition & 1 deletion haproxy/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.6.1"
__version__ = "1.6.2"
3 changes: 3 additions & 0 deletions haproxy/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def parse_extra_frontend_settings(envvars):
settings_dict[port] = settings
return settings_dict


def parse_additional_backend_settings(envvars):
settings_dict = {}
if isinstance(envvars, os._Environ) or isinstance(envvars, dict):
Expand All @@ -47,6 +48,7 @@ def parse_additional_backend_settings(envvars):
settings_dict[server] = settings
return settings_dict


# envvar
ADDITIONAL_BACKENDS = parse_additional_backend_settings(os.environ)
ADDITIONAL_SERVICES = os.getenv("ADDITIONAL_SERVICES")
Expand Down Expand Up @@ -94,6 +96,7 @@ def parse_additional_backend_settings(envvars):
API_RETRY = 10 # seconds
PID_FILE = "/tmp/dockercloud-haproxy.pid"
SERVICE_PORTS_ENVVAR_NAME = "SERVICE_PORTS"
LABEL_SWARM_MODE_DEACTIVATE = "com.docker.dockercloud.haproxy.deactivate"

# regular expressions
SERVICE_NAME_MATCH = re.compile(r"(.+)_\d+$")
Expand Down
15 changes: 5 additions & 10 deletions haproxy/eventhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import config
import helper.cloud_mode_link_helper
import helper.swarm_mode_link_helper as SwarmModeLinkHelper
from haproxycfg import add_haproxy_run_task, Haproxy
from utils import get_uuid_from_resource_uri

Expand Down Expand Up @@ -106,17 +107,11 @@ def polling_service_status_swarm_mode():
except:
docker = docker_client(os.environ)

services = docker.services()
tasks = docker.tasks(filters={"desired-state": "running"})
linked_tasks = set()
for task in tasks:
task_nets = [network.get("Network", {}).get("ID", "") for network in
task.get("NetworksAttachments", [])]
task_service_id = task.get("ServiceID", "")
if task_service_id != Haproxy.cls_service_id and Haproxy.cls_nets.intersection(set(task_nets)):
task_id = task.get("ID", "")
linked_tasks.add(task_id)

if Haproxy.cls_linked_tasks != linked_tasks:
_, linked_tasks = SwarmModeLinkHelper.get_task_links(tasks, services, Haproxy.cls_service_id,
Haproxy.cls_nets)
if cmp(Haproxy.cls_linked_tasks, linked_tasks) != 0:
add_haproxy_run_task("Tasks are updated")
except APIError as e:
logger.info("Docker API error: %s" % e)
2 changes: 1 addition & 1 deletion haproxy/haproxycfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,4 +395,4 @@ def _config_adittional_backends_sections(self):
for key in ADDITIONAL_BACKENDS:
cfg["backend %s" % key] = ADDITIONAL_BACKENDS[key]

return cfg
return cfg
1 change: 1 addition & 0 deletions haproxy/helper/backend_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def get_backend_routes(route_setting, is_sticky, routes, routes_added, service_a

return sorted(backend_routes)


def get_route_health_check(details, service_alias, default_health_check):
health_check = get_service_attribute(details, "health_check", service_alias)
health_check = health_check if health_check else default_health_check
Expand Down
15 changes: 10 additions & 5 deletions haproxy/helper/swarm_mode_link_helper.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging

import compose_mode_link_helper
from haproxy.config import SERVICE_PORTS_ENVVAR_NAME
from haproxy.config import SERVICE_PORTS_ENVVAR_NAME, LABEL_SWARM_MODE_DEACTIVATE

logger = logging.getLogger("haproxy")

Expand All @@ -27,14 +27,14 @@ def get_swarm_mode_haproxy_id_nets(docker, haproxy_container_short_id):
def get_swarm_mode_links(docker, haproxy_service_id, haproxy_nets):
services = docker.services()
tasks = docker.tasks(filters={"desired-state": "running"})
links, linked_tasks = get_task_links(tasks, services, haproxy_service_id, haproxy_nets)
return links, linked_tasks
return get_task_links(tasks, services, haproxy_service_id, haproxy_nets)


def get_task_links(tasks, services, haproxy_service_id, haproxy_nets):
services_id_name = {s.get("ID"): s.get("Spec", {}).get("Name", "") for s in services}
services_id_labels = {s.get("ID"): s.get("Spec", {}).get("Labels", {}) for s in services}
links = {}
linked_tasks = set()
linked_tasks = {}
for task in tasks:
task_nets = [network.get("Network", {}).get("ID", "") for network in task.get("NetworksAttachments", [])]
task_service_id = task.get("ServiceID", "")
Expand All @@ -44,6 +44,11 @@ def get_task_links(tasks, services, haproxy_service_id, haproxy_nets):
task_slot = "%d" % task.get("Slot", 0)
task_service_id = task.get("ServiceID", "")
task_service_name = services_id_name.get(task_service_id, "")
task_labels = services_id_labels.get(task_service_id)

if task_labels.get(LABEL_SWARM_MODE_DEACTIVATE, "").lower() == "true":
continue

container_name = ".".join([task_service_name, task_slot, task_id])
task_envvars = get_task_envvars(task.get("Spec", {}).get("ContainerSpec", {}).get("Env", []))

Expand All @@ -68,7 +73,7 @@ def get_task_links(tasks, services, haproxy_service_id, haproxy_nets):

links[task_id] = {"endpoints": task_endpoints, "container_name": container_name,
"service_name": task_service_name, "container_envvars": task_envvars}
linked_tasks.add(task_id)
linked_tasks[task_id] = task_labels
return links, linked_tasks


Expand Down
4 changes: 2 additions & 2 deletions tests/unit/helper/test_swarm_mode_link_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,8 @@
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_linked_tasks = {u'7y45xdhy929wzcq94wqdqb8d3':{}, u'bbref0yvjbix87pv6xz1jc3pr':{},
u'31b6fuwub6dcgdrvy0kivxvug':{}}
expected_nets = {"b951j4at14qali5nxevshec93", "0l130ctu8xay6vfft1mb4tjv0"}
expected_service_id = "07ql4q5a48seh1uhcr2m7ngar"

Expand Down Expand Up @@ -944,7 +945,6 @@ 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)
Expand Down

0 comments on commit b881015

Please sign in to comment.