]> git.apps.os.sepia.ceph.com Git - ceph-build.git/commitdiff
examples/builder.yml, library/jenkins_node: support node update
authorDan Mick <dmick@redhat.com>
Sat, 15 Jul 2023 01:07:32 +0000 (18:07 -0700)
committerDan Mick <dmick@redhat.com>
Sat, 15 Jul 2023 01:10:04 +0000 (18:10 -0700)
Original implementation would only permit nodes to be created if they
did not exist.  Add support to update certain attributes of the node
with a run against extant nodes.  Only permit a few attribute updates.
This is primarily motivated by the desire to support maintaining node
labels with ansible.

Signed-off-by: Dan Mick <dmick@redhat.com>
ansible/examples/builder.yml
ansible/library/jenkins_node

index 74ecc0d3b3096c713f3cdb53096ea098ac063cea..251d0375f42874afc15723d9459362cd733f3684 100644 (file)
           - python3-pip
           - python3-venv
           - python3-virtualenv
+          - python3-xmltodict
       when:
         - ansible_os_family == "Debian"
         - ansible_distribution_major_version|int >= 18
           - python3-pip
           - python3-devel
           - python3-virtualenv
+          - python3-xmltodict
           - mock
           - podman
         container_service_name: podman
           - jq
           - python3-pip
           - python3-devel
+          - python3-xmltodict
           - podman
         container_service_name: podman
         container_certs_path: "/etc/containers/certs.d/{{ container_mirror }}"
           - python2-virtualenv
           - python3-pip
           - python3-virtualenv
+          - python3-xmltodict
           - rpm-build
           - rpmdevtools
           - tig
             remoteFS: '/home/{{ jenkins_user }}/build'
             executors: '{{ executors|default(1) }}'
             exclusive: true
-          tags: register
 
         - name: Download agent.jar
           get_url:
index e4d4ba4fa33d159411c327d8733afc6d91f90598..792a027cf979a2dd3cbbad3c7292d130038eb947 100644 (file)
@@ -100,6 +100,8 @@ EXAMPLES = """
   jenkins_node: uri={{ jenkins_uri }} username={{ user }} password={{ password }}
            name={{ node_name }} operation=delete
 """
+import ast
+import xmltodict
 
 HAS_JENKINS_API = True
 try:
@@ -124,7 +126,49 @@ def translate_params(params):
     return sanitized
 
 
-def create(uri, user, password, name, **kw):
+
+#
+# it's not clear to me how ansible passes lists as lists,
+# so convert them if necessary
+#
+def maybe_convert_string_to_list(v):
+    if isinstance(v, basestring):
+        try:
+            v = ast.literal_eval(v)
+        except Exception:
+            # no, really; ast makes a best effort, and if it fails,
+            # we didn't need its conversion
+            pass
+    return v
+
+def sanitize_update_params(kw):
+
+    def translate_labels(labels):
+        return 'label', ' '.join(labels)
+
+    # this list may be smaller than it needs to be, but these are
+    # the only ones I want to support for now
+    VALID_UPDATE_PARAMS = {
+        # value, if any, is function returning new key and value to use
+        'name': None,
+        'remoteFS': None,
+        'numExecutors': None,
+        'labels': translate_labels,
+    }
+    update_kws = dict()
+    invalid = list()
+    for k, v in kw.items():
+        v = maybe_convert_string_to_list(v)
+        if k not in VALID_UPDATE_PARAMS:
+            invalid.append(k)
+        else:
+            if VALID_UPDATE_PARAMS[k]:
+                k, v = VALID_UPDATE_PARAMS[k](v)
+            update_kws[k] = v
+    return invalid, update_kws
+
+
+def create_or_modify(uri, user, password, name, **kw):
     launcher_params = {}
     launcher_params['credentialsId'] = kw.pop('credentialsId', None)
     launcher_params['host'] = kw.pop('host', None)
@@ -132,11 +176,24 @@ def create(uri, user, password, name, **kw):
         launcher_params = {}
     params = translate_params(kw)
     j = _jenkins(uri, user, password)
+
     if j.node_exists(name):
-        return False, "Failed to create node '%s' - already exists." % name
-    j.create_node(name, launcher_params=launcher_params, **params)
-    if not j.node_exists(name):
-        return False, "Failed to create node '%s'." % name
+        # if it already exists, we can reconfigure it
+
+        # select valid config keys, transform a few
+        invalid, params = sanitize_update_params(params)
+
+        config = xmltodict.parse(j.get_node_config(name))
+        for k, v in params.items():
+            config['slave'][k] = v
+        new_xconfig = xmltodict.unparse(config)
+
+        j.reconfig_node(name, new_xconfig)
+    else:
+        j.create_node(name, launcher_params=launcher_params, **params)
+        if not j.node_exists(name):
+            return False, "Failed to create node '%s'." % name
+
     return True, None
 
 
@@ -204,7 +261,7 @@ def main():
     launcher = module.params.get('launcher', 'hudson.plugins.sshslaves.SSHLauncher')
 
     api_calls = {
-        'create': create,
+        'create': create_or_modify,
         'delete': delete,
         'enable': enable,
         'disable': disable