Browse Source

Work in progress

master
Nicolas Massé 7 years ago
parent
commit
99859399d7
  1. 296
      3scale_toolbox.groovy
  2. 13
      README.md
  3. 32
      testcase-01/Jenkinsfile
  4. 34
      testcase-01/setup.yaml
  5. 0
      testcase-01/swagger.json

296
3scale_toolbox.groovy

@ -0,0 +1,296 @@
#!groovy
def importOpenAPI(Map conf) {
assert conf.destination != null
assert conf.baseSystemName != null
assert conf.oasFile != null
// Read the OpenAPI Specification file
def openAPI = readOpenAPISpecificationFile(conf.oasFile)
assert openAPI.swagger == "2.0"
def version = openAPI.info.version
assert version != null
def major = version.tokenize(".")[0]
def baseName = basename(conf.oasFile)
// Compute the target system_name
def targetSystemName = (conf.environmentName != null ? "${conf.environmentName}_" : "") + conf.baseSystemName + "_${major}"
def commandLine = "3scale import openapi -t ${targetSystemName} -d ${conf.destination} /artifacts/${baseName}"
def result = runToolbox(commandLine: commandLine,
jobName: "import",
openAPI: [
"filename": baseName,
"content": readFile(conf.oasFile)
],
toolboxConfig: conf.toolboxConfig)
echo result.stdout
}
def basename(path) {
return path.drop(path.lastIndexOf("/") != -1 ? path.lastIndexOf("/") : 0)
}
def readOpenAPISpecificationFile(fileName) {
if (fileName.toLowerCase().endsWith(".json")) {
return readJSON(file: fileName)
} else if (fileName.toLowerCase().endsWith(".yaml") || fileName.toLowerCase().endsWith(".yml")) {
return readYaml(file: fileName)
} else {
throw new Exception("Can't decide between JSON and YAML on ${fileName}")
}
}
def getToolboxVersion() {
def result = runToolbox(commandLine: "3scale -v",
jobName: "version")
return result.stdout
}
def generateRandomBaseSystemName() {
String alphabet = (('A'..'N')+('P'..'Z')+('a'..'k')+('m'..'z')+('2'..'9')).join()
def length = 6
id = new Random().with {
(1..length).collect{ alphabet[nextInt(alphabet.length())] }.join()
}
return "testcase_${id}"
}
def runToolbox(Map conf) {
def result = null
assert conf.jobName != null
assert conf.commandLine != null
def defaultToolboxConf = [
"toolboxConfig": null,
"openAPI": null,
"image": "quay.io/redhat/3scale-toolbox:v0.10.0",
"backoffLimit": 2, // three attempts (one first try + two retries)
"imagePullPolicy": "IfNotPresent",
"activeDeadlineSeconds": 90
]
// Apply default values
conf = defaultToolboxConf + conf
if (conf.toolboxConfig != null && conf.toolboxConfig.configFileId != null) {
// Generate a default secret name if none has been provided
if (conf.toolboxConfig.secretName == null) {
conf.toolboxConfig = [
"configFileId": conf.toolboxConfig.configFileId,
"secretName": "3scale-toolbox-${JOB_BASE_NAME}"
]
}
echo "Creating a secret named ${conf.toolboxConfig.secretName} containing file ${conf.toolboxConfig.configFileId}..."
configFileProvider([configFile(fileId: conf.toolboxConfig.configFileId, variable: 'TOOLBOX_CONFIG')]) {
def toolboxConfig = readFile(TOOLBOX_CONFIG)
createSecret(conf.toolboxConfig.secretName, [ ".3scalerc.yaml": toolboxConfig ])
}
}
def oasConfigMapName = null
if (conf.openAPI != null) {
oasConfigMapName = "3scale-toolbox-${JOB_BASE_NAME}-${BUILD_NUMBER}-openapi"
echo "Creating a configMap named ${oasConfigMapName} containing the OpenAPI file..."
createConfigMap(oasConfigMapName, [ (conf.openAPI.filename): conf.openAPI.content ])
}
def jobName = "${JOB_BASE_NAME}-${BUILD_NUMBER}-${conf.jobName}"
def jobSpecs = [
"apiVersion": "batch/v1",
"kind": "Job",
"metadata": [
"name": jobName,
"labels": [
"build": "${JOB_BASE_NAME}-${BUILD_NUMBER}",
"job": "${JOB_BASE_NAME}"
]
],
"spec": [
"backoffLimit": conf.backoffLimit,
"activeDeadlineSeconds": conf.activeDeadlineSeconds,
"template": [
"spec": [
"restartPolicy": "Never",
"containers": [
[
"name": "job",
"image": conf.image,
"imagePullPolicy": conf.imagePullPolicy,
"command": [ "scl", "enable", "rh-ruby25", "/opt/rh/rh-ruby25/root/usr/local/bin/${conf.commandLine}" ],
"volumeMounts": [
[
"mountPath": "/opt/app-root/src/",
"name": "toolbox-config"
],
[
"mountPath": "/artifacts",
"name": "artifacts"
]
]
]
],
"volumes": [
[
"name": "toolbox-config"
],
[
"name": "artifacts"
]
]
]
]
]
]
// Inject the toolbox configuration as a volume
if (conf.toolboxConfig != null && conf.toolboxConfig.secretName != null) {
jobSpecs.spec.template.spec.volumes[0].secret = [
"secretName": conf.toolboxConfig.secretName
]
} else {
jobSpecs.spec.template.spec.volumes[0].emptyDir = [:]
}
// Inject the OpenAPI file as a volume
if (oasConfigMapName != null) {
jobSpecs.spec.template.spec.volumes[1].configMap = [
"name": oasConfigMapName
]
} else {
jobSpecs.spec.template.spec.volumes[1].emptyDir = [:]
}
def job = null
try {
job = openshift.create(jobSpecs)
int jobTimeout = 2 + (int)(conf.activeDeadlineSeconds / 60.0f)
echo "Waiting ${jobTimeout} minutes for the job to complete..."
timeout(jobTimeout) {
// Wait for the job to complete, either Succeeded or Failed
job.watch {
def jobStatus = getJobStatus(it.object())
echo "Job ${it.name()}: succeeded = ${jobStatus.succeeded}, failed = ${jobStatus.failed}, status = ${jobStatus.status}, reason = ${jobStatus.reason}"
// Exit the watch loop when the Job has one successful pod or failed
return jobStatus.succeeded > 0 || jobStatus.status == "Failed"
}
}
} finally {
if (job != null) {
def jobStatus = getJobStatus(job.object())
echo "job ${job.name()} has status '${jobStatus.status}' and reason '${jobStatus.reason}'"
// Iterate over pods to find:
// - the pod that succeeded
// - as last resort, a pod that failed
def pods = job.related("pod")
pods.withEach {
if (it.object().status.phase == "Succeeded") {
result = getPodDetails(it)
}
if (it.object().status.phase == "Failed" && result == null) {
result = getPodDetails(it)
}
}
if (result != null && result.podPhase == "Failed") {
echo "RC: ${result.status}"
echo "STDOUT:"
echo "-------"
echo result.stdout
echo "STDERR:"
echo "-------"
echo result.stderr
error("job ${job.name()} exited with '${jobStatus.status}' and reason '${jobStatus.reason}'")
}
// Delete the job
try {
openshift.selector('job', jobName).delete()
} catch (e2) { // Best effort
echo "cannot delete the job ${jobName}: ${e2}"
}
}
// Delete the temporary configMap containing the OAS file
if (oasConfigMapName != null) {
try {
openshift.selector('configMap', oasConfigMapName).delete()
} catch (e2) { // Best effort
echo "cannot delete the configMap ${oasConfigMapName}: ${e2}"
}
}
// Delete the temporary secret
if (conf.toolboxConfig != null && conf.toolboxConfig.configFileId != null) {
try {
openshift.selector('secret', conf.toolboxConfig.secretName).delete()
} catch (e2) { // Best effort
echo "cannot delete the secret ${conf.toolboxConfig.secretName}: ${e2}"
}
}
}
return result
}
def getJobStatus(obj) {
return [
"succeeded": obj.status.succeeded != null ? obj.status.succeeded : 0,
"failed": obj.status.failed != null ? obj.status.failed : 0,
"status": obj.status.conditions != null && obj.status.conditions.size() > 0 ? obj.status.conditions[0].type : "Unknown",
"reason": obj.status.conditions != null && obj.status.conditions.size() > 0 ? obj.status.conditions[0].reason : ""
]
}
def getPodDetails(pod) {
def logs = pod.logs()
return [
"status": logs.actions[0].status,
"stdout": logs.actions[0].out,
"stderr": logs.actions[0].err,
"podPhase": pod.object().status.phase,
"podName": pod.name()
]
}
def createConfigMap(configMapName, content) {
def configMapSpecs = [
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": [
"name": "${configMapName}",
"labels": [
"job": "${JOB_BASE_NAME}",
"build": "${JOB_BASE_NAME}-${BUILD_NUMBER}"
]
],
"data": [:]
]
content.each{ k, v -> configMapSpecs.data[k] = v }
openshift.apply(configMapSpecs)
}
def createSecret(secretName, content) {
def secretSpecs = [
"apiVersion": "v1",
"kind": "Secret",
"metadata": [
"name": "${secretName}",
"labels": [
"job": "${JOB_BASE_NAME}"
]
],
"stringData": [:]
]
content.each{ k, v -> secretSpecs.stringData[k] = v }
openshift.apply(secretSpecs)
}
// required to be loaded from a jenkins pipeline
return this;

