You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
296 lines
9.0 KiB
296 lines
9.0 KiB
#!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;
|
|
|