 bb94d419fb
			
		
	
	
		bb94d419fb
		
	
	
	
	
		
			
			The new option allows storing the secret access token outside the world readable Nix store.
		
			
				
	
	
		
			206 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			206 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
| { config, lib, pkgs, ... }:
 | |
| 
 | |
| with lib;
 | |
| 
 | |
| let
 | |
|   jenkinsCfg = config.services.jenkins;
 | |
|   cfg = config.services.jenkins.jobBuilder;
 | |
| 
 | |
| in {
 | |
|   options = {
 | |
|     services.jenkins.jobBuilder = {
 | |
|       enable = mkOption {
 | |
|         type = types.bool;
 | |
|         default = false;
 | |
|         description = ''
 | |
|           Whether or not to enable the Jenkins Job Builder (JJB) service. It
 | |
|           allows defining jobs for Jenkins in a declarative manner.
 | |
| 
 | |
|           Jobs managed through the Jenkins WebUI (or by other means) are left
 | |
|           unchanged.
 | |
| 
 | |
|           Note that it really is declarative configuration; if you remove a
 | |
|           previously defined job, the corresponding job directory will be
 | |
|           deleted.
 | |
| 
 | |
|           Please see the Jenkins Job Builder documentation for more info:
 | |
|           <link xlink:href="http://docs.openstack.org/infra/jenkins-job-builder/">
 | |
|           http://docs.openstack.org/infra/jenkins-job-builder/</link>
 | |
|         '';
 | |
|       };
 | |
| 
 | |
|       accessUser = mkOption {
 | |
|         default = "";
 | |
|         type = types.str;
 | |
|         description = ''
 | |
|           User id in Jenkins used to reload config.
 | |
|         '';
 | |
|       };
 | |
| 
 | |
|       accessToken = mkOption {
 | |
|         default = "";
 | |
|         type = types.str;
 | |
|         description = ''
 | |
|           User token in Jenkins used to reload config.
 | |
|           WARNING: This token will be world readable in the Nix store. To keep
 | |
|           it secret, use the <option>accessTokenFile</option> option instead.
 | |
|         '';
 | |
|       };
 | |
| 
 | |
|       accessTokenFile = mkOption {
 | |
|         default = "";
 | |
|         type = types.str;
 | |
|         example = "/run/keys/jenkins-job-builder-access-token";
 | |
|         description = ''
 | |
|           File containing the API token for the <option>accessUser</option>
 | |
|           user.
 | |
|         '';
 | |
|       };
 | |
| 
 | |
|       yamlJobs = mkOption {
 | |
|         default = "";
 | |
|         type = types.lines;
 | |
|         example = ''
 | |
|           - job:
 | |
|               name: jenkins-job-test-1
 | |
|               builders:
 | |
|                 - shell: echo 'Hello world!'
 | |
|         '';
 | |
|         description = ''
 | |
|           Job descriptions for Jenkins Job Builder in YAML format.
 | |
|         '';
 | |
|       };
 | |
| 
 | |
|       jsonJobs = mkOption {
 | |
|         default = [ ];
 | |
|         type = types.listOf types.str;
 | |
|         example = literalExample ''
 | |
|           [
 | |
|             '''
 | |
|               [ { "job":
 | |
|                   { "name": "jenkins-job-test-2",
 | |
|                     "builders": [ "shell": "echo 'Hello world!'" ]
 | |
|                   }
 | |
|                 }
 | |
|               ]
 | |
|             '''
 | |
|           ]
 | |
|         '';
 | |
|         description = ''
 | |
|           Job descriptions for Jenkins Job Builder in JSON format.
 | |
|         '';
 | |
|       };
 | |
| 
 | |
|       nixJobs = mkOption {
 | |
|         default = [ ];
 | |
|         type = types.listOf types.attrs;
 | |
|         example = literalExample ''
 | |
|           [ { job =
 | |
|               { name = "jenkins-job-test-3";
 | |
|                 builders = [
 | |
|                   { shell = "echo 'Hello world!'"; }
 | |
|                 ];
 | |
|               };
 | |
|             }
 | |
|           ]
 | |
|         '';
 | |
|         description = ''
 | |
|           Job descriptions for Jenkins Job Builder in Nix format.
 | |
| 
 | |
|           This is a trivial wrapper around jsonJobs, using builtins.toJSON
 | |
|           behind the scene.
 | |
|         '';
 | |
|       };
 | |
|     };
 | |
|   };
 | |
| 
 | |
|   config = mkIf (jenkinsCfg.enable && cfg.enable) {
 | |
|     assertions = [
 | |
|       { assertion =
 | |
|           if cfg.accessUser != ""
 | |
|           then (cfg.accessToken != "" && cfg.accessTokenFile == "") ||
 | |
|                (cfg.accessToken == "" && cfg.accessTokenFile != "")
 | |
|           else true;
 | |
|         message = ''
 | |
|           One of accessToken and accessTokenFile options must be non-empty
 | |
|           strings, but not both. Current values:
 | |
|             services.jenkins.jobBuilder.accessToken = "${cfg.accessToken}"
 | |
|             services.jenkins.jobBuilder.accessTokenFile = "${cfg.accessTokenFile}"
 | |
|         '';
 | |
|       }
 | |
|     ];
 | |
| 
 | |
|     systemd.services.jenkins-job-builder = {
 | |
|       description = "Jenkins Job Builder Service";
 | |
|       # JJB can run either before or after jenkins. We chose after, so we can
 | |
|       # always use curl to notify (running) jenkins to reload its config.
 | |
|       after = [ "jenkins.service" ];
 | |
|       wantedBy = [ "multi-user.target" ];
 | |
| 
 | |
|       path = with pkgs; [ jenkins-job-builder curl ];
 | |
| 
 | |
|       # Q: Why manipulate files directly instead of using "jenkins-jobs upload [...]"?
 | |
|       # A: Because this module is for administering a local jenkins install,
 | |
|       #    and using local file copy allows us to not worry about
 | |
|       #    authentication.
 | |
|       script =
 | |
|         let
 | |
|           yamlJobsFile = builtins.toFile "jobs.yaml" cfg.yamlJobs;
 | |
|           jsonJobsFiles =
 | |
|             map (x: (builtins.toFile "jobs.json" x))
 | |
|               (cfg.jsonJobs ++ [(builtins.toJSON cfg.nixJobs)]);
 | |
|           jobBuilderOutputDir = "/run/jenkins-job-builder/output";
 | |
|           # Stamp file is placed in $JENKINS_HOME/jobs/$JOB_NAME/ to indicate
 | |
|           # ownership. Enables tracking and removal of stale jobs.
 | |
|           ownerStamp = ".config-xml-managed-by-nixos-jenkins-job-builder";
 | |
|           reloadScript = ''
 | |
|             echo "Asking Jenkins to reload config"
 | |
|             curl_opts="--silent --fail --show-error"
 | |
|             access_token=${if cfg.accessTokenFile != ""
 | |
|                            then "$(cat '${cfg.accessTokenFile}')"
 | |
|                            else cfg.accessToken}
 | |
|             jenkins_url="http://${cfg.accessUser}:$access_token@${jenkinsCfg.listenAddress}:${toString jenkinsCfg.port}${jenkinsCfg.prefix}"
 | |
|             crumb=$(curl $curl_opts "$jenkins_url"'/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)')
 | |
|             curl $curl_opts -X POST -H "$crumb" "$jenkins_url"/reload
 | |
|           '';
 | |
|         in
 | |
|           ''
 | |
|             rm -rf ${jobBuilderOutputDir}
 | |
|             cur_decl_jobs=/run/jenkins-job-builder/declarative-jobs
 | |
|             rm -f "$cur_decl_jobs"
 | |
| 
 | |
|             # Create / update jobs
 | |
|             mkdir -p ${jobBuilderOutputDir}
 | |
|             for inputFile in ${yamlJobsFile} ${concatStringsSep " " jsonJobsFiles}; do
 | |
|                 HOME="${jenkinsCfg.home}" "${pkgs.jenkins-job-builder}/bin/jenkins-jobs" --ignore-cache test -o "${jobBuilderOutputDir}" "$inputFile"
 | |
|             done
 | |
| 
 | |
|             for file in "${jobBuilderOutputDir}/"*; do
 | |
|                 test -f "$file" || continue
 | |
|                 jobname="$(basename $file)"
 | |
|                 jobdir="${jenkinsCfg.home}/jobs/$jobname"
 | |
|                 echo "Creating / updating job \"$jobname\""
 | |
|                 mkdir -p "$jobdir"
 | |
|                 touch "$jobdir/${ownerStamp}"
 | |
|                 cp "$file" "$jobdir/config.xml"
 | |
|                 echo "$jobname" >> "$cur_decl_jobs"
 | |
|             done
 | |
| 
 | |
|             # Remove stale jobs
 | |
|             for file in "${jenkinsCfg.home}"/jobs/*/${ownerStamp}; do
 | |
|                 test -f "$file" || continue
 | |
|                 jobdir="$(dirname $file)"
 | |
|                 jobname="$(basename "$jobdir")"
 | |
|                 grep --quiet --line-regexp "$jobname" "$cur_decl_jobs" 2>/dev/null && continue
 | |
|                 echo "Deleting stale job \"$jobname\""
 | |
|                 rm -rf "$jobdir"
 | |
|             done
 | |
|           '' + (if cfg.accessUser != "" then reloadScript else "");
 | |
|       serviceConfig = {
 | |
|         User = jenkinsCfg.user;
 | |
|         RuntimeDirectory = "jenkins-job-builder";
 | |
|       };
 | |
|     };
 | |
|   };
 | |
| }
 |