From d779302488d7d5b94bd454a102cf7a687c0d2a9d Mon Sep 17 00:00:00 2001 From: Dougal Seeley Date: Tue, 18 Jan 2022 18:56:31 +0000 Subject: [PATCH] Convert to an ansible-galaxy collection. Add Jenkinsfiles to release. --- .gitignore | 1 + LICENSE | 2 +- README.md | 4 +- galaxy.yml | 32 +++++ jenkinsfiles/Jenkinsfile_release_tag | 119 ++++++++++++++++++ jenkinsfiles/Jenkinsfile_release_tag__auto | 7 ++ meta/runtime.yml | 1 + .../modules}/esxifree_guest.py | 0 .../modules}/esxifree_guest_info.py | 0 9 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 galaxy.yml create mode 100644 jenkinsfiles/Jenkinsfile_release_tag create mode 100644 jenkinsfiles/Jenkinsfile_release_tag__auto create mode 100644 meta/runtime.yml rename {library => plugins/modules}/esxifree_guest.py (100%) rename {library => plugins/modules}/esxifree_guest_info.py (100%) diff --git a/.gitignore b/.gitignore index 485dee6..6c2c792 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .idea +*.tar.gz \ No newline at end of file diff --git a/LICENSE b/LICENSE index 3c642ec..1b4adf1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2020, Dougal Seeley +Copyright (c) 2022, Dougal Seeley All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index 406a68c..16c7257 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# esxifree_guest +# Ansible Collection - dseeley.esxifree_guest -This module can be used to create new ESXi virtual machines, including cloning from templates or other virtual machines. +This plugin can be used to create new ESXi virtual machines, including cloning from templates or other virtual machines. It does so using direct SOAP calls and Paramiko SSH to the host - without using the vSphere API - meaning it can be used on the free hypervisor. diff --git a/galaxy.yml b/galaxy.yml new file mode 100644 index 0000000..dc91735 --- /dev/null +++ b/galaxy.yml @@ -0,0 +1,32 @@ +namespace: dseeley + +name: esxifree_guest + +version: AUTOFILLED_BY_JENKINS + +readme: README.md + +authors: +- Dougal Seeley + +description: This plugin can be used to create new ESXi virtual machines, including cloning from templates or other virtual machines. + +#license: +#- BSD-3-Clause + +license_file: 'LICENSE' + +tags: ['aws', 'gcp', 'azure', 'storage', 'cluster', 'cloud', 'system'] + +dependencies: {} + +repository: https://github.com/dseeley/esxifree_guest + +documentation: https://github.com/dseeley/esxifree_guest/blob/master/README.md + +homepage: https://github.com/dseeley/esxifree_guest + +issues: https://github.com/dseeley/esxifree_guest/issues + +build_ignore: [ '.idea', '.gitignore' ] + diff --git a/jenkinsfiles/Jenkinsfile_release_tag b/jenkinsfiles/Jenkinsfile_release_tag new file mode 100644 index 0000000..ec4f230 --- /dev/null +++ b/jenkinsfiles/Jenkinsfile_release_tag @@ -0,0 +1,119 @@ +#!groovy +import groovy.json.JsonOutput + +def PROJECT_URL_DEFAULT = "https://github.com/dseeley/esxifree_guest" +def PROJECT_BRANCH_DEFAULT = "master" //Set the default git branch to use if we're not running an SCM job (e.g. if we're copying/pasting into a pipeline script) + +//This allows us to create our own Docker image for this specific use-case. Once it is built, it will not be rebuilt, so only adds delay the first time we use it. +def create_custom_image(image_name, params = "") { + // Create a lock to prevent building the same image in parallel + lock('IMAGEBUILDLOCK__' + env.NODE_NAME) { + def jenkins_username = sh(script: 'whoami', returnStdout: true).trim() + def jenkins_uid = sh(script: "id -u ${jenkins_username}", returnStdout: true).trim() + def jenkins_gid = sh(script: "id -g ${jenkins_username}", returnStdout: true).trim() + + def dockerfile = """ + FROM ubuntu:20.04 + ARG DEBIAN_FRONTEND=noninteractive + ENV JENKINS_HOME=${env.JENKINS_HOME} + ENV HOME=${env.JENKINS_HOME} + ENV PIPENV_VENV_IN_PROJECT=true + ENV TZ=Europe/London + RUN groupadd -g ${jenkins_gid} ${jenkins_username} && useradd -m -u ${jenkins_uid} -g ${jenkins_gid} -s /bin/bash ${jenkins_username} + ### Note: use pip to install pipenv (not apt) to avoid pypa/pipenv#2196 (when using PIPENV_VENV_IN_PROJECT) + RUN apt-get update \ + && apt-get install -y git iproute2 \ + python3-boto python3-boto3 python3-botocore python3-dev python3-distutils python3-dnspython python3-google-auth python3-googleapi python3-libcloud python3-jinja2 python3-jmespath python3-netaddr python3-paramiko python3-pip python3-ruamel.yaml python3-setuptools python3-wheel python3-xmltodict \ + && pip3 install pipenv ansible>=5.2.0 \ + && pip3 install -r \$(pip3 show ansible | grep ^Location | sed 's/Location: \\(.*\\)/\\1/')/ansible_collections/azure/azcollection/requirements-azure.txt + """.stripIndent() + + writeFile(file: "Dockerfile", text: dockerfile, encoding: "UTF-8") + custom_build = docker.build(image_name, params + "--network host .") + + return (custom_build) + } +} + + +properties([ + parameters([ + string(name: 'NEW_VERSION', defaultValue: "", description: "Specify either the version to be created (e.g.: v1.0.0), or 'next' to apply the next patch version."), + credentials(name: 'GIT_CREDS', credentialType: 'com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl', defaultValue: 'GITHUB_SVC_USER', description: 'Jenkins username/password credentials for GitHub', required: false), + booleanParam(name: 'UPDATE_GALAXY', defaultValue: true, description: 'Tick the box to also update Ansible-Galaxy repo') + ]) +]) + +node { + if (params.NEW_VERSION != "") { + stage('Setup Environment') { + sh 'printenv | sort' + echo "Params: $params" + + println("Checkout from SCM, or default if not a pipeline job") + try { + checkout scm + } catch (Exception e) { + println("scm not available: " + e.toString() + ", so checking out manually.") + checkout([$class: 'GitSCM', branches: [[name: "${PROJECT_BRANCH_DEFAULT}"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'WipeWorkspace']], submoduleCfg: [], userRemoteConfigs: [[credentialsId: params.GIT_CREDS ? params.GIT_CREDS : '', url: PROJECT_URL_DEFAULT]]]) + } + } + + + def docker_parent_net_str = "" + if (sh(script: 'grep -sq "docker\\|lxc" /proc/1/cgroup', returnStatus: true) == 0) { + println("Running in docker. Getting network to pass to docker-in-docker containers...") + def docker_parent_net_id = sh(script: 'docker inspect $(basename $(cat /proc/1/cpuset)) -f "{{ range .NetworkSettings.Networks }}{{println .NetworkID}}{{end}}" | head -n 1', returnStdout: true).trim() + docker_parent_net_str = "--network ${docker_parent_net_id}" + println("docker_parent_net_str: ${docker_parent_net_str}") + } + + /*** Create a custom docker image within this Jenkinsfile ***/ + create_custom_image("ubuntu_ansible", "").inside("--init ${docker_parent_net_str}") { + def new_tag_version = params.NEW_VERSION + stage('Create new version') { + withCredentials([usernamePassword(credentialsId: params.GIT_CREDS, usernameVariable: 'GIT_USER', passwordVariable: 'GIT_PASS')]) { + def apiUrlReleases = "https://api.github.com/repos/" + PROJECT_URL_DEFAULT.replaceFirst("^(http[s]?://[^/]+/)", "") + "/releases" + def latestReleaseQuery = ["curl", "-s", "-H", "Accept: application/json", "-H", "Content-type: application/json", "-H", "Authorization: token ${GIT_PASS}", "-X", "GET", "${apiUrlReleases}/latest"].execute().text.trim() + def latestRelease = readJSON text: "${latestReleaseQuery}" + if (params.NEW_VERSION == "next") { + String full_ver_str = latestRelease.tag_name ? latestRelease.tag_name : "v0.0.0" + String major_minor = full_ver_str.substring(0, full_ver_str.lastIndexOf('.') + 1) + String patch = full_ver_str.substring(full_ver_str.lastIndexOf('.') + 1) + new_tag_version = "${major_minor}${patch.toInteger() + 1}" + } + String tag_range = latestRelease.tag_name ? "${latestRelease.tag_name}..HEAD" : "" + new_tag_body = sh(returnStdout: true, script: "git log ${tag_range} --pretty=format:'
  • %H - %s
  • '").trim() + if (new_tag_body != "") { + def payload = JsonOutput.toJson(["tag_name": new_tag_version, "name": new_tag_version, "body": new_tag_body]) + def _dummyresponse = ["curl", "-s", "-H", "Accept: application/json", "-H", "Content-type: application/json", "-H", "Authorization: token ${GIT_PASS}", "-X", "POST", "-d", payload, apiUrlReleases].execute().text.trim() + echo "${new_tag_version} is now created" + } else { + currentBuild.result = 'ABORTED' + println("No change since last release") + } + } + } + + if (params.UPDATE_GALAXY == true && currentBuild.result != 'ABORTED') { + stage('Update ansible galaxy') { + withCredentials([string(credentialsId: "GALAXY_API_KEY", variable: 'GALAXY_API_KEY')]) { + sh 'sed -E -i "s|^version:.*|version: ' + new_tag_version.replaceFirst(/^\w?(.*)/, '$1') + '|" galaxy.yml' + def galaxyBuildResponse = sh(returnStdout: true, script: "ansible-galaxy collection build").trim() + def galaxyPublishResponse = sh(returnStdout: true, script: "ansible-galaxy collection publish ${galaxyBuildResponse.split(' ').last()} --api-key ${GALAXY_API_KEY} 2>&1 || true").trim() + println("galaxyPublishResponse: " + galaxyPublishResponse) + if (galaxyPublishResponse.contains("ERROR!")) { + if (galaxyPublishResponse.contains("already exists")) { + currentBuild.result = 'ABORTED' + } else { + currentBuild.result = 'FAILURE' + } + } + } + } + } + } + } else { + error "NEW_VERSION parameter not specified. Specify either the version to be created (e.g.: v1.0.0), or 'next' to apply the next patch version." + } +} \ No newline at end of file diff --git a/jenkinsfiles/Jenkinsfile_release_tag__auto b/jenkinsfiles/Jenkinsfile_release_tag__auto new file mode 100644 index 0000000..698d104 --- /dev/null +++ b/jenkinsfiles/Jenkinsfile_release_tag__auto @@ -0,0 +1,7 @@ +#!groovy + +node { + stage('Call esxifree_guest/esxifree_guest-release-tag') { + build job: 'esxifree_guest/esxifree_guest-release-tag', parameters: [string(name: 'NEW_VERSION', value: "next")] + } +} \ No newline at end of file diff --git a/meta/runtime.yml b/meta/runtime.yml new file mode 100644 index 0000000..aba42e9 --- /dev/null +++ b/meta/runtime.yml @@ -0,0 +1 @@ +requires_ansible: ">=2.9" diff --git a/library/esxifree_guest.py b/plugins/modules/esxifree_guest.py similarity index 100% rename from library/esxifree_guest.py rename to plugins/modules/esxifree_guest.py diff --git a/library/esxifree_guest_info.py b/plugins/modules/esxifree_guest_info.py similarity index 100% rename from library/esxifree_guest_info.py rename to plugins/modules/esxifree_guest_info.py