Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adjust petclinic plain to use gitops-build-lib #37

Merged
merged 2 commits into from
Apr 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions applications/nginx/fluxv1/Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ node('docker') {
cesBuildLibCredentialsId: scmManagerCredentials,
application: application,
mainBranch: mainBranch,
gitopsTool: 'FLUX',
deployments: [
sourcePath: 'k8s',
helm : [
Expand Down
203 changes: 36 additions & 167 deletions applications/petclinic/argocd/plain-k8s/Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,26 @@
String getApplication() {"spring-petclinic-plain" }
String getScmManagerCredentials() { 'scmm-user' }
String getConfigRepositoryUrl() { "${env.SCMM_URL}/repo/argocd/gitops" }
String getConfigRepositoryPRUrl() { "${env.SCMM_URL}/api/v2/pull-requests/argocd/gitops" }
String getConfigRepositoryPRBaseUrl() { "${env.SCMM_URL}" }
String getConfigRepositoryPRRepo() { "argocd/gitops" }
// The docker daemon cant use the k8s service name, because it is not running inside the cluster
String getDockerRegistryBaseUrl() { "${env.REGISTRY_URL}" }
String getDockerRegistryPath() { "${env.REGISTRY_PATH}" }
String getDockerRegistryCredentials() { 'registry-user' }
String getCesBuildLibVersion() { '1.44.3' }
String getCesBuildLibVersion() { '1.46.1' }
String getCesBuildLibRepo() { "${env.SCMM_URL}/repo/common/ces-build-lib/" }
String getGitOpsBuildLibRepo() { "${env.SCMM_URL}/repo/common/gitops-build-lib" }
String getGitOpsBuildLibVersion() { '0.0.11' }
String getMainBranch() { 'main' }

cesBuildLib = library(identifier: "ces-build-lib@${cesBuildLibVersion}",
retriever: modernSCM([$class: 'GitSCMSource', remote: cesBuildLibRepo, credentialsId: scmManagerCredentials])
).com.cloudogu.ces.cesbuildlib

gitOpsBuildLib = library(identifier: "gitops-build-lib@${gitOpsBuildLibVersion}",
retriever: modernSCM([$class: 'GitSCMSource', remote: gitOpsBuildLibRepo, credentialsId: scmManagerCredentials])
).com.cloudogu.gitops.gitopsbuildlib

properties([
// Don't run concurrent builds, because the ITs use the same port causing random failures on concurrent builds.
disableConcurrentBuilds()
Expand All @@ -34,7 +41,6 @@ node {

stage('Build') {
mvn 'clean package -DskipTests'

archiveArtifacts artifacts: '**/target/*.jar'
}

Expand Down Expand Up @@ -66,16 +72,35 @@ node {
def gitopsConfig = [
scmmCredentialsId: scmManagerCredentials,
scmmConfigRepoUrl: configRepositoryUrl,
scmmPullRequestUrl: configRepositoryPRUrl,
updateImages: [
[ deploymentFilename: "deployment.yaml",
containerName: "spring-petclinic-plain",
imageName: imageName ]
scmmPullRequestBaseUrl: configRepositoryPRBaseUrl,
scmmPullRequestRepo: configRepositoryPRRepo,
cesBuildLibRepo: cesBuildLibRepo,
cesBuildLibVersion: cesBuildLibVersion,
cesBuildLibCredentialsId: scmManagerCredentials,
application: application,
mainBranch: mainBranch,
gitopsTool: 'ARGO_CD',
deployments: [
sourcePath: 'k8s',
plain: [
updateImages: [
[ filename: "deployment.yaml",
containerName: application,
imageName: imageName ]
]
]
],
stages: [
staging: [
namespace: 'argocd-staging',
deployDirectly: true ],
production: [
namespace: 'argocd-production',
deployDirectly: false ],
]
]

String pushedChanges = pushToConfigRepo(gitopsConfig)
currentBuild.description = createBuildDescription(pushedChanges, imageName)
deployViaGitops(gitopsConfig)
} else {
echo 'Skipping deploy, because build not successful or not on main branch'
}
Expand All @@ -86,91 +111,6 @@ node {
junit allowEmptyResults: true, testResults: '**/target/failsafe-reports/TEST-*.xml,**/target/surefire-reports/TEST-*.xml'
}

String pushToConfigRepo(Map gitopsConfig) {

def git = cesBuildLib.Git.new(this, scmManagerCredentials)
def changesOnGitOpsRepo = ''

// Query and store info about application repo before cloning into gitops repo
def applicationRepo = GitRepo.create(git)

// Display that Jenkins made the GitOps commits not the application repo author
git.committerName = 'Jenkins'
git.committerEmail = 'jenkins@cloudogu.com'

def configRepoTempDir = '.configRepoTempDir'

try {

dir(configRepoTempDir) {

git url: gitopsConfig.scmmConfigRepoUrl, branch: mainBranch, changelog: false, poll: false
git.fetch()

def repoChanges = new HashSet<String>()
repoChanges += createApplicationForStageAndPushToBranch 'staging', mainBranch, applicationRepo, git, gitopsConfig

git.checkoutOrCreate(application)
repoChanges += createApplicationForStageAndPushToBranch 'production', application, applicationRepo, git, gitopsConfig

changesOnGitOpsRepo = aggregateChangesOnGitOpsRepo(repoChanges)

if (changesOnGitOpsRepo) {
createPullRequest(gitopsConfig)
}
}
} finally {
sh "rm -rf ${configRepoTempDir}"
}

return changesOnGitOpsRepo
}

private String aggregateChangesOnGitOpsRepo(changes) {
// Remove empty
(changes - '')
// and concat into string
.join('; ')
}

String createApplicationForStageAndPushToBranch(String stage, String branch, GitRepo applicationRepo, def git, Map gitopsConfig) {

String commitPrefix = stage == 'staging' ? '[S] ' : ''

sh "mkdir -p ${stage}/${application}"
// copy extra resources like sealed secrets
echo "Copying k8s payload from application repo to gitOps Repo: 'k8s/${stage}/*' to '${stage}/${application}'"
sh "cp ${env.WORKSPACE}/k8s/${stage}/* ${stage}/${application}/ || true"

gitopsConfig.updateImages.each {
updateImageVersion("${stage}/${application}/${it['deploymentFilename']}", it['containerName'], it['imageName'])
}

git.add('.')
if (git.areChangesStagedForCommit()) {
git.commit(commitPrefix + createApplicationCommitMessage(git, applicationRepo), applicationRepo.authorName, applicationRepo.authorEmail)

// If some else pushes between the pull above and this push, the build will fail.
// So we pull if push fails and try again
git.pushAndPullOnFailure("origin ${branch}")
return "${stage} (${git.commitHashShort})"
} else {
echo "No changes on gitOps repo for ${stage} (branch: ${branch}). Not committing or pushing."
return ''
}
}

String createApplicationCommitMessage(def git, def applicationRepo) {
String issueIds = (applicationRepo.commitMessage =~ /#\d*/).collect { "${it} " }.join('')

String[] urlSplit = applicationRepo.repositoryUrl.split('/')
def repoNamespace = urlSplit[-2]
def repoName = urlSplit[-1]
String message = "${issueIds}${repoNamespace}/${repoName}@${applicationRepo.commitHash}"

return message
}

String createImageTag() {
def git = cesBuildLib.Git.new(this)
String branch = git.simpleBranchName
Expand All @@ -183,76 +123,5 @@ String createImageTag() {
return "${new Date().format('yyyyMMddHHmm')}-${git.commitHashShort}${branchSuffix}"
}

void updateImageVersion(String deploymentFilePath, String containerName, String newImageTag) {
def data = readYaml file: deploymentFilePath
def containers = data.spec.template.spec.containers
def updateContainer = containers.find {it.name == containerName}
updateContainer.image = newImageTag
writeYaml file: deploymentFilePath, data: data, overwrite: true
}

void createPullRequest(Map gitopsConfig) {

withCredentials([usernamePassword(credentialsId: gitopsConfig.scmmCredentialsId, passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USER')]) {

String script =
'curl -s -o /dev/null -w "%{http_code}" ' +
"-u ${GIT_USER}:${GIT_PASSWORD} " +
'-H "Content-Type: application/vnd.scmm-pullRequest+json;v=2" ' +
'--data \'{"title": "created by service ' + application + '", "source": "' + application + '", "target": "' + mainBranch + '"}\' ' +
gitopsConfig.scmmPullRequestUrl

// For debugging the quotation of the shell script, just do: echo script
String http_code = sh returnStdout: true, script: script

// At this point we could write a mail to the last committer that his commit triggered a new or updated GitOps PR

echo "http_code: ${http_code}"
// PR exists if we get 409
if (http_code != "201" && http_code != "409") {
unstable 'Could not create pull request'
}
}
}

private String createBuildDescription(String pushedChanges, String imageName) {
String description = ''

description += "GitOps commits: "

if (pushedChanges) {
description += pushedChanges
} else {
description += 'No changes'
}

description += "\nImage: ${imageName}"

return description
}

/** Queries and stores info about current repo and HEAD commit */
class GitRepo {

static GitRepo create(git) {
// Constructors can't be used in Jenkins pipelines due to CPS
// https://www.jenkins.io/doc/book/pipeline/cps-method-mismatches/#constructors
return new GitRepo(git.commitAuthorName, git.commitAuthorEmail ,git.commitHashShort, git.commitMessage, git.repositoryUrl)
}

GitRepo(String authorName, String authorEmail, String commitHash, String commitMessage, String repositoryUrl) {
this.authorName = authorName
this.authorEmail = authorEmail
this.commitHash = commitHash
this.commitMessage = commitMessage
this.repositoryUrl = repositoryUrl
}

final String authorName
final String authorEmail
final String commitHash
final String commitMessage
final String repositoryUrl
}

def cesBuildLib
def gitOpsBuildLib
1 change: 1 addition & 0 deletions applications/petclinic/fluxv1/helm/Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ node {
cesBuildLibCredentialsId: scmManagerCredentials,
application: application,
mainBranch: mainBranch,
gitopsTool: 'FLUX',
deployments: [
sourcePath: 'k8s',
helm : [
Expand Down
1 change: 1 addition & 0 deletions applications/petclinic/fluxv1/plain-k8s/Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ node {
cesBuildLibCredentialsId: scmManagerCredentials,
application: application,
mainBranch: mainBranch,
gitopsTool: 'FLUX',
deployments: [
sourcePath: 'k8s',
plain: [
Expand Down