13
README.md

@ -0,0 +1,13 @@
# API Lifecycle Mockup
## Setup
```sh
oc project api-lifecycle
3scale remote add $NAME https://$TOKEN@$TENANT.3scale.net/
oc create secret generic 3scale-toolbox --from-file=~/.3scalerc.yaml
```
```sh
oc process -f testcase-01/setup.yaml |oc create -f -
```

32
testcase-01/Jenkinsfile

@ -0,0 +1,32 @@
#!groovy
def toolbox = load '../3scale_toolbox.groovy'
def toolboxConfig = [
"secretName": params.SECRET_NAME
]
def baseSystemName = toolbox.generateRandomBaseSystemName()
node() {
stage('Checkout Source') {
checkout scm
}
stage("Get toolbox version") {
openshift.withCluster() {
openshift.withProject(params.NAMESPACE) {
echo "toolbox version = " + toolbox.getToolboxVersion()
}
}
}
stage("Import OpenAPI") {
openshift.withCluster() {
openshift.withProject(params.NAMESPACE) {
toolbox.importOpenAPI(destination: params.TARGET_INSTANCE,
toolboxConfig: toolboxConfig,
oasFile: "swagger.json",
baseSystemName: baseSystemName)
}
}
}
}

34
testcase-01/setup.yaml

@ -0,0 +1,34 @@
apiVersion: v1
kind: Template
metadata:
name: testcase-01
objects:
- kind: "BuildConfig"
apiVersion: "v1"
metadata:
name: "testcase-01"
namespace: ${NAMESPACE}
spec:
source:
git:
uri: ${GIT_REPO}
strategy:
type: "JenkinsPipeline"
jenkinsPipelineStrategy:
jenkinsfilePath: testcase-01/Jenkinsfile
env:
- name: SECRET_NAME
value: ${SECRET_NAME}
- name: NAMESPACE
value: ${NAMESPACE}
- name: TARGET_INSTANCE
value: ${TARGET_INSTANCE}
parameters:
- name: SECRET_NAME
value: 3scale-toolbox
- name: NAMESPACE
value: api-lifecycle
- name: TARGET_INSTANCE
value: nmasse-redhat
- name: GIT_REPO
value: https://github.com/nmasse-itix/API-Lifecycle-Mockup.git

0
swagger.json → testcase-01/swagger.json

Loading…
Cancel
Save