├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── bin └── .gitkeep ├── resources ├── README.md ├── agentManagement │ ├── AWSAgentBootstrap.groovy │ └── AWSAgentDestroy.groovy ├── config │ ├── configuration-as-code-plugin │ │ └── jenkins.yaml │ └── groovy │ │ ├── auth.groovy │ │ ├── baseURL.groovy │ │ ├── credentials.groovy │ │ ├── github.groovy │ │ ├── globalEnvVars.groovy │ │ ├── globalSharedLibrary.groovy │ │ ├── securitySettings.groovy │ │ ├── slack.groovy │ │ ├── theme.groovy │ │ ├── timezone.groovy │ │ ├── triggerConfigurationAsCodePlugin.groovy │ │ └── userPublicKeys.groovy ├── docker │ ├── Dockerfile │ ├── dsl │ │ └── ConfigurationAndSeedingPipelineDSL.groovy │ ├── init.groovy.d │ │ └── init.groovy │ ├── plugins.txt │ └── scriptApproval.xml ├── helm │ ├── Chart.yaml │ ├── templates │ │ ├── ingress.yaml │ │ ├── secrets.yaml │ │ ├── services.yaml │ │ └── statefulsets.yaml │ └── values.yaml ├── init │ └── ConfigurationAndSeedingPipeline.groovy ├── jobDSL │ ├── agentManagement.groovy │ └── example.groovy └── terraform │ ├── Makefile │ ├── README.md │ ├── aws │ ├── agent-network │ │ ├── Makefile │ │ ├── all.tf │ │ ├── aws-agent-network.backend.config │ │ ├── io.tf │ │ └── terraform.tfvars │ └── agent-vms │ │ ├── Makefile │ │ ├── all.tf │ │ ├── aws-agent-vms.backend.config │ │ ├── io.tf │ │ ├── terraform.tfvars │ │ └── user_data │ │ └── docker.sh │ └── config └── vars ├── AWSAgentBootstrap.groovy ├── AWSAgentDestroy.groovy └── printDockerVersion.groovy /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | resources/helm/secret-files 3 | .terraform 4 | bin/helm 5 | bin/kubectl 6 | bin/minikube 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 devtail 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | get-tools-linux: ARCH=linux 2 | get-tools-mac: ARCH=darwin 3 | 4 | .PHONY: help 5 | 6 | help: ## Prints help for targets with comments 7 | @grep -E '^[a-zA-Z0-9.\ _-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' 8 | 9 | get-tools-linux get-tools-mac: ## Pull dep-less binaries (minikube, kubectl, helm) to local bin/ 10 | # TODO: verify checksums .. 11 | curl -Lo bin/minikube https://storage.googleapis.com/minikube/releases/v1.0.0/minikube-$(ARCH)-amd64 && chmod +x bin/minikube 12 | curl -Lo bin/kubectl https://storage.googleapis.com/kubernetes-release/release/v1.14.0/bin/$(ARCH)/amd64/kubectl && chmod +x bin/kubectl 13 | curl -L https://storage.googleapis.com/kubernetes-helm/helm-v2.13.1-$(ARCH)-amd64.tar.gz | tar -xz -C bin/ && mv bin/$(ARCH)-amd64/helm bin/ && rm -r bin/$(ARCH)-amd64 14 | 15 | minikube-start: ## Start and prepare minikube with ingress addon 16 | bin/minikube start --extra-config=apiserver.service-node-port-range=80-32000 17 | bin/minikube addons enable ingress 18 | bin/helm init --history-max 100 19 | 20 | helm-deploy: ## Deploy jenkins to minikube 21 | bin/helm upgrade --wait --install jenkins resources/helm 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Docker Repository on Quay.io](https://quay.io/repository/devtail/jenkins-as-code/status "Docker Repository on Quay.io")](https://quay.io/repository/devtail/jenkins-as-code) 2 | 3 | # jenkins-as-code 4 | 5 | **[Demo] Bootstrapping Jenkins:** 6 |

7 | 8 |

9 | 10 | This repository contains a Jenkins-as-Code approach. 11 | Everything is tested and running with Jenkins `2.204.1` on minikube `v1.0.0` (the .gif you above was done with an older version). 12 | The setup is based on docker, helm and git so it can be easily applied in different infrastructures. 13 | Plugins and minimum setup are pre-baked inside a docker image. 14 | A configuration and seeding pipeline provisions Jenkins with configuration code from a central git repository. 15 | Configuration includes: agents on demand (with terraform), slack, github, github-oauth, security settings, theming, ... 16 | 17 | ## Running locally 18 | 19 | The following files with your secrets have to be created to run this prototype: 20 | 21 | ``` 22 | resources/helm/ 23 | |-- secret-files 24 | |-- default-setup-password # pre-baked setup user password 25 | |-- default-setup-user # pre-baked setup user name 26 | |-- deploy-key-shared-library # private ssh deploy key 27 | |-- deploy-key-shared-library.pub # public ssh deploy key 28 | |-- github-ci-password # GitHub Jenkins user password 29 | |-- github-ci-token # GitHub Jenkins user access token 30 | |-- github-ci-user # GitHub Jenkins user name 31 | |-- github-oauth-client-id 32 | |-- github-oauth-client-secret 33 | |-- slack-token 34 | |-- ssh-agent-access-key # private ssh key for agent access 35 | |-- ssh-agent-access-key.pub # public ssh key for agent access 36 | `-- terraform-config # Terraform backend configs and secrets 37 | |-- aws-agent-network.backend.config 38 | |-- aws-agent-network.tfvars 39 | |-- aws-agent-vms.backend.config 40 | `-- aws-agent-vms.tfvars 41 | ``` 42 | 43 | After you have placed your secret files you can run: 44 | 45 | ``` 46 | make get-tools-(linux|mac) 47 | make minikube-start 48 | make deploy-helm 49 | ``` 50 | 51 | This should open Jenkins on [http://192.168.99.100/](http://192.168.99.100/). 52 | 53 | ## On-Demand Agents 54 | 55 | The code base also supports on-demand agents with custom terraform bootstrapping/destroy pipelines. 56 | A demo can be found [here](resources/README.md) 57 | 58 | ## Detailed Explanations 59 | 60 | The following blog entries describe in more detail how this works: 61 | 62 | - [Jenkins-as-Code Part I | Initial Setup](https://fishi.devtail.io/weblog/2019/01/06/jenkins-as-code-part-1/) 63 | - [Jenkins-as-Code Part II | Configuration](https://fishi.devtail.io/weblog/2019/01/12/jenkins-as-code-part-2/) 64 | - [Jenkins-as-Code Part III | JobDSL](https://fishi.devtail.io/weblog/2019/02/09/jenkins-as-code-part-3/) 65 | -------------------------------------------------------------------------------- /bin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devtail/jenkins-as-code/5d5e977c709a59d047f767b7a04e528268f9ee7a/bin/.gitkeep -------------------------------------------------------------------------------- /resources/README.md: -------------------------------------------------------------------------------- 1 | ## On-Demand Agents 2 | 3 | **[Demo] On-Demand Agents:** 4 |

5 | 6 |

