From 603ecbe92b9686d421a438a43da54182409657f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20K=C3=A4mmerling?= Date: Tue, 11 Aug 2020 16:16:02 +0200 Subject: [PATCH] Implement Label Selector and IP Targets (#23) --- plugins/modules/hcloud_load_balancer.py | 2 +- .../modules/hcloud_load_balancer_target.py | 109 ++++++++++++++++-- .../hcloud_load_balancer/tasks/main.yml | 2 +- .../defaults/main.yml | 1 + .../tasks/main.yml | 30 +++++ tests/utils/gitlab/gitlab.sh | 2 +- 6 files changed, 135 insertions(+), 11 deletions(-) diff --git a/plugins/modules/hcloud_load_balancer.py b/plugins/modules/hcloud_load_balancer.py index ce4b48a..3626bea 100644 --- a/plugins/modules/hcloud_load_balancer.py +++ b/plugins/modules/hcloud_load_balancer.py @@ -246,7 +246,7 @@ class AnsibleHcloudLoadBalancer(Hcloud): load_balancer_type = self.module.params.get("load_balancer_type") if load_balancer_type is not None and self.hcloud_load_balancer.load_balancer_type.name != load_balancer_type: - new_load_balancer_type = self.client.server_types.get_by_name(load_balancer_type) + new_load_balancer_type = self.client.load_balancer_types.get_by_name(load_balancer_type) if not new_load_balancer_type: self.module.fail_json(msg="unknown load balancer type") if not self.module.check_mode: diff --git a/plugins/modules/hcloud_load_balancer_target.py b/plugins/modules/hcloud_load_balancer_target.py index 50dd00d..890da71 100644 --- a/plugins/modules/hcloud_load_balancer_target.py +++ b/plugins/modules/hcloud_load_balancer_target.py @@ -26,7 +26,7 @@ options: description: - The type of the target. type: str - choices: [ server ] + choices: [ server, label_selector, ip ] required: true load_balancer: description: @@ -38,6 +38,16 @@ options: - The name of the Hetzner Cloud Server. - Required if I(type) is server type: str + label_selector: + description: + - A Label Selector that will be used to determine the targets dynamically + - Required if I(type) is label_selector + type: str + ip: + description: + - An IP from a Hetzner Dedicated Server, needs to belongs to the same user as the project. + - Required if I(type) is ip + type: str use_private_ip: description: - Route the traffic over the private IP of the Load Balancer through a Hetzner Cloud Network. @@ -66,6 +76,20 @@ EXAMPLES = """ server: my-server state: present +- name: Create a label_selector Load Balancer target + hcloud_load_balancer_target: + type: server + load_balancer: my-LoadBalancer + label_selector: application=backend + state: present + +- name: Create an IP Load Balancer target + hcloud_load_balancer_target: + type: server + load_balancer: my-LoadBalancer + ip: 127.0.0.1 + state: present + - name: Ensure the Load Balancer target is absent (remove if needed) hcloud_load_balancer_target: type: server @@ -95,6 +119,23 @@ hcloud_load_balancer_target: type: str returned: if I(type) is server sample: my-server + label_selector: + description: Label Selector + type: str + returned: if I(type) is label_selector + sample: application=backend + ip: + description: IP of the dedicated server + type: str + returned: if I(type) is ip + sample: 127.0.0.1 + use_private_ip: + description: + - Route the traffic over the private IP of the Load Balancer through a Hetzner Cloud Network. + - Load Balancer needs to be attached to a network. See M(hetzner.hcloud.hcloud.hcloud_load_balancer_network) + type: bool + sample: true + returned: always """ from ansible.module_utils.basic import AnsibleModule @@ -103,7 +144,7 @@ from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hclou try: from hcloud import APIException - from hcloud.load_balancers.domain import LoadBalancerTarget + from hcloud.load_balancers.domain import LoadBalancerTarget, LoadBalancerTargetLabelSelector, LoadBalancerTargetIP except ImportError: APIException = None @@ -116,12 +157,20 @@ class AnsibleHcloudLoadBalancerTarget(Hcloud): self.hcloud_server = None def _prepare_result(self): - return { + result = { "type": to_native(self.hcloud_load_balancer_target.type), "load_balancer": to_native(self.hcloud_load_balancer.name), - "server": to_native(self.hcloud_server.name) if self.hcloud_server else None, + "use_private_ip": self.hcloud_load_balancer_target.use_private_ip } + if self.hcloud_load_balancer_target.type == "server": + result["server"] = to_native(self.hcloud_load_balancer_target.server.name) + elif self.hcloud_load_balancer_target.type == "label_selector": + result["label_selector"] = to_native(self.hcloud_load_balancer_target.label_selector.selector) + elif self.hcloud_load_balancer_target.type == "ip": + result["ip"] = to_native(self.hcloud_load_balancer_target.ip.ip) + return result + def _get_load_balancer_and_target(self): try: self.hcloud_load_balancer = self.client.load_balancers.get_by_name(self.module.params.get("load_balancer")) @@ -133,9 +182,15 @@ class AnsibleHcloudLoadBalancerTarget(Hcloud): def _get_load_balancer_target(self): for target in self.hcloud_load_balancer.targets: - if self.module.params.get("type") == "server": + if self.module.params.get("type") == "server" and target.type == "server": if target.server.id == self.hcloud_server.id: self.hcloud_load_balancer_target = target + elif self.module.params.get("type") == "label_selector" and target.type == "label_selector": + if target.label_selector.selector == self.module.params.get("label_selector"): + self.hcloud_load_balancer_target = target + elif self.module.params.get("type") == "ip" and target.type == "ip": + if target.ip.ip == self.module.params.get("ip"): + self.hcloud_load_balancer_target = target def _create_load_balancer_target(self): params = { @@ -148,11 +203,30 @@ class AnsibleHcloudLoadBalancerTarget(Hcloud): ) params["target"] = LoadBalancerTarget(type=self.module.params.get("type"), server=self.hcloud_server, use_private_ip=self.module.params.get("use_private_ip")) + elif self.module.params.get("type") == "label_selector": + self.module.fail_on_missing_params( + required_params=["label_selector"] + ) + params["target"] = LoadBalancerTarget(type=self.module.params.get("type"), + label_selector=LoadBalancerTargetLabelSelector( + selector=self.module.params.get("label_selector")), + use_private_ip=self.module.params.get("use_private_ip")) + elif self.module.params.get("type") == "ip": + self.module.fail_on_missing_params( + required_params=["ip"] + ) + params["target"] = LoadBalancerTarget(type=self.module.params.get("type"), + ip=LoadBalancerTargetIP(ip=self.module.params.get("ip")), + use_private_ip=False) + if not self.module.check_mode: try: self.hcloud_load_balancer.add_target(**params).wait_until_finished() except APIException as e: - self.module.fail_json(msg=e.message) + if e.code == "locked" or e.code == "conflict": + self._create_load_balancer_target() + else: + self.module.fail_json(msg=e.message) self._mark_as_changed() self._get_load_balancer_and_target() @@ -171,9 +245,26 @@ class AnsibleHcloudLoadBalancerTarget(Hcloud): if not self.module.check_mode: target = None if self.module.params.get("type") == "server": + self.module.fail_on_missing_params( + required_params=["server"] + ) target = LoadBalancerTarget(type=self.module.params.get("type"), - server=self.hcloud_server, + server=self.hcloud_server) + elif self.module.params.get("type") == "label_selector": + self.module.fail_on_missing_params( + required_params=["label_selector"] + ) + target = LoadBalancerTarget(type=self.module.params.get("type"), + label_selector=LoadBalancerTargetLabelSelector( + selector=self.module.params.get("label_selector")), use_private_ip=self.module.params.get("use_private_ip")) + elif self.module.params.get("type") == "ip": + self.module.fail_on_missing_params( + required_params=["ip"] + ) + target = LoadBalancerTarget(type=self.module.params.get("type"), + ip=LoadBalancerTargetIP(ip=self.module.params.get("ip")), + use_private_ip=False) self.hcloud_load_balancer.remove_target(target).wait_until_finished() self._mark_as_changed() self.hcloud_load_balancer_target = None @@ -182,9 +273,11 @@ class AnsibleHcloudLoadBalancerTarget(Hcloud): def define_module(): return AnsibleModule( argument_spec=dict( - type={"type": "str", "required": True, "choices": ["server"]}, + type={"type": "str", "required": True, "choices": ["server", "label_selector", "ip"]}, load_balancer={"type": "str", "required": True}, server={"type": "str"}, + label_selector={"type": "str"}, + ip={"type": "str"}, use_private_ip={"type": "bool", "default": False}, state={ "choices": ["absent", "present"], diff --git a/tests/integration/targets/hcloud_load_balancer/tasks/main.yml b/tests/integration/targets/hcloud_load_balancer/tasks/main.yml index 99386b6..436ed1a 100644 --- a/tests/integration/targets/hcloud_load_balancer/tasks/main.yml +++ b/tests/integration/targets/hcloud_load_balancer/tasks/main.yml @@ -81,7 +81,7 @@ assert: that: - result_after_test is not changed - - result_after_test.hcloud_load_balancer.load_balancer_type == "lb21" + - result_after_test.hcloud_load_balancer.load_balancer_type == "lb21" - name: test update Load Balancer protection hcloud_load_balancer: diff --git a/tests/integration/targets/hcloud_load_balancer_target/defaults/main.yml b/tests/integration/targets/hcloud_load_balancer_target/defaults/main.yml index 9061af9..bb38666 100644 --- a/tests/integration/targets/hcloud_load_balancer_target/defaults/main.yml +++ b/tests/integration/targets/hcloud_load_balancer_target/defaults/main.yml @@ -4,3 +4,4 @@ hcloud_prefix: "tests" hcloud_server_name: "{{hcloud_prefix}}-lb-target" hcloud_load_balancer_name: "{{hcloud_prefix}}-load_balancer-target" +hcloud_testing_ip: "176.9.59.39" diff --git a/tests/integration/targets/hcloud_load_balancer_target/tasks/main.yml b/tests/integration/targets/hcloud_load_balancer_target/tasks/main.yml index 65fb9b0..3375668 100644 --- a/tests/integration/targets/hcloud_load_balancer_target/tasks/main.yml +++ b/tests/integration/targets/hcloud_load_balancer_target/tasks/main.yml @@ -78,6 +78,36 @@ that: - result is changed +- name: test create label_selector target + hcloud_load_balancer_target: + type: "label_selector" + load_balancer: "{{hcloud_load_balancer_name}}" + label_selector: "application=backend" + state: present + register: load_balancer_target +- name: verify create label_selector target + assert: + that: + - load_balancer_target is changed + - load_balancer_target.hcloud_load_balancer_target.type == "label_selector" + - load_balancer_target.hcloud_load_balancer_target.label_selector == "application=backend" + - load_balancer_target.hcloud_load_balancer_target.load_balancer == hcloud_load_balancer_name + +- name: test create ip target + hcloud_load_balancer_target: + type: "ip" + load_balancer: "{{hcloud_load_balancer_name}}" + ip: "{{hcloud_testing_ip}}" + state: present + register: load_balancer_target +- name: verify create ip target + assert: + that: + - load_balancer_target is changed + - load_balancer_target.hcloud_load_balancer_target.type == "ip" + - load_balancer_target.hcloud_load_balancer_target.ip == hcloud_testing_ip + - load_balancer_target.hcloud_load_balancer_target.load_balancer == hcloud_load_balancer_name + - name: cleanup load_balancer hcloud_load_balancer: name: "{{ hcloud_load_balancer_name }}" diff --git a/tests/utils/gitlab/gitlab.sh b/tests/utils/gitlab/gitlab.sh index a23b74d..62eb8e0 100755 --- a/tests/utils/gitlab/gitlab.sh +++ b/tests/utils/gitlab/gitlab.sh @@ -56,7 +56,7 @@ retry ansible-galaxy -vvv collection install community.general retry ansible-galaxy -vvv collection install ansible.netcommon retry ansible-galaxy -vvv collection install community.internal_test_tools retry pip install netaddr --disable-pip-version-check -retry python -m pip install hcloud +retry pip install hcloud # END: HACK export PYTHONIOENCODING='utf-8'