From 54894c14dce863689447e4644719c06a39921652 Mon Sep 17 00:00:00 2001 From: Lila Yasin Date: Fri, 30 Jun 2023 16:53:58 -0400 Subject: [PATCH] Hop node AWX Collection Updates (#14153) Add hop node support to awx collections - add peers and peers_from_control_nodes fields - show new node_type "hop" - add tests for adding hop nodes via collections Co-authored-by: Seth Foster --- awx_collection/plugins/modules/instance.py | 26 ++++++++- awx_collection/test/awx/test_instance.py | 53 +++++++++++++++++ .../targets/instance/tasks/main.yml | 57 +++++++++++++++++++ 3 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 awx_collection/test/awx/test_instance.py diff --git a/awx_collection/plugins/modules/instance.py b/awx_collection/plugins/modules/instance.py index 049dd976dd..432dca4a39 100644 --- a/awx_collection/plugins/modules/instance.py +++ b/awx_collection/plugins/modules/instance.py @@ -47,6 +47,7 @@ options: - Role that this node plays in the mesh. choices: - execution + - hop required: False type: str node_state: @@ -62,6 +63,18 @@ options: - Port that Receptor will listen for incoming connections on. required: False type: int + peers: + description: + - List of peers to connect outbound to. Only configurable for hop and execution nodes. + - To remove all current peers, set value to an empty list, []. + required: False + type: list + elements: str + peers_from_control_nodes: + description: + - If enabled, control plane nodes will automatically peer to this node. + required: False + type: bool extends_documentation_fragment: awx.awx.auth ''' @@ -88,9 +101,11 @@ def main(): capacity_adjustment=dict(type='float'), enabled=dict(type='bool'), managed_by_policy=dict(type='bool'), - node_type=dict(type='str', choices=['execution']), + node_type=dict(type='str', choices=['execution', 'hop']), node_state=dict(type='str', choices=['deprovisioning', 'installed']), listener_port=dict(type='int'), + peers=dict(required=False, type='list', elements='str'), + peers_from_control_nodes=dict(required=False, type='bool'), ) # Create a module for ourselves @@ -104,7 +119,8 @@ def main(): node_type = module.params.get('node_type') node_state = module.params.get('node_state') listener_port = module.params.get('listener_port') - + peers = module.params.get('peers') + peers_from_control_nodes = module.params.get('peers_from_control_nodes') # Attempt to look up an existing item based on the provided data existing_item = module.get_one('instances', name_or_id=hostname) @@ -122,6 +138,12 @@ def main(): new_fields['node_state'] = node_state if listener_port is not None: new_fields['listener_port'] = listener_port + if peers is not None: + new_fields['peers'] = peers + if peers is None: + peers = [''] + if peers_from_control_nodes is not None: + new_fields['peers_from_control_nodes'] = peers_from_control_nodes module.create_or_update_if_needed( existing_item, diff --git a/awx_collection/test/awx/test_instance.py b/awx_collection/test/awx/test_instance.py new file mode 100644 index 0000000000..2c8eda1f56 --- /dev/null +++ b/awx_collection/test/awx/test_instance.py @@ -0,0 +1,53 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import pytest + +from awx.main.models import Instance +from django.test.utils import override_settings + + +@pytest.mark.django_db +def test_peers_adding_and_removing(run_module, admin_user): + with override_settings(IS_K8S=True): + result = run_module( + 'instance', + {'hostname': 'hopnode1', 'node_type': 'hop', 'peers_from_control_nodes': True, 'node_state': 'installed', 'listener_port': 27199}, + admin_user, + ) + assert result['changed'] + + hop_node_1 = Instance.objects.get(pk=result.get('id')) + + assert hop_node_1.peers_from_control_nodes == True + assert hop_node_1.node_type == 'hop' + + result = run_module( + 'instance', + {'hostname': 'hopnode2', 'node_type': 'hop', 'peers_from_control_nodes': True, 'node_state': 'installed', 'listener_port': 27199}, + admin_user, + ) + assert result['changed'] + + hop_node_2 = Instance.objects.get(pk=result.get('id')) + + result = run_module( + 'instance', + {'hostname': 'executionnode', 'node_type': 'execution', 'node_state': 'installed', 'listener_port': 27199, 'peers': ['hopnode1', 'hopnode2']}, + admin_user, + ) + assert result['changed'] + + execution_node = Instance.objects.get(pk=result.get('id')) + + assert set(execution_node.peers.all()) == {hop_node_1, hop_node_2} + + result = run_module( + 'instance', + {'hostname': 'executionnode', 'node_type': 'execution', 'node_state': 'installed', 'listener_port': 27199, 'peers': []}, + admin_user, + ) + + assert result['changed'] + assert set(execution_node.peers.all()) == set() diff --git a/awx_collection/tests/integration/targets/instance/tasks/main.yml b/awx_collection/tests/integration/targets/instance/tasks/main.yml index 36ff0a138e..798db734df 100644 --- a/awx_collection/tests/integration/targets/instance/tasks/main.yml +++ b/awx_collection/tests/integration/targets/instance/tasks/main.yml @@ -70,3 +70,60 @@ - "{{ hostname3 }}" when: IS_K8S + +- block: + - name: Create hop node 1 + awx.awx.instance: + hostname: hopnode1 + node_type: hop + node_state: installed + listener_port: 27199 + peers_from_control_nodes: True + register: result + + - assert: + that: + - result is changed + + - name: Create hop node 2 + awx.awx.instance: + hostname: hopnode2 + node_type: hop + node_state: installed + listener_port: 27199 + peers_from_control_nodes: True + register: result + + - assert: + that: + - result is changed + + - name: Create execution node + awx.awx.instance: + hostname: executionnode + node_type: execution + node_state: installed + listener_port: 27199 + peers: + - "hopnode1" + - "hopnode2" + register: result + + - assert: + that: + - result is changed + + - name: Remove execution node peers + awx.awx.instance: + hostname: executionnode + node_type: execution + node_state: installed + listener_port: 27199 + peers: [] + register: result + + - assert: + that: + - result is changed + + when: IS_K8S