Init push for ci-yadp
Signed-off-by: Benjamin Copeland <ben.copeland@linaro.org>
Change-Id: Iafb9217fc347da3d29f9fbdf3dcf89383cbf30bb
diff --git a/docker_ssh_buildslave.yml b/docker_ssh_buildslave.yml
new file mode 100644
index 0000000..3a3a2b7
--- /dev/null
+++ b/docker_ssh_buildslave.yml
@@ -0,0 +1,7 @@
+---
+# Name: buildslave SSH user creds on jenkins
+launch_ssh_credentials_id: 'buildslave'
+launch_ssh_port: '22'
+launch_ssh_connection_timeout: '60'
+launch_ssh_max_num_retries: '1000'
+launch_ssh_time_wait_between_retries: '60'
diff --git a/docker_templates_amd64.yml b/docker_templates_amd64.yml
new file mode 100644
index 0000000..1abc993
--- /dev/null
+++ b/docker_templates_amd64.yml
@@ -0,0 +1,9 @@
+---
+- docker-ros-builder:
+ docker_image_name: 'trustedfirmware/ci-amd64-ubuntu:bionic'
+ labels: 'docker-amd64-bionic'
+ launch_method: 'launch_ssh'
+ ssh: !include docker_ssh_buildslave.yml
+ environment:
+ - 'PATH=/home/buildslave/tools/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
+ - 'PLANTUML_JAR_PATH=/usr/share/plantuml/plantuml.jar'
diff --git a/hosts b/hosts
new file mode 100644
index 0000000..4684fc5
--- /dev/null
+++ b/hosts
@@ -0,0 +1,10 @@
+---
+# List of YADP Hosts
+
+- x86_64-TF-01:
+ cloud_name: x86_64-TF-01
+ docker_url: tcp://148.251.195.69:2375
+ host_credentials_id: docker-access
+ max_containers: 50 # equals the amount of nodes we have in the swarm
+ docker_templates: !include docker_templates_amd64.yml
+
diff --git a/templates/configure-yadocker-cloud.groovy.j2 b/templates/configure-yadocker-cloud.groovy.j2
new file mode 100644
index 0000000..df444e9
--- /dev/null
+++ b/templates/configure-yadocker-cloud.groovy.j2
@@ -0,0 +1,435 @@
+/*
+ Configure Yet Another Docker clouds provided by the Yet Another Docker
+ Plugin. This Jenkins Script Console script makes it easy to maintain large
+ amounts of configured clouds.
+
+ Note: This script deletes all yet another docker cloud configurations from
+ Jenkins before configuring the clouds. It does not affect in-process
+ builds. However, if there are previously configured yet another docker
+ clouds then they will be removed.
+
+ Yet Another Docker Plugin 0.1.0-rc31
+ */
+
+import com.github.kostyasha.yad.DockerCloud
+import com.github.kostyasha.yad.DockerConnector
+import com.github.kostyasha.yad.DockerContainerLifecycle
+import com.github.kostyasha.yad.DockerSlaveTemplate
+import com.github.kostyasha.yad.commons.DockerCreateContainer
+import com.github.kostyasha.yad.commons.DockerImagePullStrategy
+import com.github.kostyasha.yad.commons.DockerPullImage
+import com.github.kostyasha.yad.commons.DockerRemoveContainer
+import com.github.kostyasha.yad.commons.DockerSSHConnector
+import com.github.kostyasha.yad.commons.DockerStopContainer
+import com.github.kostyasha.yad.launcher.DockerComputerJNLPLauncher
+import com.github.kostyasha.yad.launcher.DockerComputerLauncher
+import com.github.kostyasha.yad.launcher.DockerComputerSSHLauncher
+import com.github.kostyasha.yad.other.ConnectorType
+import com.github.kostyasha.yad.strategy.DockerOnceRetentionStrategy
+import hudson.plugins.sshslaves.verifiers.NonVerifyingKeyVerificationStrategy
+import hudson.plugins.sshslaves.verifiers.SshHostKeyVerificationStrategy
+
+import hudson.model.Node
+import hudson.slaves.EnvironmentVariablesNodeProperty
+import hudson.slaves.NodeProperty
+import hudson.slaves.RetentionStrategy
+import hudson.tools.ToolLocationNodeProperty
+import jenkins.model.Jenkins
+import net.sf.json.JSONArray
+import net.sf.json.JSONObject
+
+/*
+ Configure the Yet Another Docker Plugin via this clouds_yadocker variable.
+
+ This variable can be removed and referenced for other configurations. All
+ values are optional with defaults set.
+ */
+
+JSONArray clouds_yadocker = [ {% for host in hosts %}
+ [
+ cloud_name: '{{host.cloud_name}}',
+ docker_url: '{{host.docker_url}}',
+ docker_api_version: '{{host.docker_api_version}}',
+ host_credentials_id: '{{host.host_credentials_id}}',
+ //valid values: netty, jersey
+ connection_type: '{{host.connection_type}}',
+ connect_timeout: '{{host.connect_timeout}}',
+ max_containers: '{{host.max_containers}}',
+ docker_templates: [
+ // List item of templates {% for docker in host.docker_templates %}
+ [
+ max_instances: '{{docker.max_instances}}',
+ //DOCKER CONTAINER LIFECYCLE
+ docker_image_name: '{{docker.docker_image_name}}',
+ //PULL IMAGE SETTINGS
+ //valid values: pull_latest, pull_always, pull_once, pull_never
+ pull_strategy: '{{docker.pull_strategy}}',
+ pull_registry_credentials_id: '{{docker.pull_registry_credentials_id}}',
+ //CREATE CONTAINER SETTINGS
+ docker_command: '{{docker.docker_command}}',
+ hostname: '{{docker.hostname}}',
+ dns: '{{docker.dns|default('8.8.8.8')}}',
+ //volumes can be a string or list of strings
+ volumes: '{{docker.volumes}}',
+ //volumes_from can be a string or list of strings
+ //volumes_from: '{{docker.volumes_from}}',
+ //environment can be a string or list of strings
+ environment: '{{docker.environment}}',
+ port_bindings: '{{docker.port_bindings}}',
+ bind_all_declared_ports: '{{docker.bind_all_declared_ports}}',
+ //0 is unlimited
+ memory_limit_in_mb: '{{docker.memory_limit_in_mb}}',
+ //0 is unlimited
+ cpu_shares: '{{docker.cpu_shares}}',
+ run_container_privileged: '{{docker.run_container_privileged}}',
+ allocate_pseudo_tty: '{{docker.allocate_pseudo_tty}}',
+ mac_address: '{{docker.mac_address}}',
+ //extra_hosts can be a string or list of strings
+ extra_hosts: '{{docker.extra_hosts}}',
+ network_mode: '{{docker.network_mode}}',
+ //devices can be a string or list of strings
+ devices: '{{docker.devices}}',
+ cpuset_constraint_cpus: '{{docker.cpuset_constraint_cpus}}',
+ cpuset_constraint_mems: '{{docker.cpuset_constraint_mems}}',
+ //links can be a string or list of strings
+ links: '{{docker.links}}',
+ //STOP CONTAINER SETTINGS
+ stop_container_timeout: '{{docker.stop_container_timeout}}',
+ //STOP CONTAINER SETTINGS
+ remove_volumes: '{{docker.remove_volumes}}',
+ force_remove_containers: '{{docker.force_remove_containers}}',
+ //JENKINS SLAVE CONFIG
+ remote_fs_root: '{{docker.remote_fs_root|default('/home/buildslave')}}',
+ labels: '{{docker.labels}}',
+ //valid values: exclusive or normal
+ usage: '{{docker.usage}}',
+ availability_strategy: '{{docker.availability_strategy}}',
+ availability_idle_timeout: '{{docker.availability_idle_timeout}}',
+ executors: '{{docker.executors}}',
+ //LAUNCH METHOD
+ //valid values: launch_ssh or launch_jnlp
+ {%if docker.launch_method == 'launch_ssh' %}
+ launch_method: 'launch_ssh',
+ //settings specific to launch_ssh (you only need one or the other)
+ launch_ssh_credentials_id: '{{docker.ssh.launch_ssh_credentials_id}}',
+ launch_ssh_port: '{{docker.ssh.launch_ssh_port|default('22')}}',
+ launch_ssh_java_path: '{{docker.ssh.launch_ssh_java_path}}',
+ launch_ssh_jvm_options: '{{docker.ssh.launch_ssh_jvm_options}}',
+ launch_ssh_prefix_start_slave_command: '{{docker.ssh.launch_ssh_prefix_start_slave_command}}',
+ launch_ssh_suffix_start_slave_command: '{{docker.ssh.launch_ssh_suffix_start_slave_command}}',
+ launch_ssh_connection_timeout: '{{docker.ssh.launch_ssh_connection_timeout}}',
+ launch_ssh_max_num_retries: '{{docker.ssh.launch_ssh_max_num_retries}}',
+ launch_ssh_time_wait_between_retries: '{{docker.ssh.launch_ssh_time_wait_between_retries}}',
+ {%elif docker.launch_method == 'launch_jnlp' %}
+ launch_method: 'launch_jnlp',
+ //settings specific to launch_jnlp
+ launch_jnlp_linux_user: '{{docker.jnlp.launch_jnlp_linux_user}}',
+ launch_jnlp_lauch_timeout: '{{docker.jnlp.launch_jnlp_lauch_timeout}}',
+ launch_jnlp_slave_jar_options: '{{docker.jnlp.launch_jnlp_slave_jar_options}}',
+ launch_jnlp_slave_jvm_options: '{{docker.jnlp.launch_jnlp_slave_jvm_options}}',
+ launch_jnlp_different_jenkins_master_url: '{{docker.jnlp.launch_jnlp_different_jenkins_master_url}}',
+ launch_jnlp_ignore_certificate_check: '{{docker.jnlp.launch_jnlp_ignore_certificate_check}}',
+ {%endif%}
+ //NODE PROPERTIES
+ //environment_variables is a HashMap of key/value pairs
+ environment_variables: '{{docker.environment_variables}}',
+ //tool location key/value pairs from https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/tools/ToolLocationNodeProperty.java
+ //The key is type@name = home where type is typically the class name of the tool and name is the name given in the Global Tools configuration.
+ //For example let's say you have a global tool configuration named OracleJDK8 for JDK installations
+ //tool_locations would be something like ['hudson.model.JDK$DescriptorImpl@OracleJDK8': '/path/to/java_home']
+ //If you're unsure of the tool@name then check config.xml where YADocker configurations are saved.
+ tool_locations: '{{docker.tool_locations}}',
+ remote_fs_root_mapping: '{{docker.remote_fs_root_mapping}}'
+
+ ],
+{% endfor %}
+ ],
+ ],
+{% endfor %}
+ ] as JSONArray
+
+//detect an existing global tool
+def detectGlobalToolExists(String location) {
+ def (toolType, toolName) = location.split('@')
+ boolean found_installation = Jenkins.instance.getExtensionList(toolType)[0].installations.findAll { it.name == toolName } as boolean
+ return found_installation
+}
+
+//return a launcher
+def selectLauncher(String launcherType, JSONObject obj) {
+ switch(launcherType) {
+ case 'launch_ssh':
+ def jenkinsCredentials = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
+ com.cloudbees.plugins.credentials.Credentials.class,
+ Jenkins.instance,
+ null,
+ null
+ );
+ def launch_credentials = null
+ for (creds in jenkinsCredentials) {
+ if(creds.id == obj.optString('launch_ssh_credentials_id')){
+ launch_credentials = creds;
+ }
+ }
+ DockerSSHConnector sshConnector = new DockerSSHConnector(obj.optInt('launch_ssh_port', 22),
+ launch_credentials,
+ obj.optString('launch_ssh_credentials_id'),
+ obj.optString('launch_ssh_jvm_options'),
+ obj.optString('launch_ssh_java_path'),
+ null,
+ obj.optString('launch_ssh_prefix_start_slave_command'),
+ obj.optString('launch_ssh_suffix_start_slave_command'),
+ obj.optInt('launch_ssh_connection_timeout'),
+ obj.optInt('launch_ssh_max_num_retries'),
+ obj.optInt('launch_ssh_time_wait_between_retries'),
+ new NonVerifyingKeyVerificationStrategy()
+ )
+ return new DockerComputerSSHLauncher(sshConnector)
+ case 'launch_jnlp':
+ DockerComputerJNLPLauncher dockerComputerJNLPLauncher = new DockerComputerJNLPLauncher()
+ dockerComputerJNLPLauncher.setUser(obj.optString('launch_jnlp_linux_user','jenkins'))
+ dockerComputerJNLPLauncher.setLaunchTimeout(obj.optLong('launch_jnlp_lauch_timeout', 120L))
+ dockerComputerJNLPLauncher.setSlaveOpts(obj.optString('launch_jnlp_slave_jar_options'))
+ dockerComputerJNLPLauncher.setJvmOpts(obj.optString('launch_jnlp_slave_jvm_options'))
+ dockerComputerJNLPLauncher.setJenkinsUrl(obj.optString('launch_jnlp_different_jenkins_master_url'))
+ dockerComputerJNLPLauncher.setNoCertificateCheck(obj.optBoolean('launch_jnlp_ignore_certificate_check', false))
+ return dockerComputerJNLPLauncher
+ default:
+ return null
+ }
+}
+
+//create a new instance of the DockerCloud class from a JSONObject
+def newDockerCloud(JSONObject obj) {
+ DockerConnector connector = new DockerConnector(obj.optString('docker_url', 'tcp://localhost:2375'))
+ if(obj.optInt('connect_timeout')) {
+ connector.setConnectTimeout(obj.optInt('connect_timeout'))
+ }
+ connector.setApiVersion(obj.optString('docker_api_version'))
+ connector.setCredentialsId(obj.optString('host_credentials_id'))
+ //select connection_type
+ List<String> connection_types = ['NETTY', 'JERSEY']
+ String connection_type_default = 'JERSEY'
+ String user_selected_connection_type = obj.optString('connection_type', connection_type_default).toUpperCase()
+ String connection_type = (user_selected_connection_type in connection_types)? user_selected_connection_type : connection_type_default
+ connector.setConnectorType(ConnectorType."${connection_type}")
+
+ DockerCloud cloud = new DockerCloud(obj.optString('cloud_name'),
+ bindJSONToList(DockerSlaveTemplate.class, obj.opt('docker_templates')),
+ obj.optInt('max_containers', 1),
+ connector)
+
+ return cloud
+}
+
+def newDockerSlaveTemplate(JSONObject obj) {
+ //DockerPullImage
+ DockerPullImage pullImage = new DockerPullImage()
+ String pullStrategy = obj.optString('pull_strategy', 'PULL_LATEST').toUpperCase()
+ if(pullStrategy in ['PULL_LATEST', 'PULL_ALWAYS', 'PULL_ONCE', 'PULL_NEVER']) {
+ pullImage.setPullStrategy(DockerImagePullStrategy."${pullStrategy}")
+ } else {
+ pullImage.setPullStrategy(DockerImagePullStrategy.PULL_LATEST)
+ }
+ pullImage.setCredentialsId(obj.optString('pull_registry_credentials_id'))
+
+ //DockerCreateContainer
+ DockerCreateContainer createContainer = new DockerCreateContainer()
+ createContainer.setBindPorts(obj.optString('port_bindings'))
+ createContainer.setBindAllPorts(obj.optBoolean('bind_all_declared_ports', false))
+ createContainer.setDnsString(obj.optString('dns'))
+ createContainer.setHostname(obj.optString('hostname'))
+ if(obj.optLong('memory_limit_in_mb')) {
+ createContainer.setMemoryLimit(obj.optLong('memory_limit_in_mb'))
+ }
+ createContainer.setPrivileged(obj.optBoolean('run_container_privileged', true))
+ createContainer.setTty(obj.optBoolean('allocate_pseudo_tty', false))
+ if(obj.optJSONArray('volumes')) {
+ createContainer.setVolumes(obj.optJSONArray('volumes') as List<String>)
+ } else {
+ createContainer.setVolumesString(obj.optString('volumes'))
+ }
+ if(obj.optJSONArray('volumes_from')) {
+ createContainer.setVolumesFrom(obj.optJSONArray('volumes_from') as List<String>)
+ } else {
+ createContainer.setVolumesFromString(obj.optString('volumes_from'))
+ }
+ createContainer.setMacAddress(obj.optString('mac_address'))
+ if(obj.optInt('cpu_shares')) {
+ createContainer.setCpuShares(obj.optInt('cpu_shares'))
+ }
+ createContainer.setCommand(obj.optString('docker_command'))
+ if(obj.optJSONArray('environment')) {
+ createContainer.setEnvironment(obj.optJSONArray('environment') as List<String>)
+ } else {
+ createContainer.setEnvironmentString(obj.optString('environment'))
+ }
+ if(obj.optJSONArray('extra_hosts')) {
+ createContainer.setExtraHosts(obj.optJSONArray('extra_hosts') as List<String>)
+ } else {
+ createContainer.setExtraHostsString(obj.optString('extra_hosts'))
+ }
+ createContainer.setNetworkMode(obj.optString('network_mode'))
+ if(obj.optJSONArray('devices')) {
+ createContainer.setDevices(obj.optJSONArray('devices'))
+ } else {
+ createContainer.setDevicesString(obj.optString('devices'))
+ }
+ createContainer.setCpusetCpus(obj.optString('cpuset_constraint_cpus'))
+ createContainer.setCpusetMems(obj.optString('cpuset_constraint_mems'))
+ if(obj.optJSONArray('links')) {
+ createContainer.setLinks(obj.optJSONArray('links'))
+ } else {
+ createContainer.setLinksString(obj.optString('links'))
+ }
+
+ //DockerStopContainer
+ DockerStopContainer stopContainer = new DockerStopContainer()
+ stopContainer.setTimeout(obj.optInt('stop_container_timeout', 10))
+
+ //DockerRemoveContainer
+ DockerRemoveContainer removeContainer = new DockerRemoveContainer()
+ removeContainer.setRemoveVolumes(obj.optBoolean('remove_volumes', false))
+ removeContainer.setForce(obj.optBoolean('force_remove_containers', false))
+
+ //DockerContainerLifecycle
+ DockerContainerLifecycle dockerContainerLifecycle = new DockerContainerLifecycle()
+ dockerContainerLifecycle.setImage(obj.optString('docker_image_name'))
+ dockerContainerLifecycle.setPullImage(pullImage)
+ dockerContainerLifecycle.setCreateContainer(createContainer)
+ dockerContainerLifecycle.setStopContainer(stopContainer)
+ dockerContainerLifecycle.setRemoveContainer(removeContainer)
+
+ //DockerOnceRetentionStrategy
+ //this availability_strategy is for "run_once". We can customize it later
+ RetentionStrategy retentionStrategy = new DockerOnceRetentionStrategy(obj.optInt('availability_idle_timeout', 10))
+
+ //DockerComputerLauncher
+ //select a launch method from the list of available launch methods
+ List<String> launch_methods = ['launch_ssh', 'launch_jnlp']
+ String default_launch_method = 'launch_jnlp'
+ String user_selected_launch_method = obj.optString('launch_method', default_launch_method).toLowerCase()
+ String launch_method = (user_selected_launch_method in launch_methods)? user_selected_launch_method : default_launch_method
+ DockerComputerLauncher launcher = selectLauncher(launch_method, obj)
+
+ //DockerSlaveTemplate
+ DockerSlaveTemplate dockerSlaveTemplate = new DockerSlaveTemplate()
+ dockerSlaveTemplate.setDockerContainerLifecycle(dockerContainerLifecycle)
+ dockerSlaveTemplate.setLabelString(obj.optString('labels', 'docker'))
+ String node_usage = (obj.optString('usage', 'EXCLUSIVE').toUpperCase().equals('NORMAL'))? 'NORMAL' : 'EXCLUSIVE'
+ dockerSlaveTemplate.setMode(Node.Mode."${node_usage}")
+ dockerSlaveTemplate.setNumExecutors(obj.optInt('executors', 1))
+ dockerSlaveTemplate.setRetentionStrategy(retentionStrategy)
+ dockerSlaveTemplate.setLauncher(launcher)
+ dockerSlaveTemplate.setRemoteFs(obj.optString('remote_fs_root', '/srv/jenkins'))
+ dockerSlaveTemplate.setMaxCapacity(obj.optInt('max_instances', 1))
+ //dockerSlaveTemplate.setRemoteFsMapping(obj.optString('remote_fs_root_mapping'))
+ //dockerSlaveTemplate.remoteFsMapping = obj.optString('remote_fs_root_mapping')
+ //define NODE PROPERTIES
+ List<NodeProperty> nodeProperties = [] as List<NodeProperty>
+ if(obj.optJSONObject('environment_variables')) {
+ HashMap<String,String> env = obj.optJSONObject('environment_variables') as HashMap<String,String>
+ List<EnvironmentVariablesNodeProperty.Entry> envEntries = [] as List<EnvironmentVariablesNodeProperty.Entry>
+ (env.keySet() as String[]).each { var ->
+ envEntries << (new EnvironmentVariablesNodeProperty.Entry(var, env[var]))
+ }
+ //add environment_variables to nodeProperties
+ nodeProperties << (new EnvironmentVariablesNodeProperty(envEntries))
+ }
+ if(obj.optJSONObject('tool_locations')) {
+ HashMap<String,String> tool = obj.optJSONObject('tool_locations') as HashMap<String,String>
+ List<ToolLocationNodeProperty.ToolLocation> toolLocations = [] as List<ToolLocationNodeProperty.ToolLocation>
+ (tool.keySet() as String[]).each { location ->
+ //tool location is only valid if it contains a type i.e. @ symbol
+ if(location.contains('@') && detectGlobalToolExists(location)) {
+ toolLocations << (new ToolLocationNodeProperty.ToolLocation(location, tool[location]))
+ } else {
+ //alert the user they configured an invalid tool type or name in tool_locations
+ println "WARNING: Invalid tool: '${location}'. Format should be 'type@name' where both type and name already exist in global tool configurations."
+ }
+ }
+ nodeProperties << (new ToolLocationNodeProperty(toolLocations))
+ }
+ //set NODE PROPERTIES
+ if(nodeProperties) {
+ dockerSlaveTemplate.setNodeProperties(nodeProperties)
+ }
+ return dockerSlaveTemplate
+}
+
+def bindJSONToList(Class type, Object src) {
+ if(!(type == DockerCloud) && !(type == DockerSlaveTemplate)) {
+ throw new Exception("Must use DockerCloud or DockerSlaveTemplate class.")
+ }
+ //docker_array should be a DockerCloud or DockerSlaveTemplate
+ ArrayList<?> docker_array
+ if(type == DockerCloud){
+ docker_array = new ArrayList<DockerCloud>()
+ } else {
+ docker_array = new ArrayList<DockerSlaveTemplate>()
+ }
+ //cast the configuration object to a Docker instance which Jenkins will use in configuration
+ if (src instanceof JSONObject) {
+ //uses string interpolation to call a method
+ //e.g instead of newDockerCloud(src) we use instead...
+ docker_array.add("new${type.getSimpleName()}"(src))
+ } else if(src instanceof JSONArray) {
+ for (Object o : src) {
+ if (o instanceof JSONObject) {
+ docker_array.add("new${type.getSimpleName()}"(o))
+ }
+ }
+ }
+ return docker_array
+}
+
+if(!Jenkins.instance.isQuietingDown()) {
+ ArrayList<DockerCloud> clouds = new ArrayList<DockerCloud>()
+ clouds = bindJSONToList(DockerCloud.class, clouds_yadocker)
+ if(clouds.size() > 0) {
+{%if dryrun == True %}
+ clouds*.name.each { cloudName ->
+ println "DRYRUN: Configured Yet Another Docker cloud ${cloudName}"
+ }
+{%else%}
+ dockerConfigUpdated = true
+ Jenkins.instance.clouds.removeAll(DockerCloud)
+ Jenkins.instance.clouds.addAll(clouds)
+ clouds*.name.each { cloudName ->
+ println "Configured Yet Another Docker cloud ${cloudName}"
+ }
+ Jenkins.instance.save()
+{%endif%}
+ } else {
+ println 'Nothing changed. No Yet Another docker clouds to configure.'
+ }
+} else {
+ println 'Shutdown mode enabled. Configure Yet Another Docker clouds SKIPPED.'
+}
+
+//return null so there's no result value in the script console
+null
+
+/*
+ The MIT License (MIT)
+
+ Copyright 2017 Sam Gleske - https://github.com/samrocketman/jenkins-bootstrap-jervis
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+ */
diff --git a/yadp_builder.py b/yadp_builder.py
new file mode 100755
index 0000000..8e9bf06
--- /dev/null
+++ b/yadp_builder.py
@@ -0,0 +1,121 @@
+import argparse
+import yaml
+import os
+from jinja2 import FileSystemLoader, Environment
+import jenkins
+import logging
+
+
+class LoaderMeta(type):
+
+ def __new__(metacls, __name__, __bases__, __dict__):
+ """Add include constructer to class."""
+
+ # register the include constructor on the class
+ cls = super().__new__(metacls, __name__, __bases__, __dict__)
+ cls.add_constructor('!include', cls.construct_include)
+
+ return cls
+
+
+class Loader(yaml.Loader, metaclass=LoaderMeta):
+ """YAML Loader with `!include` constructor."""
+
+ def __init__(self, stream):
+ """Initialise Loader."""
+
+ try:
+ self._root = os.path.split(stream.name)[0]
+ except AttributeError:
+ self._root = os.path.curdir
+
+ super().__init__(stream)
+
+ def construct_include(self, node):
+ if isinstance(node, yaml.ScalarNode):
+ return self.extractFile(self.construct_scalar(node))
+
+ elif isinstance(node, yaml.SequenceNode):
+ result = []
+ for filename in self.construct_sequence(node):
+ result += self.extractFile(filename)
+ return result
+
+ elif isinstance(node, yaml.MappingNode):
+ result = {}
+ for k,v in self.construct_mapping(node).iteritems():
+ result[k] = self.extractFile(v)
+ return result
+
+ else:
+ print "Error:: unrecognised node type in !include statement"
+ raise yaml.constructor.ConstructorError
+
+
+ def extractFile(self, filename):
+ filepath = os.path.join(self._root, filename)
+ with open(filepath, 'r') as f:
+ return yaml.load(f, Loader=Loader)
+
+
+def jinja2_from_template(directory, template_name, data, dryrun=False):
+ loader = FileSystemLoader(directory)
+ env = Environment(loader=loader)
+ template = env.get_template(template_name)
+ return template.render(hosts=data, dryrun=dryrun)
+
+
+def get_parser():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-u', '--username', type=str,
+ default=os.environ.get('JJB_USER'),
+ help='Username for Jenkins server')
+ parser.add_argument('-p', '--password', type=str,
+ default=os.environ.get('JJB_PASSWORD'),
+ help='Password for Jenkins server')
+ parser.add_argument('-s', '--server', type=str,
+ default='http://localhost:8080',
+ help='Jenkins server URL. e.g. http://localhost:8080')
+ parser.add_argument('-i', '--inventory', type=str, default='hosts',
+ help='specify inventory host path')
+ parser.add_argument('-l', '--loglevel', default='INFO',
+ help="Setting logging level, default: %(default)s")
+ parser.add_argument('--dryrun', action='store_true',
+ help='Do not publish to Jenkins')
+ parser.add_argument('--local', action='store_true',
+ help='Create tmp file only, to be used with dryrun.')
+ return parser
+
+
+if __name__ == '__main__':
+ parser = get_parser()
+ args = parser.parse_args()
+ logging.basicConfig(level=args.loglevel)
+ with open(args.inventory, 'r') as f:
+ data = yaml.load(f, Loader=Loader)
+ logging.debug(data)
+ template_output = jinja2_from_template(
+ './templates',
+ 'configure-yadocker-cloud.groovy.j2', data)
+
+ if not args.local:
+ server = jenkins.Jenkins(args.server, username=args.username,
+ password=args.password)
+ if args.dryrun:
+ with open('/tmp/configure-yadocker-cloud.groovy', 'w') as fw:
+ fw.write(template_output)
+ template_output = jinja2_from_template(
+ './templates',
+ 'configure-yadocker-cloud.groovy.j2', data, args.dryrun)
+ if not args.local:
+ publishdry = server.run_script(template_output)
+ if 'error' in publishdry:
+ logging.info(publishdry)
+ exit(1)
+ logging.info(publishdry)
+ else:
+ logging.info('Template file created at \
+ /tmp/configure-yadocker-cloud.groovy')
+ else:
+ publish = server.run_script(template_output)
+ logging.info(publish)