7 | -------------------------------------------------------------------------------- /resources/agentManagement/AWSAgentBootstrap.groovy: -------------------------------------------------------------------------------- 1 | @Library('devtail-ci-lib@master') _ 2 | 3 | AWSAgentBootstrap(agentID: env.agentID) 4 | -------------------------------------------------------------------------------- /resources/agentManagement/AWSAgentDestroy.groovy: -------------------------------------------------------------------------------- 1 | @Library('devtail-ci-lib@master') _ 2 | 3 | AWSAgentDestroy(agentID: env.agentID) 4 | -------------------------------------------------------------------------------- /resources/config/configuration-as-code-plugin/jenkins.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | jenkins: 3 | systemMessage: "Powered automagically by Groovy and Configuration-as-Code Plugin\n\n" 4 | numExecutors: 5 5 | 6 | crumbIssuer: 7 | standard: 8 | excludeClientIPFromCrumb: false 9 | 10 | remotingSecurity: 11 | enabled: true 12 | 13 | globalNodeProperties: 14 | - envVars: 15 | env: 16 | - key: "DEVTAIL_DEBUG" 17 | value: "True" 18 | 19 | authorizationStrategy: 20 | globalMatrix: 21 | grantedPermissions: 22 | - "Overall/Administer:devtail*Admin" 23 | - "Overall/Administer:admin" 24 | 25 | securityRealm: 26 | github: 27 | githubWebUri: "https://github.com" 28 | githubApiUri: "https://api.github.com" 29 | clientID: "${jenkins-tokens/github-oauth-client-id}" 30 | clientSecret: "${jenkins-tokens/github-oauth-client-secret}" 31 | oauthScopes: "read:org,user:email" 32 | 33 | nodes: 34 | - permanent: 35 | labelString: "docker" 36 | name: "docker-agent-1" 37 | nodeName: "docker-agent-1" 38 | numExecutors: 2 39 | remoteFS: "/home/ubuntu" 40 | launcher: 41 | setupSlaveLauncher: 42 | startScript: "java -jar /var/jenkins_home/war/WEB-INF/jenkins-cli.jar -s http://localhost:8080 -ssh -user admin -i /var/jenkins_home/jenkins-ssh-keys/ssh-agent-access-key build 'Admin/AWSAgentBootstrap' -p agentID='0' -s" 43 | stopScript: "java -jar /var/jenkins_home/war/WEB-INF/jenkins-cli.jar -s http://localhost:8080 -ssh -user admin -i /var/jenkins_home/jenkins-ssh-keys/ssh-agent-access-key build 'Admin/AWSAgentDestroy' -p agentID='0' -s" 44 | launcher: 45 | sSHLauncher: 46 | credentialsId: "ssh-agent-access-key" 47 | host: "18.195.157.53" 48 | workDir: "/home/ubuntu" 49 | sshHostKeyVerificationStrategy: "nonVerifyingKeyVerificationStrategy" 50 | retentionStrategy: 51 | demand: 52 | idleDelay: 1000 53 | inDemandDelay: 0 54 | - permanent: 55 | labelString: "docker" 56 | name: "docker-agent-2" 57 | nodeName: "docker-agent-2" 58 | numExecutors: 2 59 | remoteFS: "/home/ubuntu" 60 | launcher: 61 | setupSlaveLauncher: 62 | startScript: "java -jar /var/jenkins_home/war/WEB-INF/jenkins-cli.jar -s http://localhost:8080 -ssh -user admin -i /var/jenkins_home/jenkins-ssh-keys/ssh-agent-access-key build 'Admin/AWSAgentBootstrap' -p agentID='1' -s" 63 | stopScript: "java -jar /var/jenkins_home/war/WEB-INF/jenkins-cli.jar -s http://localhost:8080 -ssh -user admin -i /var/jenkins_home/jenkins-ssh-keys/ssh-agent-access-key build 'Admin/AWSAgentDestroy' -p agentID='1' -s" 64 | launcher: 65 | sSHLauncher: 66 | credentialsId: "ssh-agent-access-key" 67 | host: "3.122.182.67" 68 | workDir: "/home/ubuntu" 69 | sshHostKeyVerificationStrategy: "nonVerifyingKeyVerificationStrategy" 70 | retentionStrategy: 71 | demand: 72 | idleDelay: 1000 73 | inDemandDelay: 0 74 | 75 | security: 76 | globalJobDslSecurityConfiguration: 77 | useScriptSecurity: false 78 | sSHD: 79 | port: 6666 80 | 81 | credentials: 82 | system: 83 | domainCredentials: 84 | - credentials: 85 | ####### 86 | # User Auth 87 | ####### 88 | - usernamePassword: 89 | scope: "GLOBAL" 90 | id: "github-ci-user" 91 | description: "Github CI User Credentials" 92 | username: "${jenkins-basic-auth-credentials/github-ci-user}" 93 | password: "${jenkins-basic-auth-credentials/github-ci-password}" 94 | ######## 95 | # Tokens 96 | ######## 97 | - string: 98 | scope: "GLOBAL" 99 | id: "slack-token" 100 | description: "Slack Access Token" 101 | secret: "${jenkins-tokens/slack-token}" 102 | - string: 103 | scope: "GLOBAL" 104 | id: "github-ci-token" 105 | description: "Github CI User Token" 106 | secret: "${jenkins-tokens/github-ci-token}" 107 | - string: 108 | scope: "GLOBAL" 109 | id: "github-oauth-client-id" 110 | description: "Github OAuth Client ID" 111 | secret: "${jenkins-tokens/github-oauth-client-id}" 112 | - string: 113 | scope: "GLOBAL" 114 | id: "github-oauth-client-secret" 115 | description: "Github OAuth Client Secret" 116 | secret: "${jenkins-tokens/github-oauth-client-secret}" 117 | ######## 118 | # SSH Keys 119 | ######## 120 | - basicSSHUserPrivateKey: 121 | scope: "GLOBAL" 122 | id: "deploy-key-shared-library" 123 | username: "root" 124 | passphrase: "" 125 | description: "Deploy key for global shared library" 126 | privateKeySource: 127 | directEntry: 128 | privateKey: "${jenkins-ssh-keys/deploy-key-shared-library}" 129 | - basicSSHUserPrivateKey: 130 | scope: "GLOBAL" 131 | id: "ssh-agent-access-key" 132 | username: "ubuntu" 133 | passphrase: "" 134 | description: "SSH key to access agents" 135 | privateKeySource: 136 | directEntry: 137 | privateKey: "${jenkins-ssh-keys/ssh-agent-access-key}" 138 | 139 | unclassified: 140 | location: 141 | url: "http://192.168.99.100:30001/" 142 | adminAddress: "admin@devtail.io" 143 | 144 | globalLibraries: 145 | libraries: 146 | - name: "devtail-ci-lib" 147 | retriever: 148 | modernSCM: 149 | scm: 150 | git: 151 | remote: "git@github.com:devtail/jenkins-as-code.git" 152 | credentialsId: "deploy-key-shared-library" 153 | 154 | simple-theme-plugin: 155 | elements: 156 | - cssUrl: 157 | url: "https://fishi.devtail.io/content-images/jenkins-devtail-theme.css" 158 | 159 | slackNotifier: 160 | teamDomain: "devtail" 161 | baseUrl: "https://devtail.slack.com/services/hooks/jenkins-ci/" 162 | tokenCredentialId: "slack-token" 163 | 164 | githubpluginconfig: 165 | configs: 166 | - name: "Github" 167 | credentialsId: "github-ci-token" 168 | manageHooks: true 169 | -------------------------------------------------------------------------------- /resources/config/groovy/auth.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | /* 4 | * This script configures Github OAuth access 5 | * in Jenkins. It uses a global authorization 6 | * Matrix strategy as authorization configurtion. 7 | * This script requires the Gibhub Authentication 8 | * plugin (github-oauth) to be installed. It is 9 | * tested with github-oauth:0.29 10 | */ 11 | 12 | import hudson.security.SecurityRealm 13 | import org.jenkinsci.plugins.GithubSecurityRealm 14 | import jenkins.model.* 15 | import hudson.security.* 16 | 17 | // Setup OAUTH Realm 18 | String githubWebUri = GithubSecurityRealm.DEFAULT_WEB_URI 19 | String githubApiUri = GithubSecurityRealm.DEFAULT_API_URI 20 | String oauthScopes = 'read:org,user:email' //GithubSecurityRealm.DEFAULT_OAUTH_SCOPES 21 | String clientID = System.getenv()['GITHUB_OAUTH_CLIENT_ID'] // TODO: get from credential store 22 | String clientSecret = System.getenv()['GITHUB_OAUTH_CLIENT_SECRET'] // TODO: get from credential store 23 | 24 | SecurityRealm github_realm = new GithubSecurityRealm(githubWebUri, githubApiUri, clientID, clientSecret, oauthScopes) 25 | Jenkins.instance.setSecurityRealm(github_realm) 26 | 27 | // Create global authorization matrix 28 | def strategy = new GlobalMatrixAuthorizationStrategy() 29 | 30 | // Give admin access to fishi0x01 user 31 | strategy.add(Jenkins.ADMINISTER, "fishi0x01") 32 | 33 | // Give admin access to my_team_name 34 | strategy.add(Jenkins.ADMINISTER, "devtail*Admin") 35 | 36 | // wrap up 37 | Jenkins.instance.setAuthorizationStrategy(strategy) 38 | Jenkins.instance.save() 39 | -------------------------------------------------------------------------------- /resources/config/groovy/baseURL.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | /* 4 | * This script configures the Jenkins base URL. 5 | */ 6 | 7 | import jenkins.model.JenkinsLocationConfiguration 8 | 9 | JenkinsLocationConfiguration location = Jenkins.instance.getExtensionList('jenkins.model.JenkinsLocationConfiguration')[0] 10 | location.url = 'http://192.168.99.100:30001/' // minikube:jenkins-node-port 11 | location.save() 12 | -------------------------------------------------------------------------------- /resources/config/groovy/credentials.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | /* 4 | * This script loads different kinds 5 | * of credentials from files into the 6 | * Jenkins credential store. 7 | * 8 | * https://gist.github.com/fishi0x01/7c2d29afbaa0f16126eb4d4b35942f76#file-credentials-groovy 9 | */ 10 | 11 | import jenkins.model.* 12 | import com.cloudbees.plugins.credentials.* 13 | import com.cloudbees.plugins.credentials.common.* 14 | import com.cloudbees.plugins.credentials.domains.* 15 | import com.cloudbees.plugins.credentials.impl.* 16 | import com.cloudbees.jenkins.plugins.sshcredentials.impl.* 17 | import org.jenkinsci.plugins.plaincredentials.* 18 | import org.jenkinsci.plugins.plaincredentials.impl.* 19 | import hudson.util.Secret 20 | 21 | /////////////////// 22 | // Helper functions 23 | /////////////////// 24 | def getStore() { 25 | return Jenkins.instance.getExtensionList('com.cloudbees.plugins.credentials.SystemCredentialsProvider')[0].getStore() 26 | } 27 | 28 | def getContent(filePath) { 29 | return new File(filePath).text 30 | } 31 | 32 | // This function reads the contents of a key file and returns 33 | // a Jenkins SSH private key object with the given user as owner 34 | def getSSHKeyCredential(id, path, user) { 35 | return new BasicSSHUserPrivateKey( 36 | CredentialsScope.GLOBAL, 37 | id, 38 | user, 39 | new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(getContent(path)), 40 | "", 41 | "SSH key ${id}" 42 | ) 43 | } 44 | 45 | // Get master credential store 46 | domain = Domain.global() 47 | 48 | ////////////////////////////// 49 | // Add username/password pairs 50 | ////////////////////////////// 51 | userPasswords = [ 52 | [id: 'github-ci-user', description: 'GitHub CI User Credentials', userNameFile: '/var/jenkins_home/jenkins-basic-auth-credentials/github-ci-user', userPasswordFile: '/var/jenkins_home/jenkins-basic-auth-credentials/github-ci-password'], 53 | ] 54 | 55 | for(userPassword in userPasswords) { 56 | Credentials cred = (Credentials) new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, userPassword.id, userPassword.description, getContent(userPassword.userNameFile), getContent(userPassword.userPasswordFile)) 57 | getStore().addCredentials(domain, cred) 58 | } 59 | 60 | ///////////// 61 | // Add tokens 62 | ///////////// 63 | secretTokens = [ 64 | [id: 'slack-token', description: 'Slack Token', tokenFile: '/var/jenkins_home/jenkins-tokens/slack-token'], 65 | [id: 'github-ci-token', description: 'Github CI User Token', tokenFile: '/var/jenkins_home/jenkins-tokens/github-ci-token'], 66 | [id: 'github-oauth-client-id', description: 'Github OAuth Client ID', tokenFile: '/var/jenkins_home/jenkins-tokens/github-oauth-client-id'], 67 | [id: 'github-oauth-client-secret', description: 'Github OAuth Client Secret', tokenFile: '/var/jenkins_home/jenkins-tokens/github-oauth-client-secret'], 68 | ] 69 | 70 | for(secretToken in secretTokens) { 71 | Credentials token = (Credentials) new StringCredentialsImpl(CredentialsScope.GLOBAL, secretToken.id, secretToken.description, Secret.fromString(getContent(secretToken.tokenFile))) 72 | getStore().addCredentials(domain, token) 73 | } 74 | 75 | /////////////// 76 | // Add ssh keys 77 | /////////////// 78 | sshKeys = [ 79 | [id: 'ssh-global-shared-library', path: '/var/jenkins_home/jenkins-ssh-keys/deploy-key-shared-library', user: 'root'], 80 | ] 81 | 82 | for(sshKey in sshKeys) { 83 | getStore().addCredentials(domain, getSSHKeyCredential(sshKey.id, sshKey.path, sshKey.user)) 84 | } 85 | -------------------------------------------------------------------------------- /resources/config/groovy/github.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | /* 4 | * This script configures the GitHub Plugin. 5 | * Requires the GitHub Plugin to be installed. 6 | * Tested with github:1.29.3 7 | */ 8 | 9 | import jenkins.model.Jenkins 10 | import org.jenkinsci.plugins.github.config.GitHubPluginConfig 11 | import org.jenkinsci.plugins.github.config.GitHubServerConfig 12 | 13 | def githubConfig = new GitHubServerConfig("github-ci-token") // credential ID for our user token for the GitHub CI User 14 | githubConfig.setManageHooks(true) 15 | githubConfig.setName("GitHub") 16 | def github = Jenkins.instance.getExtensionList(GitHubPluginConfig.class)[0] 17 | github.setConfigs([ 18 | githubConfig, 19 | ]) 20 | github.save() 21 | -------------------------------------------------------------------------------- /resources/config/groovy/globalEnvVars.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | /* 4 | * This script configures global environment variables in Jenkins 5 | */ 6 | 7 | import jenkins.model.Jenkins 8 | import hudson.EnvVars; 9 | import hudson.slaves.EnvironmentVariablesNodeProperty; 10 | import hudson.slaves.NodeProperty; 11 | import hudson.slaves.NodePropertyDescriptor; 12 | import hudson.util.DescribableList; 13 | 14 | def createGlobalEnvironmentVariables(String key, String value){ 15 | 16 | Jenkins instance = Jenkins.getInstance(); 17 | 18 | DescribableList, NodePropertyDescriptor> globalNodeProperties = instance.getGlobalNodeProperties(); 19 | List envVarsNodePropertyList = globalNodeProperties.getAll(EnvironmentVariablesNodeProperty.class); 20 | 21 | EnvironmentVariablesNodeProperty newEnvVarsNodeProperty = null; 22 | EnvVars envVars = null; 23 | 24 | if ( envVarsNodePropertyList == null || envVarsNodePropertyList.size() == 0 ) { 25 | newEnvVarsNodeProperty = new hudson.slaves.EnvironmentVariablesNodeProperty(); 26 | globalNodeProperties.add(newEnvVarsNodeProperty); 27 | envVars = newEnvVarsNodeProperty.getEnvVars(); 28 | } else { 29 | envVars = envVarsNodePropertyList.get(0).getEnvVars(); 30 | } 31 | envVars.put(key, value) 32 | instance.save() 33 | } 34 | 35 | createGlobalEnvironmentVariables('DEBUG', 'True') 36 | -------------------------------------------------------------------------------- /resources/config/groovy/globalSharedLibrary.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | /* 4 | * Configure pipeline shared libraries in the global Jenkins configuration. 5 | * This will safely compare configured libraries and only overwrite the global 6 | * shared library config if changes have been made. 7 | * workflow-cps-global-lib:2.12 8 | * 9 | * Source: https://github.com/thbkrkr/jks/blob/master/init.groovy.d/11-configure-pipeline-global-shared.groovy 10 | */ 11 | import jenkins.model.Jenkins 12 | import jenkins.plugins.git.GitSCMSource 13 | import jenkins.plugins.git.traits.BranchDiscoveryTrait 14 | import org.jenkinsci.plugins.workflow.libs.GlobalLibraries 15 | import org.jenkinsci.plugins.workflow.libs.LibraryConfiguration 16 | import org.jenkinsci.plugins.workflow.libs.SCMSourceRetriever 17 | 18 | List libraries = [] as ArrayList 19 | 20 | def remote = 'git@github.com:devtail/jenkins-as-code.git' 21 | def credentialsId = 'deploy-key-shared-library' 22 | 23 | name = 'devtail-ci' 24 | defaultVersion = 'master' 25 | 26 | if (remote != null) { 27 | 28 | def scm = new GitSCMSource(remote) 29 | if (credentialsId != null) { 30 | scm.credentialsId = credentialsId 31 | } 32 | 33 | scm.traits = [new BranchDiscoveryTrait()] 34 | def retriever = new SCMSourceRetriever(scm) 35 | 36 | def library = new LibraryConfiguration(name, retriever) 37 | library.defaultVersion = defaultVersion 38 | library.implicit = false 39 | library.allowVersionOverride = true 40 | library.includeInChangesets = false 41 | 42 | libraries << library 43 | 44 | def global_settings = Jenkins.instance.getExtensionList(GlobalLibraries.class)[0] 45 | global_settings.libraries = libraries 46 | global_settings.save() 47 | println 'Configured Pipeline Global Shared Libraries:\n ' + global_settings.libraries.collect { it.name }.join('\n ') 48 | } 49 | -------------------------------------------------------------------------------- /resources/config/groovy/securitySettings.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | /* 4 | * Configure general security settings. 5 | */ 6 | import jenkins.model.Jenkins 7 | import hudson.security.csrf.DefaultCrumbIssuer 8 | import jenkins.security.s2m.AdminWhitelistRule 9 | 10 | 11 | // Disable Remote CLI 12 | Jenkins.instance.getDescriptor("jenkins.CLI").get().setEnabled(false) 13 | 14 | // CSRF Issuer 15 | if(Jenkins.instance.getCrumbIssuer() == null) { 16 | Jenkins.instance.setCrumbIssuer(new DefaultCrumbIssuer(true)) 17 | } 18 | 19 | // agent to master security subsystem 20 | Jenkins.instance.getInjector().getInstance(AdminWhitelistRule.class).setMasterKillSwitch(false) 21 | 22 | // sshd settings 23 | def sshDesc = Jenkins.instance.getDescriptor("org.jenkinsci.main.modules.sshd.SSHD") 24 | sshDesc.setPort(6666) 25 | sshDesc.getActualPort() 26 | sshDesc.save() 27 | 28 | Jenkins.instance.save() 29 | -------------------------------------------------------------------------------- /resources/config/groovy/slack.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | /* 4 | * This script configures the Jenkins Slack Plugin. 5 | * Requires the installation of the Slack Plugin. 6 | * Tested with slack:2.14 7 | */ 8 | 9 | import jenkins.model.Jenkins 10 | 11 | def slack = Jenkins.instance.getExtensionList('jenkins.plugins.slack.SlackNotifier$DescriptorImpl')[0] 12 | slack.tokenCredentialId = 'slack-token' 13 | slack.teamDomain = 'devtail' 14 | slack.baseUrl = 'https://devtail.slack.com/services/hooks/jenkins-ci/' 15 | slack.save() 16 | -------------------------------------------------------------------------------- /resources/config/groovy/theme.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | /* 4 | * This script configures the simple theme plugin. 5 | * Requires the simple theme plugin to be installed. 6 | * Tested with simple-theme-plugin:0.5.1 7 | * 8 | * Use http://afonsof.com/jenkins-material-theme/ to generate a new jenkins theme. 9 | * Place the theme at the userContent directory of Jenkins to be publicly available 10 | */ 11 | 12 | import jenkins.model.Jenkins 13 | import org.jenkinsci.plugins.simpletheme.CssUrlThemeElement 14 | 15 | 16 | def themeDecorator = Jenkins.instance.getExtensionList(org.codefirst.SimpleThemeDecorator.class).first() 17 | 18 | themeDecorator.setElements([ 19 | new CssUrlThemeElement('https://fishi.devtail.io/content-images/jenkins-devtail-theme.css') 20 | ]) 21 | 22 | Jenkins.instance.save() 23 | 24 | -------------------------------------------------------------------------------- /resources/config/groovy/timezone.groovy: -------------------------------------------------------------------------------- 1 | #! groovy 2 | 3 | /* 4 | * This script configures the timezone in Jenkins 5 | */ 6 | 7 | System.setProperty('org.apache.commons.jelly.tags.fmt.timeZone', 'Europe/Berlin') 8 | -------------------------------------------------------------------------------- /resources/config/groovy/triggerConfigurationAsCodePlugin.groovy: -------------------------------------------------------------------------------- 1 | #! groovy 2 | 3 | import jenkins.model.Jenkins 4 | 5 | // trigger configuration 6 | def jcacPlugin = Jenkins.instance.getExtensionList(io.jenkins.plugins.casc.ConfigurationAsCode.class).first() 7 | jcacPlugin.configure() 8 | -------------------------------------------------------------------------------- /resources/config/groovy/userPublicKeys.groovy: -------------------------------------------------------------------------------- 1 | #! groovy 2 | 3 | def getFileContent(filePath) { 4 | return new File(filePath).text 5 | } 6 | 7 | def user = hudson.model.User.get('admin') 8 | 9 | def pubKey = new org.jenkinsci.main.modules.cli.auth.ssh.UserPropertyImpl(getFileContent('/var/jenkins_home/jenkins-ssh-keys/ssh-agent-access-key.pub')) 10 | user.addProperty(pubKey) 11 | 12 | user.save() 13 | -------------------------------------------------------------------------------- /resources/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM jenkins/jenkins:2.204.1 2 | 3 | ENV ARCH=linux_amd64 \ 4 | VAULT_VERSION=1.3.1 \ 5 | TERRAFORM_VERSION=0.12.18 6 | 7 | # Delay for CasC + disable install wizard 8 | ENV JAVA_OPTS="-Dio.jenkins.plugins.casc.ConfigurationAsCode.initialDelay=9000 -Djenkins.install.runSetupWizard=false -Dorg.jenkinsci.main.modules.sshd.SSHD.hostName=127.0.0.1" 9 | 10 | # JCasC Plugin pointer to config/secret values 11 | ENV SECRETS="/var/jenkins_home/" 12 | 13 | USER root 14 | 15 | # Install deps 16 | RUN apt-get update -y \ 17 | && apt-get install -y build-essential rsync 18 | 19 | # Add Vault client 20 | RUN curl -sL https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_${ARCH}.zip -o /tmp/vault_${VAULT_VERSION}_${ARCH}.zip \ 21 | && curl -sL https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_SHA256SUMS -o /tmp/vault_${VAULT_VERSION}_SHA256SUMS \ 22 | && export CUR_DIR=$(pwd) \ 23 | && cd /tmp \ 24 | && sha256sum -c vault_${VAULT_VERSION}_SHA256SUMS 2>&1 | grep OK \ 25 | && cd ${CUR_DIR} 26 | 27 | RUN unzip /tmp/vault_${VAULT_VERSION}_${ARCH}.zip -d /bin 28 | RUN rm -f /tmp/vault_${VAULT_VERSION}_${ARCH}.zip \ 29 | && rm -f /tmp/vault_${VAULT_VERSION}_SHA256SUMS 30 | 31 | # Add Terraform client 32 | RUN curl -sL https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_${ARCH}.zip -o /tmp/terraform_${TERRAFORM_VERSION}_${ARCH}.zip \ 33 | && curl -sL https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_SHA256SUMS -o /tmp/terraform_${TERRAFORM_VERSION}_SHA256SUMS \ 34 | && export CUR_DIR=$(pwd) \ 35 | && cd /tmp \ 36 | && sha256sum -c terraform_${TERRAFORM_VERSION}_SHA256SUMS 2>&1 | grep OK \ 37 | && cd ${CUR_DIR} 38 | 39 | RUN unzip /tmp/terraform_${TERRAFORM_VERSION}_${ARCH}.zip -d /bin 40 | RUN rm -f /tmp/terraform_${TERRAFORM_VERSION}_${ARCH}.zip \ 41 | && rm -f /tmp/terraform_${TERRAFORM_VERSION}_SHA256SUMS 42 | 43 | USER jenkins 44 | 45 | # Add minimum jenkins setup 46 | ADD init.groovy.d /usr/share/jenkins/ref/init.groovy.d 47 | ADD dsl /usr/share/jenkins/ref/dsl 48 | COPY scriptApproval.xml /var/jenkins_home/scriptApproval.xml 49 | 50 | # Install plugins 51 | COPY plugins.txt /usr/share/jenkins/ref/plugins.txt 52 | RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/ref/plugins.txt 53 | 54 | -------------------------------------------------------------------------------- /resources/docker/dsl/ConfigurationAndSeedingPipelineDSL.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | folder('Admin') { 4 | description('Folder containing configuration and seed jobs') 5 | } 6 | 7 | pipelineJob("Admin/Configure") { 8 | parameters { 9 | gitParam('revision') { 10 | type('BRANCH_TAG') 11 | sortMode('ASCENDING_SMART') 12 | defaultValue('origin/master') 13 | } 14 | } 15 | 16 | triggers { 17 | githubPush() 18 | } 19 | 20 | logRotator { 21 | numToKeep(20) 22 | } 23 | 24 | definition { 25 | cpsScm { 26 | scm { 27 | git { 28 | remote { 29 | github("devtail/jenkins-as-code", "ssh") 30 | credentials("deploy-key-shared-library") 31 | } 32 | 33 | branch('$revision') 34 | } 35 | } 36 | 37 | scriptPath('resources/init/ConfigurationAndSeedingPipeline.groovy') 38 | } 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /resources/docker/init.groovy.d/init.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | /* 4 | * This script is designated for the init.groovy.d 5 | * directory to be executed at startup time of the 6 | * Jenkins instance. This script requires the jobDSL 7 | * Plugin. Tested with job-dsl:1.70 8 | */ 9 | 10 | import javaposse.jobdsl.dsl.DslScriptLoader 11 | import javaposse.jobdsl.plugin.JenkinsJobManagement 12 | import jenkins.model.Jenkins 13 | import com.cloudbees.plugins.credentials.domains.Domain 14 | import com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey 15 | import com.cloudbees.plugins.credentials.CredentialsScope 16 | import hudson.security.FullControlOnceLoggedInAuthorizationStrategy 17 | import hudson.security.HudsonPrivateSecurityRealm 18 | 19 | 20 | // Add deploy key for the centrally shared pipeline and configuration repository 21 | def domain = Domain.global() 22 | def store = Jenkins.instance.getExtensionList('com.cloudbees.plugins.credentials.SystemCredentialsProvider')[0].getStore() 23 | def keyFileContents = new File("/var/jenkins_home/jenkins-ssh-keys/deploy-key-shared-library").text 24 | def privateKey = new BasicSSHUserPrivateKey( 25 | CredentialsScope.GLOBAL, 26 | "deploy-key-shared-library", 27 | "root", 28 | new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(keyFileContents), 29 | "", 30 | "SSH key for shared-library" 31 | ) 32 | store.addCredentials(domain, privateKey) 33 | 34 | // Create the configuration pipeline from a jobDSL script 35 | def jobDslScript = new File('/var/jenkins_home/dsl/ConfigurationAndSeedingPipelineDSL.groovy') 36 | def workspace = new File('.') 37 | def jobManagement = new JenkinsJobManagement(System.out, [:], workspace) 38 | new DslScriptLoader(jobManagement).runScript(jobDslScript.text) 39 | 40 | // Disable Wizards 41 | if(Jenkins.instance.getSecurityRealm().getClass().getSimpleName() == 'None') { 42 | def instance = Jenkins.getInstance() 43 | def setupUser = new File("/var/jenkins_home/jenkins-basic-auth-credentials/default-setup-user").text.trim() 44 | def setupPass = new File("/var/jenkins_home/jenkins-basic-auth-credentials/default-setup-password").text.trim() 45 | 46 | def hudsonRealm = new HudsonPrivateSecurityRealm(false) 47 | instance.setSecurityRealm(hudsonRealm) 48 | def user = instance.getSecurityRealm().createAccount(setupUser, setupPass) 49 | user.save() 50 | 51 | def strategy = new FullControlOnceLoggedInAuthorizationStrategy() 52 | strategy.setAllowAnonymousRead(false) 53 | instance.setAuthorizationStrategy(strategy) 54 | 55 | instance.save() 56 | 57 | println("SetupWizard Disabled") 58 | } 59 | -------------------------------------------------------------------------------- /resources/docker/plugins.txt: -------------------------------------------------------------------------------- 1 | jdk-tool 2 | command-launcher 3 | configuration-as-code 4 | configuration-as-code-support 5 | uno-choice 6 | github-oauth 7 | workflow-cps-global-lib 8 | slack 9 | github 10 | throttle-concurrents 11 | ansicolor 12 | mask-passwords 13 | build-user-vars-plugin 14 | git-parameter 15 | authorize-project 16 | job-dsl 17 | simple-theme-plugin 18 | build-name-setter 19 | cloudbees-folder 20 | conditional-buildstep 21 | credentials-binding 22 | credentials 23 | dashboard-view 24 | display-url-api 25 | docker-commons 26 | docker-workflow 27 | durable-task 28 | email-ext 29 | emailext-template 30 | embeddable-build-status 31 | external-monitor-job 32 | git-client 33 | git-server 34 | git 35 | github-api 36 | github-branch-source 37 | handlebars 38 | hashicorp-vault-plugin 39 | icon-shim 40 | jackson2-api 41 | javadoc 42 | jquery-detached 43 | jquery 44 | jsch 45 | junit 46 | mailer 47 | matrix-auth 48 | matrix-project 49 | maven-plugin 50 | momentjs 51 | pam-auth 52 | parameterized-trigger 53 | pipeline-build-step 54 | pipeline-github-lib 55 | pipeline-graph-analysis 56 | pipeline-input-step 57 | pipeline-milestone-step 58 | pipeline-model-api 59 | pipeline-model-declarative-agent 60 | pipeline-model-definition 61 | pipeline-model-extensions 62 | pipeline-rest-api 63 | pipeline-stage-step 64 | pipeline-stage-tags-metadata 65 | pipeline-stage-view 66 | pipeline-utility-steps 67 | plain-credentials 68 | resource-disposer 69 | role-strategy 70 | scm-api 71 | script-security 72 | ssh-credentials 73 | ssh-slaves 74 | slave-setup 75 | ssh 76 | structs 77 | timestamper 78 | token-macro 79 | workflow-aggregator 80 | workflow-api 81 | workflow-basic-steps 82 | workflow-cps 83 | workflow-durable-task-step 84 | workflow-job 85 | workflow-multibranch 86 | workflow-scm-step 87 | workflow-step-api 88 | workflow-support 89 | ws-cleanup 90 | -------------------------------------------------------------------------------- /resources/docker/scriptApproval.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | field jenkins.plugins.slack.SlackNotifier$DescriptorImpl baseUrl 6 | field jenkins.plugins.slack.SlackNotifier$DescriptorImpl teamDomain 7 | field jenkins.plugins.slack.SlackNotifier$DescriptorImpl tokenCredentialId 8 | method com.cloudbees.plugins.credentials.CredentialsStore addCredentials com.cloudbees.plugins.credentials.domains.Domain com.cloudbees.plugins.credentials.Credentials 9 | method com.cloudbees.plugins.credentials.SystemCredentialsProvider getStore 10 | method groovy.lang.GroovyObject invokeMethod java.lang.String java.lang.Object 11 | method hudson.model.Descriptor load 12 | method hudson.model.Saveable save 13 | method hudson.slaves.EnvironmentVariablesNodeProperty getEnvVars 14 | method hudson.util.PersistedList getAll java.lang.Class 15 | method java.lang.Class isInstance java.lang.Object 16 | method jenkins.model.Jenkins addNode hudson.model.Node 17 | method jenkins.model.Jenkins getDescriptor java.lang.String 18 | method jenkins.model.Jenkins getExtensionList java.lang.Class 19 | method jenkins.model.Jenkins getExtensionList java.lang.String 20 | method jenkins.model.Jenkins getGlobalNodeProperties 21 | method jenkins.model.Jenkins setAuthorizationStrategy hudson.security.AuthorizationStrategy 22 | method jenkins.model.Jenkins setSecurityRealm hudson.security.SecurityRealm 23 | method jenkins.model.JenkinsLocationConfiguration setUrl java.lang.String 24 | method jenkins.plugins.git.GitSCMSource setCredentialsId java.lang.String 25 | method jenkins.plugins.git.GitSCMSource setTraits java.util.List 26 | method jenkins.scm.api.SCMSource setTraits java.util.List 27 | method org.codefirst.SimpleThemeDecorator setCssUrl java.lang.String 28 | method org.jenkinsci.main.modules.sshd.SSHD getActualPort 29 | method org.jenkinsci.main.modules.sshd.SSHD setPort int 30 | method org.jenkinsci.plugins.github.config.GitHubPluginConfig setConfigs java.util.List 31 | method org.jenkinsci.plugins.github.config.GitHubServerConfig setManageHooks boolean 32 | method org.jenkinsci.plugins.github.config.GitHubServerConfig setName java.lang.String 33 | method org.jenkinsci.plugins.matrixauth.AuthorizationContainer add hudson.security.Permission java.lang.String 34 | method org.jenkinsci.plugins.workflow.libs.GlobalLibraries getLibraries 35 | method org.jenkinsci.plugins.workflow.libs.GlobalLibraries setLibraries java.util.List 36 | method org.jenkinsci.plugins.workflow.libs.LibraryConfiguration getName 37 | method org.jenkinsci.plugins.workflow.libs.LibraryConfiguration setAllowVersionOverride boolean 38 | method org.jenkinsci.plugins.workflow.libs.LibraryConfiguration setDefaultVersion java.lang.String 39 | method org.jenkinsci.plugins.workflow.libs.LibraryConfiguration setImplicit boolean 40 | method org.jenkinsci.plugins.workflow.libs.LibraryConfiguration setIncludeInChangesets boolean 41 | method org.codefirst.SimpleThemeDecorator setElements java.util.List 42 | method jenkins.CLI get 43 | method jenkins.CLI setEnabled boolean 44 | method jenkins.model.Jenkins getCrumbIssuer 45 | method jenkins.model.Jenkins setCrumbIssuer hudson.security.csrf.CrumbIssuer 46 | method jenkins.model.Jenkins getInjector 47 | method com.google.inject.Injector getInstance java.lang.Class 48 | method jenkins.security.s2m.AdminWhitelistRule setMasterKillSwitch boolean 49 | method io.jenkins.plugins.casc.ConfigurationAsCode configure 50 | method hudson.model.User addProperty hudson.model.UserProperty 51 | new hudson.security.csrf.DefaultCrumbIssuer boolean 52 | new com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey com.cloudbees.plugins.credentials.CredentialsScope java.lang.String java.lang.String com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$PrivateKeySource java.lang.String java.lang.String 53 | new com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource java.lang.String 54 | new com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$FileOnMasterPrivateKeySource java.lang.String 55 | new com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl com.cloudbees.plugins.credentials.CredentialsScope java.lang.String java.lang.String java.lang.String java.lang.String 56 | new hudson.plugins.sshslaves.SSHLauncher java.lang.String int com.cloudbees.plugins.credentials.common.StandardUsernameCredentials java.lang.String java.lang.String hudson.tools.JDKInstaller java.lang.String java.lang.String java.lang.Integer java.lang.Integer java.lang.Integer 57 | new hudson.plugins.sshslaves.SSHLauncher java.lang.String int java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String java.lang.Integer java.lang.Integer java.lang.Integer hudson.plugins.sshslaves.verifiers.SshHostKeyVerificationStrategy 58 | new hudson.plugins.sshslaves.verifiers.NonVerifyingKeyVerificationStrategy 59 | new hudson.security.GlobalMatrixAuthorizationStrategy 60 | new hudson.slaves.DumbSlave java.lang.String java.lang.String java.lang.String java.lang.String hudson.model.Node$Mode java.lang.String hudson.slaves.ComputerLauncher hudson.slaves.RetentionStrategy java.util.List 61 | new hudson.slaves.EnvironmentVariablesNodeProperty hudson.slaves.EnvironmentVariablesNodeProperty$Entry[] 62 | new hudson.slaves.RetentionStrategy$Always 63 | new hudson.slaves.RetentionStrategy$Demand long long 64 | new java.io.File java.lang.String 65 | new java.util.LinkedList 66 | new jenkins.plugins.git.GitSCMSource java.lang.String 67 | new jenkins.plugins.git.traits.BranchDiscoveryTrait 68 | new org.jenkinsci.plugins.GithubSecurityRealm java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String 69 | new org.jenkinsci.plugins.github.config.GitHubServerConfig java.lang.String 70 | new org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl com.cloudbees.plugins.credentials.CredentialsScope java.lang.String java.lang.String hudson.util.Secret 71 | new org.jenkinsci.plugins.slave_setup.SetupSlaveLauncher hudson.slaves.ComputerLauncher java.lang.String java.lang.String 72 | new org.jenkinsci.plugins.workflow.libs.LibraryConfiguration java.lang.String org.jenkinsci.plugins.workflow.libs.LibraryRetriever 73 | new org.jenkinsci.plugins.workflow.libs.SCMSourceRetriever jenkins.scm.api.SCMSource 74 | new org.jenkinsci.plugins.simpletheme.CssUrlThemeElement java.lang.String 75 | new org.jenkinsci.main.modules.cli.auth.ssh.UserPropertyImpl java.lang.String 76 | staticField com.cloudbees.plugins.credentials.CredentialsScope GLOBAL 77 | staticField hudson.model.Node$Mode EXCLUSIVE 78 | staticField jenkins.model.Jenkins ADMINISTER 79 | staticField org.jenkinsci.plugins.GithubSecurityRealm DEFAULT_API_URI 80 | staticField org.jenkinsci.plugins.GithubSecurityRealm DEFAULT_WEB_URI 81 | staticMethod com.cloudbees.plugins.credentials.domains.Domain global 82 | staticMethod hudson.model.PageDecorator all 83 | staticMethod hudson.model.User get java.lang.String boolean java.util.Map 84 | staticMethod hudson.plugins.sshslaves.SSHLauncher lookupSystemCredentials java.lang.String 85 | staticMethod hudson.util.Secret fromString java.lang.String 86 | staticMethod java.lang.System getenv 87 | staticMethod java.lang.System setProperty java.lang.String java.lang.String 88 | staticMethod jenkins.model.Jenkins getInstance 89 | staticMethod org.apache.commons.io.FileUtils copyDirectory java.io.File java.io.File 90 | staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods deleteDir java.io.File 91 | staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods getText java.io.File 92 | staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods leftShift java.io.File java.lang.Object 93 | staticMethod hudson.model.User get java.lang.String 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /resources/helm/Chart.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: "v1" 3 | name: "JenkinsAsCode" 4 | version: "0.0.1" 5 | -------------------------------------------------------------------------------- /resources/helm/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Ingress 3 | metadata: 4 | name: jenkins 5 | namespace: default 6 | spec: 7 | rules: 8 | - http: 9 | paths: 10 | - backend: 11 | serviceName: jenkins 12 | servicePort: 80 13 | path: / 14 | 15 | -------------------------------------------------------------------------------- /resources/helm/templates/secrets.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: jenkins-ssh-keys 6 | type: Opaque 7 | data: 8 | deploy-key-shared-library: {{ b64enc (.Files.Get "secret-files/deploy-key-shared-library") }} 9 | ssh-agent-access-key: {{ b64enc (.Files.Get "secret-files/ssh-agent-access-key") }} 10 | ssh-agent-access-key.pub: {{ b64enc (.Files.Get "secret-files/ssh-agent-access-key.pub") }} 11 | 12 | --- 13 | apiVersion: v1 14 | kind: Secret 15 | metadata: 16 | name: jenkins-basic-auth-credentials 17 | type: Opaque 18 | data: 19 | docker-registry-user: {{ b64enc (.Files.Get "secret-files/docker-registry-user") }} 20 | docker-registry-password: {{ b64enc (.Files.Get "secret-files/docker-registry-password") }} 21 | github-ci-user: {{ b64enc (.Files.Get "secret-files/github-ci-user") }} 22 | github-ci-password: {{ b64enc (.Files.Get "secret-files/github-ci-password") }} 23 | default-setup-user: {{ b64enc (.Files.Get "secret-files/default-setup-user") }} 24 | default-setup-password: {{ b64enc (.Files.Get "secret-files/default-setup-password") }} 25 | 26 | --- 27 | apiVersion: v1 28 | kind: Secret 29 | metadata: 30 | name: jenkins-tokens 31 | type: Opaque 32 | data: 33 | slack-token: {{ b64enc (.Files.Get "secret-files/slack-token") }} 34 | github-ci-token: {{ b64enc (.Files.Get "secret-files/github-ci-token") }} 35 | github-oauth-client-id: {{ b64enc (.Files.Get "secret-files/github-oauth-client-id") }} 36 | github-oauth-client-secret: {{ b64enc (.Files.Get "secret-files/github-oauth-client-secret") }} 37 | 38 | --- 39 | apiVersion: v1 40 | kind: Secret 41 | metadata: 42 | name: terraform-config 43 | type: Opaque 44 | data: 45 | {{- (.Files.Glob "secret-files/terraform-config/*").AsSecrets | nindent 2 }} 46 | -------------------------------------------------------------------------------- /resources/helm/templates/services.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Service 3 | apiVersion: v1 4 | metadata: 5 | name: jenkins 6 | spec: 7 | selector: 8 | app: jenkins 9 | ports: 10 | - protocol: TCP 11 | port: 80 12 | targetPort: 8080 13 | -------------------------------------------------------------------------------- /resources/helm/templates/statefulsets.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: StatefulSet 4 | metadata: 5 | name: jenkins 6 | spec: 7 | selector: 8 | matchLabels: 9 | app: jenkins 10 | serviceName: "jenkins" 11 | replicas: 1 12 | template: 13 | metadata: 14 | labels: 15 | app: jenkins 16 | spec: 17 | terminationGracePeriodSeconds: 10 18 | containers: 19 | - name: jenkins 20 | image: quay.io/devtail/jenkins-as-code:master 21 | imagePullPolicy: Always 22 | env: 23 | - name: DEPLOY_TS 24 | value: "{{ date "20060102150405" .Release.Time }}" 25 | - name: GITHUB_OAUTH_CLIENT_ID 26 | valueFrom: 27 | secretKeyRef: 28 | name: jenkins-tokens 29 | key: github-oauth-client-id 30 | - name: GITHUB_OAUTH_CLIENT_SECRET 31 | valueFrom: 32 | secretKeyRef: 33 | name: jenkins-tokens 34 | key: github-oauth-client-secret 35 | ports: 36 | - containerPort: 8080 37 | name: http 38 | volumeMounts: 39 | - name: jenkins-ssh-keys 40 | mountPath: /var/jenkins_home/jenkins-ssh-keys 41 | - name: jenkins-basic-auth-credentials 42 | mountPath: /var/jenkins_home/jenkins-basic-auth-credentials 43 | - name: jenkins-tokens 44 | mountPath: /var/jenkins_home/jenkins-tokens 45 | - name: terraform-config 46 | mountPath: /var/jenkins_home/agent-bootstrapping-terraform-config 47 | volumes: 48 | - name: jenkins-ssh-keys 49 | secret: 50 | secretName: jenkins-ssh-keys 51 | - name: jenkins-basic-auth-credentials 52 | secret: 53 | secretName: jenkins-basic-auth-credentials 54 | - name: jenkins-tokens 55 | secret: 56 | secretName: jenkins-tokens 57 | - name: terraform-config 58 | secret: 59 | secretName: terraform-config 60 | -------------------------------------------------------------------------------- /resources/helm/values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /resources/init/ConfigurationAndSeedingPipeline.groovy: -------------------------------------------------------------------------------- 1 | node('master') { 2 | stage('Checkout') { 3 | // Clean workspace and checkout shared library repository on the jenkins master 4 | cleanWs() 5 | checkout scm 6 | } 7 | 8 | stage('Configuration') { 9 | // set config file in master 10 | sh('cp /var/jenkins_home/workspace/Admin/Configure/resources/config/configuration-as-code-plugin/jenkins.yaml /var/jenkins_home/jenkins.yaml') 11 | 12 | // run configuration from config file 13 | load('resources/config/groovy/triggerConfigurationAsCodePlugin.groovy') 14 | 15 | // set public key for bootstrapping user 16 | load('resources/config/groovy/userPublicKeys.groovy') 17 | 18 | // set the timezone 19 | load('resources/config/groovy/timezone.groovy') 20 | } 21 | 22 | stage('Deploy Agent Networks') { 23 | ansiColor('xterm') { 24 | sh('ln -sfn /var/jenkins_home/agent-bootstrapping-terraform-config/aws-agent-network.backend.config resources/terraform/aws/agent-network/') 25 | sh('ln -sfn /var/jenkins_home/agent-bootstrapping-terraform-config/aws-agent-network.tfvars resources/terraform/aws/agent-network/terraform.tfvars') 26 | sh('cd resources/terraform/ && make deploy-agent-network') 27 | } 28 | } 29 | 30 | stage('Job Seeding') { 31 | jobDsl(targets: 'resources/jobDSL/*.groovy', sandbox: false) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /resources/jobDSL/agentManagement.groovy: -------------------------------------------------------------------------------- 1 | pipelineJob("Admin/AWSAgentBootstrap") { 2 | 3 | parameters { 4 | stringParam('agentID', '', '') 5 | } 6 | 7 | logRotator { 8 | numToKeep(50) 9 | } 10 | 11 | throttleConcurrentBuilds { 12 | maxTotal(1) 13 | } 14 | 15 | definition { 16 | cpsScm { 17 | scm { 18 | git { 19 | remote { 20 | github("devtail/jenkins-as-code", "ssh") 21 | credentials("deploy-key-shared-library") 22 | } 23 | 24 | branch('master') 25 | } 26 | } 27 | 28 | scriptPath('resources/agentManagement/AWSAgentBootstrap.groovy') 29 | } 30 | } 31 | } 32 | 33 | pipelineJob("Admin/AWSAgentDestroy") { 34 | 35 | parameters { 36 | stringParam('agentID', '', '') 37 | } 38 | 39 | logRotator { 40 | numToKeep(50) 41 | } 42 | 43 | throttleConcurrentBuilds { 44 | maxTotal(1) 45 | } 46 | 47 | definition { 48 | cpsScm { 49 | scm { 50 | git { 51 | remote { 52 | github("devtail/jenkins-as-code", "ssh") 53 | credentials("deploy-key-shared-library") 54 | } 55 | 56 | branch('master') 57 | } 58 | } 59 | 60 | scriptPath('resources/agentManagement/AWSAgentDestroy.groovy') 61 | } 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /resources/jobDSL/example.groovy: -------------------------------------------------------------------------------- 1 | projects = [ 2 | [name: "ServiceA"], 3 | [name: "ServiceB"], 4 | [name: "ServiceC"] 5 | ] 6 | 7 | for(project in projects) { 8 | folder("${project.name}") 9 | 10 | pipelineJob("${project.name}/build") { 11 | 12 | logRotator { 13 | numToKeep(50) 14 | } 15 | 16 | definition { 17 | cps { 18 | sandbox(true) 19 | script("""@Library('devtail-ci-lib@master') _ 20 | printDockerVersion() 21 | """) 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /resources/terraform/Makefile: -------------------------------------------------------------------------------- 1 | export AGENT_ID 2 | 3 | deploy-agent-network: 4 | $(MAKE) -C aws/agent-network deploy-agent-network 5 | 6 | bootstrap-agent-vm: 7 | $(MAKE) -C aws/agent-vms bootstrap-agent-vm 8 | 9 | destroy-agent-vm: 10 | $(MAKE) -C aws/agent-vms destroy-agent-vm 11 | -------------------------------------------------------------------------------- /resources/terraform/README.md: -------------------------------------------------------------------------------- 1 | # Terraform for Agent Infrastructure 2 | 3 | ## Place variables/secrets in config/ 4 | 5 | Secrets are not commited to Github ;-) 6 | 7 | Create `config/` dir with secrets/variables for terraform automation. 8 | Directory structure should in the end look like that: 9 | 10 | ``` 11 | terraform/ 12 | ├── aws 13 | │   ├── agent-network 14 | │   │   ├── all.tf 15 | │   │   ├── aws-agent-network.backend.config -> ../../config/aws-agent-network.backend.config 16 | │   │   ├── io.tf 17 | │   │   └── terraform.tfvars -> ../../config/aws-agent-network.tfvars 18 | │   └── agent-vms 19 | │   ├── all.tf 20 | │   ├── aws-agent-vms.backend.config -> ../../config/aws-agent-vms.backend.config 21 | │   ├── io.tf 22 | │   └── terraform.tfvars -> ../../config/aws-agent-vms.tfvars 23 | ├── config 24 | │   ├── aws-agent-network.backend.config 25 | │   ├── aws-agent-network.tfvars 26 | │   ├── aws-agent-vms.backend.config 27 | │   └── aws-agent-vms.tfvars 28 | ├── Makefile 29 | └── README.md 30 | ``` 31 | 32 | ## Agent Network Infrastructure 33 | 34 | To initialize the state: 35 | ``` 36 | cd aws/agent-network 37 | terraform init -backend-config aws-agent-network.backend.config 38 | ``` 39 | 40 | To bootstrap the network: 41 | ``` 42 | cd aws/agent-network 43 | terraform apply 44 | ``` 45 | 46 | ## Agent VMs 47 | 48 | Bootstrap agent-1: 49 | ``` 50 | cd aws/agent-vms 51 | terraform workspace select 0 52 | terraform apply 53 | ``` 54 | 55 | Bootstrap agent-2: 56 | ``` 57 | cd aws/agent-vms 58 | terraform workspace select 1 59 | terraform apply 60 | ``` 61 | -------------------------------------------------------------------------------- /resources/terraform/aws/agent-network/Makefile: -------------------------------------------------------------------------------- 1 | deploy-agent-network: 2 | terraform init -backend-config aws-agent-network.backend.config 3 | terraform apply -auto-approve 4 | -------------------------------------------------------------------------------- /resources/terraform/aws/agent-network/all.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | access_key = "${var.aws_access_key}" 3 | secret_key = "${var.aws_secret_key}" 4 | region = "${var.aws_region}" 5 | } 6 | 7 | terraform { 8 | backend "s3" {} 9 | } 10 | 11 | resource "aws_vpc" "agents" { 12 | cidr_block = "${var.agents_vpc_cidr}" 13 | } 14 | 15 | resource "aws_internet_gateway" "gw" { 16 | vpc_id = "${aws_vpc.agents.id}" 17 | } 18 | 19 | resource "aws_route_table" "agent-routing" { 20 | vpc_id = "${aws_vpc.agents.id}" 21 | } 22 | 23 | resource "aws_route" "internet" { 24 | route_table_id = "${aws_route_table.agent-routing.id}" 25 | destination_cidr_block = "0.0.0.0/0" 26 | gateway_id = "${aws_internet_gateway.gw.id}" 27 | } 28 | 29 | resource "aws_subnet" "agents" { 30 | vpc_id = "${aws_vpc.agents.id}" 31 | cidr_block = "${var.agents_subnet_cidr}" 32 | } 33 | 34 | resource "aws_main_route_table_association" "agent-routing" { 35 | vpc_id = "${aws_vpc.agents.id}" 36 | route_table_id = "${aws_route_table.agent-routing.id}" 37 | } 38 | 39 | resource "aws_security_group" "agents" { 40 | name = "agent_rules" 41 | description = "Agent Rules" 42 | vpc_id = "${aws_vpc.agents.id}" 43 | } 44 | 45 | resource "aws_security_group_rule" "allow-ssh" { 46 | type = "ingress" 47 | from_port = 22 48 | to_port = 22 49 | protocol = "tcp" 50 | cidr_blocks = ["0.0.0.0/0"] 51 | 52 | security_group_id = "${aws_security_group.agents.id}" 53 | } 54 | 55 | resource "aws_security_group_rule" "allow_all" { 56 | type = "egress" 57 | to_port = 0 58 | protocol = "-1" 59 | from_port = 0 60 | cidr_blocks = ["0.0.0.0/0"] 61 | 62 | security_group_id = "${aws_security_group.agents.id}" 63 | } 64 | 65 | resource "aws_key_pair" "agent-access" { 66 | key_name = "devtail-jenkins-agent-access" 67 | public_key = "${var.agent_access_public_key}" 68 | } 69 | 70 | data "aws_eip" "public-agent-ip" { 71 | count = "${length(keys(var.agent_config_map)) / var.num_agents}" 72 | 73 | filter { 74 | name = "tag:Name" 75 | values = ["${var.agent_config_map["${count.index}_name"]}"] 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /resources/terraform/aws/agent-network/aws-agent-network.backend.config: -------------------------------------------------------------------------------- 1 | ../../config/aws-agent-network.backend.config -------------------------------------------------------------------------------- /resources/terraform/aws/agent-network/io.tf: -------------------------------------------------------------------------------- 1 | variable "aws_access_key" {} 2 | variable "aws_secret_key" {} 3 | variable "aws_region" {} 4 | 5 | variable "agents_vpc_cidr" {} 6 | variable "agents_subnet_cidr" {} 7 | 8 | variable "agent_access_public_key" {} 9 | 10 | variable "agent_config_map" { type = "map" } 11 | variable "num_agents" {} 12 | 13 | ### Output 14 | output "agents_subnet_id" { 15 | value = "${aws_subnet.agents.id}" 16 | } 17 | 18 | output "agent_access_key_name" { 19 | value = "${aws_key_pair.agent-access.key_name}" 20 | } 21 | 22 | output "agent_public_ip_ids" { 23 | value = "${data.aws_eip.public-agent-ip.*.id}" 24 | } 25 | 26 | output "agent_config_map" { 27 | value = "${var.agent_config_map}" 28 | } 29 | 30 | output "num_agents" { 31 | value = "${var.num_agents}" 32 | } 33 | 34 | output "agent_security_group_id" { 35 | value = "${aws_security_group.agents.id}" 36 | } 37 | -------------------------------------------------------------------------------- /resources/terraform/aws/agent-network/terraform.tfvars: -------------------------------------------------------------------------------- 1 | ../../config/aws-agent-network.tfvars -------------------------------------------------------------------------------- /resources/terraform/aws/agent-vms/Makefile: -------------------------------------------------------------------------------- 1 | get-workspace: 2 | terraform init -backend-config aws-agent-vms.backend.config 3 | terraform workspace new $(AGENT_ID) || true 4 | terraform workspace select $(AGENT_ID) 5 | 6 | bootstrap-agent-vm: get-workspace 7 | terraform apply -auto-approve 8 | 9 | destroy-agent-vm: get-workspace 10 | terraform destroy -auto-approve 11 | 12 | -------------------------------------------------------------------------------- /resources/terraform/aws/agent-vms/all.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | access_key = "${var.aws_access_key}" 3 | secret_key = "${var.aws_secret_key}" 4 | region = "${var.aws_region}" 5 | } 6 | 7 | terraform { 8 | backend "s3" {} 9 | } 10 | 11 | data "terraform_remote_state" "agent-network" { 12 | backend = "s3" 13 | config { 14 | bucket = "${var.remote_state_agent_network_s3_bucket}" 15 | key = "${var.remote_state_agent_network_s3_key}" 16 | region = "${var.remote_state_agent_network_region}" 17 | access_key = "${var.remote_state_agent_network_access_key}" 18 | secret_key = "${var.remote_state_agent_network_secret_key}" 19 | } 20 | } 21 | 22 | locals { 23 | agent_config_map = "${data.terraform_remote_state.agent-network.agent_config_map}" 24 | num_agents = "${data.terraform_remote_state.agent-network.num_agents}" 25 | } 26 | 27 | data "aws_ami" "ubuntu18-04" { 28 | most_recent = true 29 | 30 | filter { 31 | name = "name" 32 | values = ["ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*"] 33 | } 34 | 35 | filter { 36 | name = "virtualization-type" 37 | values = ["hvm"] 38 | } 39 | 40 | owners = ["099720109477"] # Canonical 41 | } 42 | 43 | resource "aws_instance" "agent" { 44 | ami = "${data.aws_ami.ubuntu18-04.id}" 45 | instance_type = "${local.agent_config_map["${terraform.workspace}_size"]}" 46 | subnet_id = "${data.terraform_remote_state.agent-network.agents_subnet_id}" 47 | key_name = "${data.terraform_remote_state.agent-network.agent_access_key_name}" 48 | user_data = "${file("user_data/docker.sh")}" 49 | vpc_security_group_ids = [ 50 | "${data.terraform_remote_state.agent-network.agent_security_group_id}" 51 | ] 52 | 53 | tags = { 54 | Name = "${local.agent_config_map["${terraform.workspace}_name"]}" 55 | } 56 | } 57 | 58 | resource "aws_eip_association" "agent" { 59 | instance_id = "${aws_instance.agent.id}" 60 | allocation_id = "${data.terraform_remote_state.agent-network.agent_public_ip_ids[terraform.workspace]}" 61 | } 62 | -------------------------------------------------------------------------------- /resources/terraform/aws/agent-vms/aws-agent-vms.backend.config: -------------------------------------------------------------------------------- 1 | ../../config/aws-agent-vms.backend.config -------------------------------------------------------------------------------- /resources/terraform/aws/agent-vms/io.tf: -------------------------------------------------------------------------------- 1 | variable "aws_access_key" {} 2 | variable "aws_secret_key" {} 3 | variable "aws_region" {} 4 | 5 | variable "remote_state_agent_network_s3_bucket" {} 6 | variable "remote_state_agent_network_s3_key" {} 7 | variable "remote_state_agent_network_region" {} 8 | variable "remote_state_agent_network_access_key" {} 9 | variable "remote_state_agent_network_secret_key" {} 10 | -------------------------------------------------------------------------------- /resources/terraform/aws/agent-vms/terraform.tfvars: -------------------------------------------------------------------------------- 1 | ../../config/aws-agent-vms.tfvars -------------------------------------------------------------------------------- /resources/terraform/aws/agent-vms/user_data/docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## agent connectivity 4 | apt-get update -y 5 | apt-get install -y openjdk-8-jdk 6 | 7 | ## docker 8 | apt-get install -y \ 9 | apt-transport-https \ 10 | ca-certificates \ 11 | curl \ 12 | gnupg-agent \ 13 | software-properties-common 14 | 15 | curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - 16 | 17 | add-apt-repository \ 18 | "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ 19 | $(lsb_release -cs) \ 20 | stable" 21 | 22 | apt-get update -y 23 | apt-get install -y docker-ce 24 | 25 | groupadd docker 26 | usermod -aG docker ubuntu 27 | -------------------------------------------------------------------------------- /resources/terraform/config: -------------------------------------------------------------------------------- 1 | ../helm/secret-files/terraform-config -------------------------------------------------------------------------------- /vars/AWSAgentBootstrap.groovy: -------------------------------------------------------------------------------- 1 | def call(Map config) { 2 | node('master') { 3 | stage('Checkout') { 4 | cleanWs() 5 | checkout scm 6 | } 7 | 8 | stage('Terraform') { 9 | ansiColor('xterm') { 10 | sh('ln -sfn /var/jenkins_home/agent-bootstrapping-terraform-config/aws-agent-vms.backend.config resources/terraform/aws/agent-vms/') 11 | sh('ln -sfn /var/jenkins_home/agent-bootstrapping-terraform-config/aws-agent-vms.tfvars resources/terraform/aws/agent-vms/terraform.tfvars') 12 | sh("cd resources/terraform/ && make bootstrap-agent-vm AGENT_ID=${config['agentID']}") 13 | } 14 | 15 | // Wait for cloud-init to bootstrap 16 | sh("sleep 120") 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vars/AWSAgentDestroy.groovy: -------------------------------------------------------------------------------- 1 | def call(Map config) { 2 | node('master') { 3 | stage('Checkout') { 4 | cleanWs() 5 | checkout scm 6 | } 7 | 8 | stage('Terraform') { 9 | ansiColor('xterm') { 10 | sh('ln -sfn /var/jenkins_home/agent-bootstrapping-terraform-config/aws-agent-vms.backend.config resources/terraform/aws/agent-vms/') 11 | sh('ln -sfn /var/jenkins_home/agent-bootstrapping-terraform-config/aws-agent-vms.tfvars resources/terraform/aws/agent-vms/terraform.tfvars') 12 | sh("cd resources/terraform/ && make destroy-agent-vm AGENT_ID=${config['agentID']}") 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /vars/printDockerVersion.groovy: -------------------------------------------------------------------------------- 1 | def call(Map config) { 2 | node('master') { 3 | stage('Hello') { 4 | echo('Hello World') 5 | } 6 | } 7 | 8 | node('docker') { 9 | stage('Version') { 10 | sh('sudo docker version') 11 | } 12 | } 13 | } 14 | --------------------------------------------------------------------------------