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

Added create deployment version.... #13

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions License.md
@@ -1,7 +1,7 @@
Copyright 2019 XEBIALABS
Copyright ${year} ${name}

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
84 changes: 76 additions & 8 deletions README.md
Expand Up @@ -16,13 +16,18 @@ This document describes the functionality provided by the XL Release Bamboo plug

See the [XL Release reference manual](https://docs.xebialabs.com/xl-release) for background information on XL Release and release automation concepts.

This is a 'See It Work' plugin project, meaning the code base includes functionality that makes it easy to spin up and configure a dockerized version of the XebiaLabs platform with this plugin already installed. Using the provided test data, you can then try out the plugin features. This is useful for familiarizing yourself with the plugin functionality, for demonstrations, testing and for further plugin development. [See the Demo/Dev section.](#to-run-demo-or-dev-version-set-up-docker-containers-for-both-xlr-and-the-mock-server)

The plugin code base also includes a gradle task for [automatic integration testing](#to-run-integration-tests).

## Overview

This plugin allows XL Release to run a Bamboo plan or trigger a Bamboo deployment.
This plugin allows XL Release to run a Bamboo plan, create a Bamboo Release or trigger a Bamboo deployment.

## Requirements

* XL Release 5.0+
* XL Release 9.0+
* This has been tested with XL Release 9.7 and Bamboo version 6.10.3

## Installation

Expand All @@ -31,24 +36,87 @@ This plugin allows XL Release to run a Bamboo plan or trigger a Bamboo deploymen

## Usage

### RunPlan
### Run Plan

The RunPlan.py script accepts a Bamboo project-plan-key (for example, PROJ-PLAN). It calls Bamboo's API to run the next build job(s) for that plan and the build number is returned. Polling of the job status occurs at 5-second intervals. The script output will indicate the build status as success or failure.
The Run Plan task accepts a Bamboo project-plan-key (for example, MYN-MYN). It calls Bamboo's API to run the next build job(s) for that plan. Polling of the job status occurs at 5-second intervals. The script output will indicate the build status as success or failure. The Plan Result Key, Build Number, Build State and State, which are return by the Bamboo API call can be saved into Release Variables, if you choose.

![run-plan screenshot](images/run-plan.png)

### TriggerDeployment
### Create Release

The Create Release task take a Bamboo Deployment Project Name, a Project Build Result (which is the Plan Result Key returned after a Plan is run, such as 'MYN-MYN-9'), and a Version Name. It calls Bamboo's API to create a release for that project.

The TriggerDeployment script accepts a project name, environment name, and version name. It calls Bamboo's API to look up to respective ids of these items and then triggers a deployment.
![run-plan screenshot](images/createRelease.png)

![trigger-deployment screenshot](images/trigger-deployment.png)
### Trigger Deployment

The TriggerDeployment script accepts a project name, environment name, and version name. It calls Bamboo's API to look up the respective ids of these items and then triggers a deployment.

![trigger-deployment screenshot](images/triggerDeployment.png)

### Configuration ###

![server-configuration screenshot](images/server-configuration.png)
![server-configuration screenshot](images/bambooServerConfig.png)

## Developers

Build and package the plugins with...

```bash
./gradlew assemble
```

### To run integration tests

1. Clone this git project to your local dev environment
2. You will need to have Docker and Docker Compose installed.
3. The XL-Release docker image uses the community trial license
4. Open a terminal in the root of the xlr-bamboo-plugin project and run the following gradle task

```bash
./gradlew clean integrationTest
```

The test will set up a temporary xlr/mockserver testbed using docker. The mockserver acts as a mock Bamboo server and returns canned responses to Bamboo REST API calls. After testing is complete, the test docker containers are stopped and removed.

### To run demo or dev version (set up docker containers for both XLR and the mock server)

NOTE:

1. For requirements, see the 'To run integration tests' above.
2. XL Release will run on the [localhost port 15516](http://localhost:15516/).
3. The XL Release username / password is admin / admin.
4. The Mockserver runs on the [localhost port 5099](http://localhost:5099/).
5. The Mockserver username / password is admin / admin
6. Within XLR, you will need to set up the psuedo Bamboo server and import the provide template. When you run this XLR release, the Mockserver will act as a pseudo Bamboo server and return canned responses to Bamboo REST API calls.

* Before running the demo, be sure to create the plugin by opening a terminal, cd into the plugin source code directory, and run

```bash
./gradlew clean build
```

* To run the dev/demo mode, open a terminal, cd into the src/test/resources/docker directory of the plugin code and run

```bash
docker-compose up
```

* After XLR starts up, log in using the admin / admin credentials and set up the mock Bamboo server, as described below, and then use the XLR 'Import Template' feature to import the template found in the src/test/resources/docker/initialize/data directory. You can then create a release and run the test example.

Set up the mock Bamboo server exactly as shown with a server name of 'bamboo server' and the username/passwor admin/admin.
![mockBambooServer](images/mockBambooServer.png)

* To shut down and remove the docker containers - in a terminal, cd to the src/test/resources/docker directory, and run

```bash
docker-compose down
```

## References

<https://www.atlassian.com/software/bamboo>

<https://docs.atlassian.com/atlassian-bamboo/REST/6.10.3/?_ga=2.178472611.688514311.1571778462-1591657357.1542136994/>


72 changes: 48 additions & 24 deletions build.gradle
@@ -1,51 +1,75 @@
buildscript {
repositories {
mavenLocal()
mavenCentral()
maven {
url 'https://dist.xebialabs.com/public/maven2'
}
}

}

plugins {
id "com.github.hierynomus.license" version "0.14.0"
id 'nebula.release' version '6.0.0'
id "com.xebialabs.xl.docker" version "1.1.0"
id "com.github.hierynomus.jython" version "0.8.0"
}

apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'java'
apply plugin: 'maven'

if (!project.hasProperty('release.scope')) {
project.ext['release.scope'] = 'patch'
}

if (!project.hasProperty('release.useLastTag')) {
project.ext['release.useLastTag'] = true
}

repositories {
mavenLocal()
mavenCentral()
maven {
url 'https://dist.xebialabs.com/public/maven2'
}
}


dependencies {
// Place rest-assured before JUnit dependency to make sure correct Hamcrest is used
testCompile 'io.rest-assured:rest-assured:3.2.0'
testCompile "junit:junit:4.11"
testCompile "com.googlecode.json-simple:json-simple:1.1.1"
testCompile "org.assertj:assertj-core:3.6.2"
testCompile "org.testcontainers:testcontainers:1.12.0"
testCompile "org.skyscreamer:jsonassert:1.5.0"

jython python(":httplib2:0.11.3") {
copy {
from "python2/httplib2"
}
}
}

xlDocker {
compileImage = 'xebialabsunsupported/xlr_dev_compile'
compileVersion = '8.5.1'
runImage = 'xebialabsunsupported/xlr_dev_run'
runVersion = '8.5.1'
runPortMapping = '5516:5516'
test {
// Auto detected Unit tests only, exclude the end to end integration test
exclude '**/*IntegrationTest.class'
// show standard out and standard error of the test JVM(s) on the console
testLogging.showStandardStreams = true
}


if (!project.hasProperty('release.scope')) {
project.ext['release.scope'] = 'patch'
}
task integrationTest(type: Test, dependsOn: ['build']) {
// do not automatically scan for tests
scanForTestClasses = false
// explicitly include the integration test
include '**/*IntegrationTest.class'
// To run tests -
// 1. Docker and Docker Compose must be installed
// 2. To run the test - ./gradlew clean integrationTest
// The test will set up a temporary xlr/mockserver testbed using docker. The mockserver
// will serve up Bamboo Rest Api Responses
// After testing is complete, the test docker containers are stopped and removed.

if (!project.hasProperty('release.useLastTag')) {
project.ext['release.useLastTag'] = true
// show standard out and standard error of the test JVM(s) on the console
testLogging.showStandardStreams = true
}


license {
header rootProject.file('LICENSE.md')
header rootProject.file('License.md')
strictCheck false
excludes(["**/*.json", "**/httplib2/**/*.*"])
ext.year = Calendar.getInstance().get(Calendar.YEAR)
Expand Down
Binary file added deploy_key.enc
Binary file not shown.
Binary file added images/bambooServerConfig.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/createRelease.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/mockBambooServer.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/run-plan.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/triggerDeployment.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/main/resources/bamboo/CreateRelease.py
@@ -1,5 +1,5 @@
#
# Copyright 2019 XEBIALABS
# Copyright 2020 XEBIALABS
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
Expand Down
8 changes: 4 additions & 4 deletions src/main/resources/bamboo/RunPlan.py
@@ -1,5 +1,5 @@
#
# Copyright 2019 XEBIALABS
# Copyright 2020 XEBIALABS
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
Expand Down Expand Up @@ -28,10 +28,10 @@ def successful(brkey):
response = request.get('/rest/api/latest/result/' + brkey, contentType=contentType, headers=headers)
return json.loads(response.response)['successful']

def getStatesAndTimes(brkey):
def getKeyStatesAndTimes(brkey):
response = request.get('/rest/api/latest/result/' + brkey, contentType=contentType, headers=headers)
jsonData = json.loads(response.response)
return (jsonData['buildState'], jsonData['state'], jsonData['prettyBuildStartedTime'], jsonData['prettyBuildCompletedTime'])
return (jsonData['planResultKey']['key'], jsonData['buildState'], jsonData['state'], jsonData['prettyBuildStartedTime'], jsonData['prettyBuildCompletedTime'])

credentials = CredentialsFallback(bambooServer, username, password).getCredentials()
request = HttpRequest(bambooServer, credentials['username'], credentials['password'])
Expand All @@ -44,7 +44,7 @@ def getStatesAndTimes(brkey):
while (not finished(brkey)):
time.sleep(5)

(buildState, state, prettyBuildStartedTime, prettyBuildCompletedTime) = getStatesAndTimes(brkey)
(planResultKey, buildState, state, prettyBuildStartedTime, prettyBuildCompletedTime) = getKeyStatesAndTimes(brkey)

print "Build job started at " + prettyBuildStartedTime + "\n"

Expand Down
66 changes: 62 additions & 4 deletions src/main/resources/bamboo/TriggerDeployment.py 100644 → 100755
@@ -1,5 +1,5 @@
#
#Copyright 2019 XEBIALABS
#Copyright 2020 XEBIALABS
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
Expand Down Expand Up @@ -38,7 +38,7 @@ def getEnvironmentId(projectId, environmentName):
if item['name'] == environmentName:
print "Environment ID for %s is %s\n" % (environmentName, item['id'])
return item['id']
print "Error: environment not found for %s, %s\n" % (projectName, environmentName)
print "Error: environment not found for %s, %s\n" % (projectId, environmentName)
sys.exit(1)

def getVersionId(projectId, versionName):
Expand All @@ -48,20 +48,78 @@ def getVersionId(projectId, versionName):
if item['name'] == versionName:
print "Version ID for %s is %s\n" % (versionName, item['id'])
return item['id']
#print "Error: version not found for %s, %s\n" % (projectId, versionName)
print "Error: version not found for %s, %s, %s\n" % (projectName, environmentName, versionName)
sys.exit(1)

def getPlanKey(projectName):
print "Executing getPlanKey() with projectName %s\n" % projectName
response = request.get('rest/api/latest/deploy/project/all', contentType=contentType, headers=headers)
#print "response: %s " % response
for item in json.loads(response.response):
if item['name'] == projectName:
print "planKey for %s is %s\n" % (projectName, item['planKey'])
print "planKey.key for %s is %s\n" % (projectName, item['planKey']['key'])
return item['planKey']['key']
print "Error: project not found for %s\n" % projectName
sys.exit(1)

def getPlanKeyResult(planKey, versionName):
#e.g. SER-AR-139, planKey = SER-AR, versionName = 3.1.0-139
print "in getPlanKeyResult- planKey: %s, versionName: %s" % (planKey,versionName)
planKeyRslt = planKey + versionName[versionName.index("-"):]
print "planKey: %s, versionName: %s, planKeyResult: %s\n" % (planKey, versionName, planKeyRslt)
return planKeyRslt

def createDeploymentVersion(projectId, planKeyResult, versionName):
print "Executing createDeploymentVersion() with projectId %s and versionName %s\n" % (projectId, versionName)
response = request.post('rest/api/latest/deploy/project/%s/version' % (projectId), '{"planResultKey" : "%s", "name" : "%s"}' % (planKeyResult, versionName), contentType=contentType, headers=headers)
result = json.loads(response.response)
print (result['id'])
return result['id']

def getDeploymentVersion(projectId, planKeyResult, versionName):
print "Executing getDeploymentVersion() with projectId %s and versionName %s\n" % (projectId, versionName)
response = request.get('rest/api/latest/deploy/project/%s/versions' % (projectId), contentType=contentType, headers=headers)
for item in json.loads(response.response)['versions']:
if item['name'] == versionName:
print "versionId for %s is %s\n" % (versionName, item['id'])
return item['id']
return None

def triggerDeployment(environmentId, versionId):
print "Executing triggerDeployment() with environmentId %s and versionId %s\n" % (environmentId, versionId)
response = request.post('rest/api/latest/queue/deployment/?environmentId=%s&versionId=%s' % (environmentId, versionId), '{}', contentType=contentType, headers=headers)
result = json.loads(response.response)
print (result['deploymentResultId'], result['link']['href'])
return (result['deploymentResultId'], result['link']['href'])
return result['deploymentResultId'], result['link']['href']

credentials = CredentialsFallback(bambooServer, username, password).getCredentials()
print "1) credentials: no value"

request = HttpRequest(bambooServer, credentials['username'], credentials['password'])
print "2) request: %s" % request

projectId = getProjectId(projectName)
print "3) projectId: %s" % projectId

environmentId = getEnvironmentId(projectId, environmentName)
versionId = getVersionId(projectId, versionName)
print "4) environmentId: %s" % environmentId

#versionId = getVersionId(projectId, versionName)

print "4a) before getPlanKey with projectName: %s" % projectName
planKey = getPlanKey(projectName)
print "5) planKey: %s" % planKey

planKeyResult = getPlanKeyResult(planKey, versionName)
print "6) planKeyResult: %s" % planKeyResult

versionId = getDeploymentVersion(projectId, planKeyResult, versionName)
print "7) versionId: %s" % versionId

if versionId is None:
versionId = createDeploymentVersion(projectId, planKeyResult, versionName)
print "8) versionId: %s" % versionId

(deploymentResultId, href) = triggerDeployment(environmentId, versionId)
6 changes: 3 additions & 3 deletions src/main/resources/synthetic.xml
@@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<!--

Copyright 2019 XEBIALABS
Copyright 2020 XEBIALABS

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

Expand All @@ -24,11 +24,11 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
<property name="bambooServer" category="input" label="Server" referenced-type="bamboo.Server" kind="ci" />
<property name="username" category="input" required="false"/>
<property name="password" category="input" required="false" password="true"/>

</type>

<type type="bamboo.RunPlan" extends="bamboo.Task">
<property name="projPlanKey" category="input" />
<property name="planResultKey" category="output" />
<property name="buildNumber" category="output" />
<property name="buildState" category="output" />
<property name="state" category="output" />
Expand All @@ -39,7 +39,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
<property name="environmentName" category="input" />
<property name="versionName" category="input" />
<property name="deploymentResultId" kind="integer" category="output" />
<property name="href" category="output" />
<property name="href" label="HREF" category="output" />
</type>

<type type="bamboo.CreateRelease" extends="bamboo.Task">
Expand Down