diff --git a/changelogs/fragments/9101-jenkins_node-add-launch_ssh-option.yaml b/changelogs/fragments/9101-jenkins_node-add-launch_ssh-option.yaml
new file mode 100644
index 00000000000..4a8e2e39af4
--- /dev/null
+++ b/changelogs/fragments/9101-jenkins_node-add-launch_ssh-option.yaml
@@ -0,0 +1,2 @@
+minor_changes:
+ - jenkins_node - implement ``launch_ssh`` option which will configure a node to use the SSH launcher if specified and common suboptions (https://github.com/ansible-collections/community.general/pull/9101).
diff --git a/plugins/modules/jenkins_node.py b/plugins/modules/jenkins_node.py
index 9406eab4c5b..571f1d2b044 100644
--- a/plugins/modules/jenkins_node.py
+++ b/plugins/modules/jenkins_node.py
@@ -75,6 +75,62 @@
possible. In this case, a warning will be issued.
type: str
version_added: 10.0.0
+ launch_ssh:
+ description:
+ - When specified, sets the Jenkins node to launch by SSH.
+ type: dict
+ suboptions:
+ host:
+ description:
+ - When specified, sets the SSH host name.
+ type: str
+ port:
+ description:
+ - When specified, sets the SSH port number.
+ type: int
+ credentials_id:
+ description:
+ - When specified, sets the Jenkins credential used for SSH authentication by
+ its ID.
+ type: str
+ host_key_verify_none:
+ description:
+ - When set, sets the SSH host key non-verifying strategy.
+ type: bool
+ choices:
+ - true
+ host_key_verify_known_hosts:
+ description:
+ - When set, sets the SSH host key known hosts file verification strategy.
+ type: bool
+ choices:
+ - true
+ host_key_verify_provided:
+ description:
+ - When specified, sets the SSH host key manually provided verification strategy.
+ type: dict
+ suboptions:
+ algorithm:
+ description:
+ - Key type, for example V(ssh-rsa).
+ type: str
+ required: true
+ key:
+ description:
+ - Key value.
+ type: str
+ required: true
+ host_key_verify_trusted:
+ description:
+ - When specified, sets the SSH host key manually trusted verification strategy.
+ type: dict
+ suboptions:
+ allow_initial:
+ description:
+ - When specified, enables or disables the requiring manual verification of
+ the first connected host for this node.
+ type: bool
+ version_added: 10.1.0
'''
EXAMPLES = '''
@@ -101,12 +157,23 @@
- label-2
- label-3
-- name: Set Jenkins node offline with offline message.
+- name: Set Jenkins node offline with offline message
community.general.jenkins_node:
name: my-node
state: disabled
offline_message: >
This node is offline for some reason.
+
+- name: >
+ Set Jenkins node to launch via SSH using stored credential and trust host key on
+ initial connection
+ community.general.jenkins_node:
+ name: my-node
+ launch_ssh:
+ host: my-node.test
+ credentials_id: deaddead-dead-dead-dead-deaddeaddead
+ host_key_verify_trusted:
+ allow_initial: yes
'''
RETURN = '''
@@ -155,6 +222,7 @@
import sys
import traceback
+from abc import abstractmethod
from xml.etree import ElementTree as et
from ansible.module_utils.basic import AnsibleModule
@@ -172,6 +240,376 @@
IS_PYTHON_2 = sys.version_info[0] <= 2
+if sys.version_info[0] <= 3 or sys.version_info[1] < 8:
+ class cached_property(object): # noqa
+ def __init__(self, func):
+ self.func = func
+
+ def __get__(self, instance, cls):
+ if instance is None:
+ return self
+
+ value = self.func(instance)
+ setattr(instance, self.func.__name__, value)
+
+ return value
+else:
+ from functools import cached_property
+
+
+def bool_to_text(value): # type: (bool) -> str
+ return "true" if value else "false"
+
+
+def text_to_bool(text): # type: (str) -> bool
+ if text == "true":
+ return True
+
+ if text == "false":
+ return False
+
+ raise ValueError("unexpected boolean text value '{}'".format(text))
+
+
+class Element:
+ def __init__(self, root): # type: (et.Element | None) -> None
+ self.root = root
+
+ def get(self, key): # type: (str) -> str | None
+ return None if self.root is None else self.root.get(key)
+
+ def set(self, key, value): # type: (str, str) -> None
+ self.root.set(key, value)
+
+ def find(self, path): # type: (str) -> et.Element | None
+ return None if self.root is None else self.root.find(path)
+
+ def remove(self, element): # type: (et.Element) -> None
+ self.root.remove(element)
+
+ def append(self, element): # type: (et.Element) -> None
+ self.root.append(element)
+
+ @property
+ def class_(self): # type: () -> str | None
+ return self.get("class")
+
+ @class_.setter
+ def class_(self, value): # (str) -> None
+ self.set("class", value)
+
+
+class Config:
+ @abstractmethod
+ def init(self): # type: () -> et.Element
+ """Initialize XML element from config.
+
+ Returns:
+ Initialized XML element.
+ """
+ raise NotImplementedError
+
+ @abstractmethod
+ def update(self, root): # (et.Element) -> bool
+ """Update XML element with config.
+
+ Args:
+ root: XML element to update.
+
+ Returns:
+ True if was updated, False otherwise.
+ """
+ raise NotImplementedError
+
+
+class LauncherElement(Element):
+ TAG = "launcher"
+
+
+class LauncherConfig(Config):
+ CLASS = ""
+
+
+class SSHHostKeyVerifyElement(Element):
+ TAG = "sshHostKeyVerificationStrategy"
+
+
+class SSHHostKeyVerifyConfig(Config):
+ CLASS = ""
+
+ def init(self): # type: () -> et.Element
+ root = et.Element(SSHHostKeyVerifyElement.TAG)
+
+ Element(root).class_ = self.CLASS
+
+ return root
+
+ def update(self, root): # type: (et.Element) -> bool
+ class_ = Element(root).class_
+
+ if class_ != self.CLASS:
+ raise ValueError("unexpected class {}: expected {}".format(class_, self.CLASS))
+
+ return False
+
+
+class KnownHostsSSHHostKeyVerifyConfig(SSHHostKeyVerifyConfig):
+ CLASS = "hudson.plugins.sshslaves.verifiers.KnownHostsFileKeyVerificationStrategy"
+
+
+class NoneSSHHostKeyVerifyConfig(SSHHostKeyVerifyConfig):
+ CLASS = "hudson.plugins.sshslaves.verifiers.NonVerifyingKeyVerificationStrategy"
+
+
+class TrustedSSHHostKeyVerifyElement(SSHHostKeyVerifyElement):
+ @property
+ def allow_initial(self): # type: () -> et.Element | None
+ return self.find("requireInitialManualTrust")
+
+
+class TrustedSSHHostKeyVerifyConfig(SSHHostKeyVerifyConfig):
+ CLASS = "hudson.plugins.sshslaves.verifiers.ManuallyTrustedKeyVerificationStrategy"
+
+ TEMPLATE = """
+
+ false
+
+""".strip()
+
+ def __init__(self, allow_initial=None): # type: (bool | None) -> None
+ self.allow_initial = allow_initial
+
+ def init(self): # type: () -> et.Element
+ return et.fromstring(self.TEMPLATE)
+
+ def update(self, root): # type: (et.Element) -> bool
+ super(TrustedSSHHostKeyVerifyConfig, self).update(root)
+
+ wrapper = TrustedSSHHostKeyVerifyElement(root)
+ updated = False
+
+ if self.allow_initial is not None:
+ if wrapper.allow_initial.text != bool_to_text(self.allow_initial):
+ wrapper.allow_initial.text = bool_to_text(self.allow_initial)
+ updated = True
+
+ return updated
+
+
+class ProvidedSSHHostKeyVerifyElement(SSHHostKeyVerifyElement):
+ class Key(Element):
+ TAG = "key"
+
+ @property
+ def algorithm(self): # type: () -> et.Element | None
+ return self.find("algorithm")
+
+ @property
+ def key(self): # type: () -> et.Element | None
+ return self.find("key")
+
+ @property
+ def key(self): # type: () -> ProvidedSSHHostKeyVerifyElement.Key | None
+ root = self.find('key')
+
+ if root is None:
+ return None
+
+ return ProvidedSSHHostKeyVerifyElement.Key(root)
+
+
+class ProvidedSSHHostKeyVerifyConfig(KnownHostsSSHHostKeyVerifyConfig):
+ CLASS = "hudson.plugins.sshslaves.verifiers.ManuallyProvidedKeyVerificationStrategy"
+
+ TEMPLATE = """
+
+
+
+
+
+
+""".strip()
+
+ def __init__(self, algorithm, key): # type: (str, str) -> None
+ self.algorithm = algorithm
+ self.key = key
+
+ def init(self): # type: () -> et.Element
+ return et.fromstring(self.TEMPLATE)
+
+ def update(self, root): # type: (et.Element) -> bool
+ super(ProvidedSSHHostKeyVerifyConfig, self).update(root)
+
+ wrapper = ProvidedSSHHostKeyVerifyElement(root)
+ updated = False
+
+ if wrapper.key.algorithm.text != self.algorithm:
+ wrapper.key.algorithm.text = self.algorithm
+ updated = True
+
+ if wrapper.key.key.text != self.key:
+ wrapper.key.key.text = self.key
+ updated = True
+
+ return updated
+
+
+class SSHLauncherElement(LauncherElement):
+ @property
+ def host(self): # type: () -> et.Element | None
+ return self.find("host")
+
+ @host.setter
+ def host(self, element):
+ if self.host is not None:
+ self.remove(self.host)
+
+ self.append(element)
+
+ def ensure_host(self):
+ if self.host is None:
+ return et.SubElement(self.root, "host")
+
+ return self.host
+
+ @property
+ def port(self):
+ return self.find("port")
+
+ @property
+ def credentials_id(self):
+ return self.find("credentialsId")
+
+ def ensure_credentials_id(self):
+ if self.credentials_id is None:
+ return et.SubElement(self.root, "credentialsId")
+
+ return self.credentials_id
+
+ @property
+ def host_key_verify(self):
+ return self.find(SSHHostKeyVerifyElement.TAG)
+
+ @host_key_verify.setter
+ def host_key_verify(self, element):
+ if self.host_key_verify is not None:
+ self.remove(self.host_key_verify)
+
+ self.append(element)
+
+
+class SSHLauncherConfig(Config):
+ CLASS = "hudson.plugins.sshslaves.SSHLauncher"
+
+ def __init__(
+ self,
+ host=None,
+ port=None,
+ credentials_id=None,
+ host_key_verify=None,
+ ): # type: (str | None, int | None, str | None, SSHHostKeyVerifyConfig | None) -> None
+ self.host = host
+ self.port = port
+ self.credentials_id = credentials_id
+ self.host_key_verify = host_key_verify
+
+ TEMPLATE = """
+
+ 22
+
+ 60
+ 10
+ 15
+ true
+
+""".strip()
+
+ def init(self): # type: () -> et.Element
+ root = et.fromstring(self.TEMPLATE)
+
+ wrapper = SSHLauncherElement(root)
+
+ if self.host_key_verify is not None:
+ wrapper.host_key_verify = self.host_key_verify.init()
+
+ return root
+
+ def update(self, root): # type: (et.Element) -> bool
+ wrapper = SSHLauncherElement(root)
+ updated = False
+
+ if self.host is not None:
+ if wrapper.host is None:
+ wrapper.ensure_host()
+ updated = True
+
+ if wrapper.host.text != self.host:
+ wrapper.host.text = self.host
+ updated = True
+
+ if self.port is not None:
+ if wrapper.port.text != str(self.port):
+ wrapper.port.text = str(self.port)
+ updated = True
+
+ if self.credentials_id is not None:
+ if wrapper.credentials_id is None:
+ wrapper.ensure_credentials_id()
+ updated = True
+
+ if wrapper.credentials_id.text != self.credentials_id:
+ wrapper.credentials_id.text = self.credentials_id
+ updated = True
+
+ if self.host_key_verify is not None:
+ if Element(wrapper.host_key_verify).class_ != self.host_key_verify.CLASS:
+ wrapper.host_key_verify = self.host_key_verify.init()
+ updated = True
+
+ if self.host_key_verify.update(wrapper.host_key_verify):
+ updated = True
+
+ return updated
+
+
+def ssh_host_key_verify_config(args): # type: (dict) -> SSHHostKeyVerifyConfig | None
+ """Get SSH host key verify config from args.
+
+ Args:
+ args: SSH launcher args.
+
+ Returns:
+ SSH host key verify config.
+ """
+ if args.get("host_key_verify_none"):
+ return NoneSSHHostKeyVerifyConfig()
+
+ if args.get('host_key_verify_known_hosts'):
+ return KnownHostsSSHHostKeyVerifyConfig()
+
+ if args.get("host_key_verify_provided"):
+ return ProvidedSSHHostKeyVerifyConfig(
+ args["host_key_verify_provided"]["algorithm"],
+ args["host_key_verify_provided"]["key"],
+ )
+
+ if args.get("host_key_verify_trusted"):
+ return TrustedSSHHostKeyVerifyConfig(
+ allow_initial=args["host_key_verify_trusted"].get("allow_initial")
+ )
+
+ return None
+
+
+def ssh_launcher_args_config(args): # type: (dict) -> SSHLauncherConfig
+ return SSHLauncherConfig(
+ host=args.get("host"),
+ port=args.get("port"),
+ credentials_id=args.get("credentials_id"),
+ host_key_verify=ssh_host_key_verify_config(args),
+ )
+
+
class JenkinsNode:
def __init__(self, module):
self.module = module
@@ -211,6 +649,13 @@ def __init__(self, module):
'warnings': [],
}
+ @cached_property
+ def launch(self): # type: () -> LauncherConfig | None
+ if self.module.params["launch_ssh"]:
+ return ssh_launcher_args_config(self.module.params["launch_ssh"])
+
+ return None
+
def get_jenkins_instance(self):
try:
if self.user and self.token:
@@ -222,6 +667,25 @@ def get_jenkins_instance(self):
except Exception as e:
self.module.fail_json(msg='Unable to connect to Jenkins server, %s' % to_native(e))
+ def configure_launch(self, config): # type: (et.Element) -> bool
+ configured = False
+
+ launcher = config.find(LauncherElement.TAG)
+
+ if Element(launcher).class_ != self.launch.CLASS:
+ if launcher is not None:
+ config.remove(launcher)
+
+ launcher = self.launch.init()
+ config.append(launcher)
+
+ configured = True
+
+ if self.launch.update(launcher):
+ configured = True
+
+ return configured
+
def configure_node(self, present):
if not present:
# Node would only not be present if in check mode and if not present there
@@ -235,6 +699,10 @@ def configure_node(self, present):
data = self.instance.get_node_config(self.name)
root = et.fromstring(data)
+ if self.launch is not None:
+ if self.configure_launch(root):
+ configured = True
+
if self.num_executors is not None:
elem = root.find('numExecutors')
if elem is None:
@@ -467,6 +935,41 @@ def main():
num_executors=dict(type='int'),
labels=dict(type='list', elements='str'),
offline_message=dict(type='str'),
+ launch_ssh=dict(
+ type='dict',
+ options=dict(
+ host=dict(type='str'),
+ port=dict(type='int'),
+ credentials_id=dict(type='str'),
+ host_key_verify_none=dict(type='bool', choices=[True]),
+ host_key_verify_known_hosts=dict(type='bool', choices=[True]),
+ host_key_verify_provided=dict(
+ type='dict',
+ options=dict(
+ algorithm=dict(type='str', required=True),
+ key=dict(
+ type='str',
+ required=True,
+ no_log=False, # Is public key.
+ ),
+ ),
+ no_log=False, # Is not sensitive.
+ ),
+ host_key_verify_trusted=dict(
+ type='dict',
+ options=dict(
+ allow_initial=dict(type='bool'),
+ ),
+ no_log=False, # Is not sensitive.
+ ),
+ ),
+ mutually_exclusive=[[
+ 'host_key_verify_none',
+ 'host_key_verify_known_hosts',
+ 'host_key_verify_provided',
+ 'host_key_verify_trusted',
+ ]],
+ ),
),
supports_check_mode=True,
)
diff --git a/tests/unit/plugins/modules/test_jenkins_node.py b/tests/unit/plugins/modules/test_jenkins_node.py
index 7c2634744d4..d7640c8786a 100644
--- a/tests/unit/plugins/modules/test_jenkins_node.py
+++ b/tests/unit/plugins/modules/test_jenkins_node.py
@@ -841,3 +841,654 @@ def test_set_offline_message_when_not_equal_offline(get_instance, instance):
assert not instance.disable_node.called
assert result.value["changed"] is False
+
+
+class TestConfigureLaunchSSH:
+ @fixture(autouse=True)
+ def node_exists(self, get_instance, instance):
+ instance.node_exists.return_value = True
+
+ def test_configure_when_launcher_is_not_ssh(self, instance):
+ instance.get_node_config.return_value = """
+
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {},
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert_xml_equal(instance.reconfig_node.call_args[0][1], """
+
+ {}
+
+""".format(jenkins_node.SSHLauncherConfig.TEMPLATE))
+
+ assert result.value["configured"] is True
+
+ def test_configure_when_not_configured(self, instance):
+ instance.get_node_config.return_value = """
+
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {},
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert not instance.reconfig_node.called
+
+ assert result.value["configured"] is False
+
+ def test_configure_port_when_not_equal(self, instance):
+ instance.get_node_config.return_value = """
+
+
+ 22
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "port": 23,
+ },
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert_xml_equal(instance.reconfig_node.call_args[0][1], """
+
+
+ 23
+
+
+""")
+
+ assert result.value["configured"] is True
+
+ def test_configure_port_when_equal(self, instance):
+ instance.get_node_config.return_value = """
+
+
+ 22
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "port": 22,
+ }
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert not instance.reconfig_node.called
+
+ assert result.value["configured"] is False
+
+ def test_configure_host_when_unset(self, instance):
+ instance.get_node_config.return_value = """
+
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "host": "my-node.test",
+ }
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert_xml_equal(
+ instance.reconfig_node.call_args[0][1], """
+
+
+ my-node.test
+
+
+""")
+
+ assert result.value["configured"] is True
+
+ def test_configure_host_when_equal(self, instance):
+ instance.get_node_config.return_value = """
+
+
+ my-node.test
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "host": "my-node.test",
+ }
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert not instance.reconfig_node.called
+
+ assert result.value["configured"] is False
+
+ def test_configure_host_when_not_equal(self, instance):
+ instance.get_node_config.return_value = """
+
+
+ my-node.test
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "host": "my-other-node.test",
+ }
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert_xml_equal(instance.reconfig_node.call_args[0][1], """
+
+
+ my-other-node.test
+
+
+""")
+
+ assert result.value["configured"] is True
+
+ def test_configure_credentials_id_when_unset(self, instance):
+ instance.get_node_config.return_value = """
+
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "credentials_id": "deaddead-dead-dead-dead-deaddeaddead",
+ },
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert_xml_equal(instance.reconfig_node.call_args[0][1], """
+
+
+ deaddead-dead-dead-dead-deaddeaddead
+
+
+""")
+
+ assert result.value["configured"] is True
+
+ def test_configure_credentials_id_when_equal(self, instance):
+ instance.get_node_config.return_value = """
+
+
+ deaddead-dead-dead-dead-deaddeaddead
+
+
+"""
+
+ set_module_args(
+ {
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "credentials_id": "deaddead-dead-dead-dead-deaddeaddead",
+ },
+ }
+ )
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert not instance.reconfig_node.called
+
+ assert result.value["configured"] is False
+
+ def test_configure_credentials_id_when_not_equal(self, instance):
+ instance.get_node_config.return_value = """
+
+
+ deaddead-dead-dead-dead-deaddeaddead
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "credentials_id": "deaddead-dead-dead-dead-deaddeadbeef",
+ },
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert_xml_equal(instance.reconfig_node.call_args[0][1], """
+
+
+ deaddead-dead-dead-dead-deaddeadbeef
+
+
+""")
+
+ assert result.value["configured"] is True
+
+ def test_configure_host_key_verify_known_hosts_when_host_key_verify_is_not_known_hosts(
+ self, instance
+ ):
+ instance.get_node_config.return_value = """
+
+
+
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "host_key_verify_known_hosts": True,
+ },
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert_xml_equal(instance.reconfig_node.call_args[0][1], """
+
+
+
+
+
+""")
+
+ assert result.value["configured"] is True
+
+ def test_configure_host_key_verify_none_when_host_key_verify_is_not_none(
+ self, instance
+ ):
+ instance.get_node_config.return_value = """
+
+
+
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "host_key_verify_none": True,
+ },
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert_xml_equal(instance.reconfig_node.call_args[0][1], """
+
+
+
+
+
+""")
+
+ assert result.value["configured"] is True
+
+ def test_configure_host_key_verify_trusted_when_host_key_verify_is_not_trusted(
+ self, instance
+ ):
+ instance.get_node_config.return_value = """
+
+
+
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "host_key_verify_trusted": {},
+ },
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert_xml_equal(instance.reconfig_node.call_args[0][1], """
+
+
+
+ false
+
+
+
+""")
+
+ assert result.value["configured"] is True
+
+ def test_configure_host_key_verify_trusted_when_not_configured(self, instance):
+ instance.get_node_config.return_value = """
+
+
+
+ false
+
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "host_key_verify_trusted": {},
+ },
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert not instance.reconfig_node.called
+
+ assert result.value["configured"] is False
+
+ def test_configure_host_key_verify_trusted_allow_initial_when_equal(self, instance):
+ instance.get_node_config.return_value = """
+
+
+
+ false
+
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "host_key_verify_trusted": {
+ "allow_initial": False,
+ },
+ },
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert not instance.reconfig_node.called
+
+ assert result.value["configured"] is False
+
+ def test_configure_host_key_verify_trusted_allow_initial_when_not_equal(self, instance):
+ instance.get_node_config.return_value = """
+
+
+
+ false
+
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "host_key_verify_trusted": {
+ "allow_initial": True,
+ },
+ },
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert_xml_equal(instance.reconfig_node.call_args[0][1], """
+
+
+
+ true
+
+
+
+""")
+
+ assert result.value["configured"] is True
+
+ #######
+
+ def test_configure_host_key_verify_provided_when_host_key_verify_is_not_provided(
+ self, instance
+ ):
+ instance.get_node_config.return_value = """
+
+
+
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "host_key_verify_provided": {
+ "algorithm": "ssh-ed25519",
+ "key": "AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl",
+ },
+ },
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert_xml_equal(instance.reconfig_node.call_args[0][1], """
+
+
+
+
+ ssh-ed25519
+ AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
+
+
+
+
+""")
+
+ assert result.value["configured"] is True
+
+ def test_configure_host_key_verify_provided_algorithm_when_equal(self, instance):
+ instance.get_node_config.return_value = """
+
+
+
+
+ ssh-ed25519
+ AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
+
+
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "host_key_verify_provided": {
+ "algorithm": "ssh-ed25519",
+ "key": "AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl",
+ },
+ },
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert not instance.reconfig_node.called
+
+ assert result.value["configured"] is False
+
+ def test_configure_host_key_verify_provided_algorithm_when_not_equal(self, instance):
+ instance.get_node_config.return_value = """
+
+
+
+
+ ssh-ed25519
+ AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
+
+
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "host_key_verify_provided": {
+ "algorithm": "ssh-rsa",
+ "key": "AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl",
+ },
+ },
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert_xml_equal(instance.reconfig_node.call_args[0][1], """
+
+
+
+
+ ssh-rsa
+ AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
+
+
+
+
+""")
+
+ assert result.value["configured"] is True
+
+ def test_configure_host_key_verify_provided_key_when_equal(self, instance):
+ instance.get_node_config.return_value = """
+
+
+
+
+ ssh-ed25519
+ AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
+
+
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "host_key_verify_provided": {
+ "algorithm": "ssh-ed25519",
+ "key": "AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl",
+ },
+ },
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert not instance.reconfig_node.called
+
+ assert result.value["configured"] is False
+
+ def test_configure_host_key_verify_provided_key_when_not_equal(self, instance):
+ instance.get_node_config.return_value = """
+
+
+
+
+ ssh-ed25519
+ AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
+
+
+
+
+"""
+
+ set_module_args({
+ "name": "my-node",
+ "state": "present",
+ "launch_ssh": {
+ "host_key_verify_provided": {
+ "algorithm": "ssh-ed25519",
+ "key": "AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJk",
+ },
+ },
+ })
+
+ with raises(AnsibleExitJson) as result:
+ jenkins_node.main()
+
+ assert_xml_equal(instance.reconfig_node.call_args[0][1], """
+
+
+
+
+ ssh-ed25519
+ AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJk
+
+
+
+
+""")
+
+ assert result.value["configured"] is True