diff --git a/docs/container-labels.md b/docs/container-labels.md index daa1b51..fba93ba 100644 --- a/docs/container-labels.md +++ b/docs/container-labels.md @@ -2,20 +2,21 @@ ## Container (Docker or Swarm) labels -| Tag | Description | Default | Example | -|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|--------------|------------------------------------------------------------| -| easyhaproxy.[definition].host | Host(s) HAProxy is listening. More than one host use comma as delimiter | **required** | somehost.com OR host1.com,host2.com | -| easyhaproxy.[definition].mode | (Optional) Is this `http` or `tcp` mode in HAProxy. | http | http or tcp | -| easyhaproxy.[definition].port | (Optional) Port HAProxy will listen for the host. | 80 | 3000 | -| easyhaproxy.[definition].localport | (Optional) Port container is listening. | 80 | 8080 | -| easyhaproxy.[definition].redirect | (Optional) JSON containing key/value pair from host/to URL redirect. | *empty* | {"foo.com":"https://bla.com", "bar.com":"https://bar.org"} | -| easyhaproxy.[definition].sslcert | (Optional) Cert PEM Base64 encoded. Do not use this if `certbot` is enabled. | *empty* | base64 cert + key | -| easyhaproxy.[definition].ssl | (Optional) If `true` you need to provide certificate as a file. See below. Do not use with `sslcert`. | false | true or false | -| easyhaproxy.[definition].ssl-check | (Optional) `ssl`, enable health check via SSL in `mode tcp` | *empty* | ssl | -| easyhaproxy.[definition].certbot | (Optional) Generate certificate with certbot. Do not use with `sslcert` parameter. More info [here](acme.md). | false | true OR false | -| easyhaproxy.[definition].redirect_ssl | (Optional) Redirect all requests to https | false | true OR false | -| easyhaproxy.[definition].clone_to_ssl | (Optional) It copies the configuration to HTTPS(443) and disable SSL from the current config. **Do not use* this with `ssl` or `certbot` parameters | false | true OR false | -| easyhaproxy.[definition].balance | (Optional) HAProxy balance algorithm. See [HAProxy documentation](https://cbonte.github.io/haproxy-dconv/1.8/configuration.html#4.2-balance) | roundrobin | roundrobin, source, uri, url_param, hdr, rdp-cookie, leastconn, first, static-rr, rdp-cookie, hdr_dom, map-based | +| Tag | Description | Default | Example | +|----------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|--------------|------------------------------------------------------------------------------------------------------------------| +| easyhaproxy.[definition].host | Host(s) HAProxy is listening. More than one host use comma as delimiter | **required** | somehost.com OR host1.com,host2.com | +| easyhaproxy.[definition].mode | (Optional) Is this `http` or `tcp` mode in HAProxy. | http | http or tcp | +| easyhaproxy.[definition].port | (Optional) Port HAProxy will listen for the host. | 80 | 3000 | +| easyhaproxy.[definition].localport | (Optional) Port container is listening. | 80 | 8080 | +| easyhaproxy.[definition].redirect | (Optional) JSON containing key/value pair from host/to URL redirect. | *empty* | {"foo.com":"https://bla.com", "bar.com":"https://bar.org"} | +| easyhaproxy.[definition].sslcert | (Optional) Cert PEM Base64 encoded. Do not use this if `certbot` is enabled. | *empty* | base64 cert + key | +| easyhaproxy.[definition].ssl | (Optional) If `true` you need to provide certificate as a file. See below. Do not use with `sslcert`. | false | true or false | +| easyhaproxy.[definition].ssl-check | (Optional) `ssl`, enable health check via SSL in `mode tcp` | *empty* | ssl | +| easyhaproxy.[definition].certbot | (Optional) Generate certificate with certbot. Do not use with `sslcert` parameter. More info [here](acme.md). | false | true OR false | +| easyhaproxy.[definition].redirect_ssl | (Optional) Redirect all requests to https | false | true OR false | +| easyhaproxy.[definition].clone_to_ssl | (Optional) It copies the configuration to HTTPS(443) and disable SSL from the current config. **Do not use* this with `ssl` or `certbot` parameters | false | true OR false | +| easyhaproxy.[definition].balance | (Optional) HAProxy balance algorithm. See [HAProxy documentation](https://cbonte.github.io/haproxy-dconv/1.8/configuration.html#4.2-balance) | roundrobin | roundrobin, source, uri, url_param, hdr, rdp-cookie, leastconn, first, static-rr, rdp-cookie, hdr_dom, map-based | +| easyhaproxy.[definition].plugin.[name] | (Optional) EasyHAProxy plugin. | *empty* | Check the documentation of the plugin | The `definition` is a string that will group all configurations togethers. Different `definition` will create different configurations. diff --git a/docs/static.md b/docs/static.md index 4f7c3a1..dc68b79 100644 --- a/docs/static.md +++ b/docs/static.md @@ -109,6 +109,10 @@ easymapping: - container:5000 # Endpoints of the hostname above (ip, dns, container, etc) certbot: true # Optional. it will request a certbot certificate. Needs certbot.email set. redirect_ssl: true # Optional. It will redirect this site to it SSL. + balance: roundrobin # Optional. Default `roundrobin`. Can be roundrobin, leastconn, source + plugin: # Optional. Default `none`. + - name1: args1 + - name2: args2 ssl: true # Optional. Inform this port will listen to SSL, instead of HTTP clone_to_ssl: true # Optional. Default False. You clone these hosts to its equivalent SSL. redirect: diff --git a/src/easymapping/__init__.py b/src/easymapping/__init__.py index bb1c833..6ea415b 100644 --- a/src/easymapping/__init__.py +++ b/src/easymapping/__init__.py @@ -13,7 +13,7 @@ def __init__(self, label): def get_lookup_label(self): return self.__label_base - def create(self, key): + def format(self, key): if isinstance(key, str): return "{}.{}".format(self.__label_base, key) @@ -34,6 +34,17 @@ def get_json(self, label, default_value={}): return json.loads(self.__data[label]) return default_value + def get_pattern(self, label): + label += "." + matching_elements = [element for element in self.__data if element.startswith(label)] + result_dict = {} + + for element in matching_elements: + plugin_name = element[len(label):] + result_dict[plugin_name] = self.get(element) + + return result_dict if result_dict else False + def set_data(self, data): self.__data = data @@ -89,26 +100,26 @@ def parse(self, container_metadata): # Parse each definition found. for definition in sorted(definitions.keys()): mode = self.label.get( - self.label.create([definition, "mode"]), + self.label.format([definition, "mode"]), "http" ) # TODO: we can ignore "host" in TCP, but it would break the template - host_label = self.label.create([definition, "host"]) + host_label = self.label.format([definition, "host"]) if not self.label.has_label(host_label): continue port = self.label.get( - self.label.create([definition, "port"]), + self.label.format([definition, "port"]), "80" ) certbot = self.label.get_bool( - self.label.create([definition, "certbot"]), + self.label.format([definition, "certbot"]), False ) and self.mapping["certbot"]["email"] != "" clone_to_ssl = self.label.get_bool( - self.label.create([definition, "clone_to_ssl"]) + self.label.format([definition, "clone_to_ssl"]) ) if port not in easymapping: @@ -122,15 +133,17 @@ def parse(self, container_metadata): # TODO: this could use `EXPOSE` from `Dockerfile`? ct_port = self.label.get( - self.label.create([definition, "localport"]), + self.label.format([definition, "localport"]), "80" ) easymapping[port]["ssl-check"] = self.label.get( - self.label.create([definition, "ssl-check"]), + self.label.format([definition, "ssl-check"]), "" ) + plugins = self.label.get_pattern(self.label.format([definition, "plugin"])) + for hostname in sorted(d[host_label].split(",")): hostname = hostname.strip() self.serving_hosts.append("%s:%s" % (hostname, port)) @@ -140,17 +153,20 @@ def parse(self, container_metadata): easymapping[port]["hosts"][hostname]["containers"] += ["{}:{}".format(container, ct_port)] easymapping[port]["hosts"][hostname]["certbot"] = certbot easymapping[port]["hosts"][hostname]["redirect_ssl"] = self.label.get_bool( - self.label.create([definition, "redirect_ssl"]) + self.label.format([definition, "redirect_ssl"]) ) easymapping[port]["hosts"][hostname]["balance"] = self.label.get( - self.label.create([definition, "balance"]), + self.label.format([definition, "balance"]), "roundrobin" ) easymapping[port]["redirect"] = self.label.get_json( - self.label.create([definition, "redirect"]) + self.label.format([definition, "redirect"]) ) + if (plugins): + easymapping[port]["hosts"][hostname]["plugins"] = plugins + if certbot or clone_to_ssl: if "443" not in easymapping: easymapping["443"] = { @@ -168,13 +184,13 @@ def parse(self, container_metadata): hostname) if certbot and hostname not in self.certbot_hosts else self.certbot_hosts # handle SSL - ssl_label = self.label.create([definition, "sslcert"]) + ssl_label = self.label.format([definition, "sslcert"]) if self.label.has_label(ssl_label): filename = "{}.pem".format(d[host_label]) easymapping[port]["ssl"] = True if not clone_to_ssl else False self.certs[filename] = base64.b64decode(d[ssl_label]).decode('ascii') - if self.label.get_bool(self.label.create([definition, "ssl"])): + if self.label.get_bool(self.label.format([definition, "ssl"])): easymapping[port]["ssl"] = True if not clone_to_ssl else False return easymapping.values() diff --git a/src/tests/test_docker.py b/src/tests/test_docker.py index e31063e..fd39f8e 100644 --- a/src/tests/test_docker.py +++ b/src/tests/test_docker.py @@ -42,6 +42,7 @@ def test_processor_docker(): "easyhaproxy.http.port": "80", "easyhaproxy.http.localport": "8080", "easyhaproxy.http.host": "host1.local", + "easyhaproxy.http.plugin.xyz": "host1.local", "easyhaproxy.http2.port": "90", "easyhaproxy.http2.localport": "9000", @@ -71,6 +72,7 @@ def test_processor_docker(): 'easyhaproxy.http.host': 'host1.local', 'easyhaproxy.http.localport': '8080', 'easyhaproxy.http.port': '80', + 'easyhaproxy.http.plugin.xyz': "host1.local", 'easyhaproxy.http2.host': 'host2.local', 'easyhaproxy.http2.localport': '9000', 'easyhaproxy.http2.port': '90', diff --git a/src/tests/test_labels.py b/src/tests/test_labels.py index 14ce279..34cb60e 100644 --- a/src/tests/test_labels.py +++ b/src/tests/test_labels.py @@ -6,15 +6,15 @@ def test_label_generation(): label = DockerLabelHandler("foo") - assert label.create("bar") == "foo.bar" - assert label.create(["bar", "foobar"]) == "foo.bar.foobar" + assert label.format("bar") == "foo.bar" + assert label.format(["bar", "foobar"]) == "foo.bar.foobar" def test_label_data(): label = DockerLabelHandler("base") label.set_data(json.loads('{"base.definitions":"h2"}')) - label_name = label.create("definitions") + label_name = label.format("definitions") assert label_name == "base.definitions" assert label.has_label(label_name) assert label.get(label_name) == "h2" @@ -29,5 +29,5 @@ def test_label_complex_key(): data["till.mode.h2"] = "tcp" label.set_data(json.loads(json.dumps(data))) - assert label.get(label.create(["host", "h2"])) == "fqdn.example.org" - assert label.get(label.create(["mode", "h2"])) == "tcp" + assert label.get(label.format(["host", "h2"])) == "fqdn.example.org" + assert label.get(label.format(["mode", "h2"])) == "tcp"