How to Create a CloudOps for Kubernetes Extensions Repository
Create an Extensions Repository
An Extensions repository is a Git repository that contains a set of files that are formatted to work within the CloudOps for Kuberentes Extensions framework. These files are placed in the location that follows the repository layout of the Extensions framework. Configure the following three files to create an Extensions repository:
Repository Layout
A typical extensions repository has at least three files. The following sample shows an example of a repository layout:
jenkins/
jobs/
bootstrap/
bootstrap.groovy
test-job/
Jenkinsfile
...
extensionsName
The Extensions repositories that you consume in CloudOps for Kubernetes must follow the repository layout format. If the repositories do not follow this format, errors are returned when using the jobs designed for importing and configuration Extensions repositories.
The following example shows an error that you will receive if you do not include extensionsName
file in the root directory of the Extensions Git repository.
Cannot determine the name of this extensions repository. Ensure there is a file named 'extensionsName' in the root of the extensions Git repository. The file should be a plain text file that contains only your chosen name for this extensions repository.
extensionsName
file
Sample The extensionsName
file is a plain text file that contains the name you choose for your new extensions repository. The contents of the extensionsName
file must follow the formatting conventions of keys that are used in JSON files. This name will be used when the repository is imported to Jenkins to track the configuration of the repository and organize the jobs in the Jenkins user interface.
The following examples shows extensionsName
files that help the import-or-delete-extensions-repository
to identify the string used to identify the configuration that is stored for the Extensions repository.
testExtensionsName
test-extension-name
bootstrap.groovy
script
Sample An Extensions repository requires a Jenkins job Domain Specific Language (DSL) script in order to seed Jenkins jobs stored in the Extensions Git repository. The bootstrap.groovy
file will define the jobs to be imported. The Extensions framework will use the bootstrap.groovy
file in the different stages of importing, configuration, and setup of the Jenkins jobs.
This is an example of an Extensions repository bootstrap.groovy
file that configures an Extensions repository Jenkins job.
// this bit is just plain Jenkins Groovy
import groovy.json.JsonSlurper
// define json slurper
def jsonSlurper = new JsonSlurper();
// this is a mounted volume that contains all of the data stored in the `jenkins-secrets` kubernetes secret
// for more information on what is stored in the `jenkins-secrets` secret, view this code: https://code.elasticpath.com/ep-cloudops/cloud-ops-kubernetes/-/blob/master/bootstrap/terraform/jenkins.tf
def secretsDirectory = new File("/secret/jenkins-secrets").exists() ? new File("/secret/jenkins-secrets") : new File("/var/jenkins_secrets");
// this is a mounted volume that contains all of the data stored in the `extensions-config` kubernetes config map
// this configmap stores the configuration of your extensions repository
// using the configmap, you can ensure that the default values stored in your job reflect your desired configuration of those jobs
def configDirectory = new File("/config/extensions-config").exists() ? new File("/config/extensions-config") : new File("/var/extensions_config");
// Load extensions config
def extensionsConfigPropertiesString = new File(configDirectory, "extensions-config.json").text.trim();
def extensionsConfigPropertiesJsonObject = jsonSlurper.parseText(extensionsConfigPropertiesString);
// define the extensions name, this is the same as the extensionsName file in the root directory
// be sure to change this if your extensionsName file is not `test-extras`
def extensionsName = "test-extras";
// if there is no valid configuration stored for the ${extensionsName} value in the configmap, do not setup any jobs
def extensionsConfigPropertiesExists = extensionsConfigPropertiesJsonObject.extensions_repos.containsKey(extensionsName);
if (extensionsConfigPropertiesExists){
// Set up configuration for git repos
// this is configuration information that is managed by the import and configure jobs
// without this information, your Extensions repository jobs will not follow the current state of the Extensions repository configuration
def testExtrasGitRepoConfig = extensionsConfigPropertiesJsonObject.extensions_repos.getAt("${extensionsName}");
def testExtrasGitRepoURL = extensionsConfigPropertiesJsonObject.extensions_repos.getAt("${extensionsName}").repo_url.toString().trim();
def testExtrasGitRepoDefaultBranch = extensionsConfigPropertiesJsonObject.extensions_repos.getAt("${extensionsName}").default_branch.toString().trim();
def testExtrasGitRepoGroovyPath = extensionsConfigPropertiesJsonObject.extensions_repos.getAt("${extensionsName}").bootstrap_dsl_groovy_path.toString().trim();
// this is the default git credential id that is created to access CloudOps for kubernetes
// this can be changed if your Extensions repository requires a different credential than what is used to clone CloudOps for Kubernetes
def testExtrasGitRepoCredentialId = "gitCredentialId";
// this is configuration information related to the state of the CloudOps for Kuberetes cluster
// without this information, you will not be able to pull CloudOps for Kubernetes resources in your Extensions repository jobs
def cloudOpsForKubernetesRepoURL = new File(secretsDirectory, "cloudOpsForKubernetesRepoURL").text.trim();
def cloudOpsForKubernetesDefaultBranch = new File(secretsDirectory, "cloudOpsForKubernetesDefaultBranch").text.trim();
// define the hello-world job
pipelineJob('hello-world') {
definition {
cpsScm {
scm {
git {
remote {
name('origin')
url(testExtrasGitRepoURL)
credentials(testExtrasGitRepoCredentialId)
}
extensions {
cloneOptions {
shallow(true)
depth(1)
}
relativeTargetDirectory("${extensionsName}")
}
branch('${testExtrasGitRepoBranch}')
}
}
lightweight(false)
scriptPath("${extensionsName}/jenkins/jobs/hello-world/Jenkinsfile")
}
}
parameters {
stringParam('testExtrasGitRepoURL', testExtrasGitRepoURL, 'The repo URL of the job.')
stringParam('testExtrasGitRepoBranch', testExtrasGitRepoDefaultBranch, 'The branch to run the job on.')
stringParam('cloudOpsForKubernetesRepoURL', cloudOpsForKubernetesRepoURL, 'The repo URL of the cloudops-for-kubernetes code.')
stringParam('cloudOpsForKubernetesBranch', cloudOpsForKubernetesDefaultBranch, 'The branch of cloudops-for-kubernetes to use.')
}
}
}
Sample Jenkinsfile
This is an example of an Extensions repository Jenkins job.
// this bit is just plain Jenkins Groovy
def randomLabel = "jenkins-worker-${UUID.randomUUID().toString()}"
// this is a mounted volume that contains all of the data stored in the `jenkins-secrets` kubernetes secret
// for more information on what is stored in the `jenkins-secrets` secret, view this code: https://code.elasticpath.com/ep-cloudops/cloud-ops-kubernetes/-/blob/master/bootstrap/terraform/jenkins.tf
def secretsDirectory = new File("/secret/jenkins-secrets").exists() ? new File("/secret/jenkins-secrets") : new File("/var/jenkins_secrets");
// loading values from the `jenkins-secrets` kubernetes secret that contains the docker registry address and the agent image tags that should already exist
// if the jenkinsAgentImageTag does not exist in the docker registry, you will need to run the `build-jenkins-agents` job so the images are built
def dockerRegistryAddress = new File(secretsDirectory, "dockerRegistryAddress").text.trim();
def jenkinsAgentImageTag = new File(secretsDirectory, "cloudOpsForKubernetesDefaultBranch").text.trim().replace('/','-');
// pipeline specific variables
// make sure to change this if your job is not named `hello-world`
def jobName = "hello-world";
// this is the default git credential id that is created to access CloudOps for kubernetes
// this should not be changed
def cloudOpsForKubernetesCredentialId = "gitCredentialId";
// this is a workaround to allow to pull CloudOps for Kubernetes resources before declaring the proper pipeline agent
node {
// clone the CloudOps for Kuberentes repository
git branch: params.cloudOpsForKubernetesBranch, url: params.cloudOpsForKubernetesRepoURL, credentialsId: cloudOpsForKubernetesCredentialId
// in the CloudOps for Kubernetes repository, we store YAML files that define the agent pods that the Jenkins jobs run on
// CloudOps for Kubernetes jobs run on two types of containers that are build as part of this job: https://gitlab.elasticpath.com/commerce/cloud-ops-kubernetes/-/blob/main/jenkins/jobs/build-jenkins-agents/Jenkinsfile
// depending on the operations you want to complete in your job steps, you may want to use the docker1 container, or you may want to use the maven container
// the available pod configurations can be seen here: https://code.elasticpath.com/ep-cloudops/cloud-ops-kubernetes/-/tree/master/jenkins/agents/kubernetes
podYamlFromFile = sh(
returnStdout: true,
script: "cat /home/jenkins/workspace/${jobName}/jenkins/agents/kubernetes/docker-0.5gb-0.25core-1container.yaml"
).trim()
podYaml = podYamlFromFile.replace('${dockerRegistryAddress}', "${dockerRegistryAddress}").replace('${jenkinsAgentImageTag}', "${jenkinsAgentImageTag}")
// if you are planning on using CloudOps for Kubernetes groovy functions for logging into the: AWS account for cli commands, Kubernetes cluster, or the Terraform backend
// you may want to import the eplib library with this example:
// eplib = load "/home/jenkins/workspace/${jobName}/lib/eplib.groovy"
// note: you will still need to use the functions defined in the library in the pipeline agent steps
// for an example, consult this job that logs into the AWS account, the kubernetes cluster, and the terraform backend
// here: https://code.elasticpath.com/ep-cloudops/cloud-ops-kubernetes/-/blob/master/jenkins/jobs/create-or-delete-activemq-container/Jenkinsfile
// there are many other groovy libraries that CloudOps for Kubernetes provides that you can use in your Extensions job definitions
// they can be viewed here: https://code.elasticpath.com/ep-cloudops/cloud-ops-kubernetes/-/tree/master/lib
// examples on how the functions in the libraries are used are found throughout the CloudOps for Kubernetes Jenkinsfiles
}
// this is the Pipeline DSL.
// Syntax here: https://jenkins.io/doc/book/pipeline/syntax/
// More useful docs: https://jenkins.io/doc/pipeline/steps/
pipeline {
agent {
kubernetes {
label "${randomLabel}"
defaultContainer "jnlp"
yaml "${podYaml}"
}
}
options {
// buildDiscarder docs: https://plugins.jenkins.io/build-discarder/
buildDiscarder(logRotator(numToKeepStr: '90', artifactNumToKeepStr: '10000'))
timeout(time: 40, unit: 'MINUTES')
}
stages {
stage('Preparation Stage') {
steps {
container('docker1') {
script{
// Set the Jenkins build description
description_message = "Hello World"
currentBuild.description = description_message
}
}
}
}
stage('Hello World') {
steps {
container('docker1') {
script {
labelledShell label: 'Run Hello World', script: """#!/bin/bash
echo "Hello World!"
"""
}
}
}
}
}
}