Skip to content

Commit

Permalink
Merge pull request #55 from ci-plugins/issue_6725
Browse files Browse the repository at this point in the history
feat: 提供统一的云原生调度接入层 #6725
  • Loading branch information
carlyin0801 authored Nov 11, 2022
2 parents d34119b + b5a3f04 commit 3353313
Show file tree
Hide file tree
Showing 11 changed files with 420 additions and 6 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 插件市场-插件开发 Java SDK(v1.1.4
# 插件市场-插件开发 Java SDK(v1.1.5


使用方式(How to use)
Expand Down Expand Up @@ -36,6 +36,7 @@
| v1.1.2 | 修复Java市场插件默认输出至错误流
| v1.1.3 | 获取插件私有配置优化
| v1.1.4 | 升级jackson开源组件漏洞版本
| v1.1.5 | 新增kubernetes构建资源相关api

[TOC]

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.tencent.devops.ci-plugins</groupId>
<artifactId>java-plugin-sdk</artifactId>
<version>1.1.4</version>
<version>1.1.5</version>

<inceptionYear>2018-2118</inceptionYear>
<description>bk-ci pipeline plugins sdk for java</description>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.tencent.bk.devops.plugin.api.impl

import com.fasterxml.jackson.core.type.TypeReference
import com.tencent.bk.devops.atom.api.BaseApi
import com.tencent.bk.devops.atom.utils.http.SdkUtils
import com.tencent.bk.devops.atom.utils.json.JsonUtil
import com.tencent.bk.devops.plugin.pojo.kubernetes.DispatchBuildImageReq
import com.tencent.bk.devops.plugin.pojo.kubernetes.DispatchBuildStatusResp
import com.tencent.bk.devops.plugin.pojo.kubernetes.DispatchJobLogResp
import com.tencent.bk.devops.plugin.docker.utils.EnvUtils
import com.tencent.bk.devops.plugin.pojo.kubernetes.DispatchJobReq
import com.tencent.bk.devops.plugin.pojo.kubernetes.DispatchTaskResp
import com.tencent.bk.devops.plugin.pojo.Result
import okhttp3.RequestBody
import org.apache.commons.io.FileUtils
import org.slf4j.LoggerFactory
import java.io.File
import java.io.IOException
import java.nio.charset.Charset

/**
* BCS接口类
*/
class KubernetesBuildApi : BaseApi() {

fun createJob(dispatchJobReq: DispatchJobReq): Result<DispatchTaskResp?> {
val path = "/dispatch-kubernetes/api/build/job/create"
dispatchJobReq.copy(podNameSelector = EnvUtils.getHostName())
val requestBody = RequestBody.create(JSON_CONTENT_TYPE, JsonUtil.toJson(dispatchJobReq))

val request = buildPost(path, requestBody, mutableMapOf("X-DEVOPS-UID" to getUserId()))
val responseContent = request(request, "kubernetes job失败")
logger.debug("create kubernetes job response: $responseContent")

return JsonUtil.fromJson(responseContent, object : TypeReference<Result<DispatchTaskResp?>>() {})
}

fun getJobStatus(jobName: String): Result<DispatchBuildStatusResp> {
val path = "/dispatch-kubernetes/api/build/job/" + jobName + "/status"
val request = buildGet(path, mutableMapOf("X-DEVOPS-UID" to getUserId()))
val responseContent = request(request, "获取job状态失败")
logger.debug("get kubernetes job status response: $responseContent")
return JsonUtil.fromJson(responseContent, object : TypeReference<Result<DispatchBuildStatusResp>>() {})
}

fun getJobLogs(jobName: String, sinceTime: Int): Result<DispatchJobLogResp> {
val path = "/dispatch-kubernetes/api/build/job/" + jobName + "/logs?sinceTime=" + sinceTime
val request = buildGet(path, mutableMapOf("X-DEVOPS-UID" to getUserId()))
val responseContent = request(request, "获取job日志失败")
logger.debug("get kubernetes job logs response: $responseContent")
return JsonUtil.fromJson(responseContent, object : TypeReference<Result<DispatchJobLogResp>>() {})
}

fun getTask(taskId: String): Result<DispatchBuildStatusResp> {
val path = "/dispatch-kubernetes/api/build/task/status?taskId=" + taskId
val request = buildGet(path, mutableMapOf("X-DEVOPS-UID" to getUserId()))
val responseContent = request(request, "获取task信息失败")
logger.debug("get kubernetes task response: $responseContent")
return JsonUtil.fromJson(responseContent, object : TypeReference<Result<DispatchBuildStatusResp>>() {})
}

fun dockerBuildAndPush(dispatchBuildImageReq: DispatchBuildImageReq): Result<DispatchTaskResp?> {
val path = "/dispatch-kubernetes/api/build/image/buildPush"
dispatchBuildImageReq.copy(podName = EnvUtils.getHostName())
val requestBody = RequestBody.create(JSON_CONTENT_TYPE, JsonUtil.toJson(dispatchBuildImageReq))

val request = buildPost(path, requestBody, mutableMapOf("X-DEVOPS-UID" to getUserId()))
val responseContent = request(request, "kubernetes docker build失败")
logger.debug("docker build response: $responseContent")

return JsonUtil.fromJson(responseContent, object : TypeReference<Result<DispatchTaskResp?>>() {})
}

private fun getUserId(): String {
val inputJson: String?
try {
inputJson = FileUtils.readFileToString(
File(SdkUtils.getDataDir() + "/" + SdkUtils.getInputFile()),
Charset.defaultCharset()
)
} catch (e: IOException) {
logger.error("parse inputJson throw Exception", e)
return ""
}

val inputMap: Map<String, Any> = JsonUtil.fromJson(inputJson,
object : TypeReference<MutableMap<String, Any>>() {})
return inputMap["pipeline.start.user.name"] as String
}

companion object {
private val logger = LoggerFactory.getLogger(KubernetesBuildApi::class.java)
}
}
12 changes: 8 additions & 4 deletions src/main/kotlin/com/tencent/bk/devops/plugin/docker/DockerApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ open class DockerApi : BaseApi() {
): Result<DockerRunResponse> {
try {
val property = System.getenv("devops_slave_model")
val jobPoolType = System.getenv("JOB_POOL")

var response = dockerRunCustomize(projectId, pipelineId, buildId, param)

if (response == null) {
response = when (property) {
"docker" -> CommonExecutor.execute(projectId, pipelineId, buildId, param, taskId)
response = when {
"docker" == property -> CommonExecutor.execute(projectId, pipelineId, buildId, param, taskId)
"KUBERNETES" == jobPoolType -> KubernetesExecutor.execute(param)
else -> ThirdPartExecutor.execute(param)
}
}
Expand All @@ -61,12 +63,14 @@ open class DockerApi : BaseApi() {
): Result<DockerRunLogResponse> {
try {
val property = System.getenv("devops_slave_model")
val jobPoolType = System.getenv("JOB_POOL")

var response = dockerRunGetLogCustomize(projectId, pipelineId, buildId, param)

if (response == null) {
response = when (property) {
"docker" -> CommonExecutor.getLogs(projectId, pipelineId, buildId, param)
response = when {
"docker" == property -> CommonExecutor.getLogs(projectId, pipelineId, buildId, param)
"KUBERNETES" == jobPoolType -> KubernetesExecutor.getLogs(param)
else -> ThirdPartExecutor.getLogs(param)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
package com.tencent.bk.devops.plugin.docker

import com.tencent.bk.devops.plugin.api.impl.KubernetesBuildApi
import com.tencent.bk.devops.plugin.docker.pojo.DockerRunLogRequest
import com.tencent.bk.devops.plugin.docker.pojo.DockerRunLogResponse
import com.tencent.bk.devops.plugin.docker.pojo.DockerRunRequest
import com.tencent.bk.devops.plugin.docker.pojo.DockerRunResponse
import com.tencent.bk.devops.plugin.pojo.kubernetes.DispatchBuildStatusResp
import com.tencent.bk.devops.plugin.pojo.kubernetes.DispatchJobReq
import com.tencent.bk.devops.plugin.pojo.kubernetes.DockerRegistry
import com.tencent.bk.devops.plugin.pojo.kubernetes.JobParam
import com.tencent.bk.devops.plugin.docker.pojo.common.DockerStatus
import com.tencent.bk.devops.plugin.docker.utils.EnvUtils
import org.apache.commons.lang3.RandomStringUtils
import org.apache.tools.ant.types.Commandline
import org.slf4j.LoggerFactory

object KubernetesExecutor {
private const val VOLUME_SERVER = "volume_server"
private const val VOLUME_PATH = "volume_path"
private const val VOLUME_MOUNT_PATH = "volume_mount_path"

private val logger = LoggerFactory.getLogger(KubernetesExecutor::class.java)

fun execute(request: DockerRunRequest): DockerRunResponse {
val startTimeStamp = System.currentTimeMillis() / 1000
val jobRequest = getJobRequest(request)
val task = KubernetesBuildApi().createJob(jobRequest).data

val extraOptionMap = mapOf(
"kubernetesTaskId" to task?.taskId.toString(),
"bcsJobName" to jobRequest.alias,
"startTimeStamp" to startTimeStamp.toString()
)

return DockerRunResponse(
extraOptions = request.extraOptions?.plus(extraOptionMap) ?: extraOptionMap
)
}

fun getLogs(param: DockerRunLogRequest): DockerRunLogResponse {
val extraOptions = param.extraOptions.toMutableMap()

// get task status
val taskId = param.extraOptions["kubernetesTaskId"] ?: throw RuntimeException("kubernetesTaskId is null")
val taskStatusFlag = param.extraOptions["taskStatusFlag"]
if (taskStatusFlag.isNullOrBlank() || taskStatusFlag == DockerStatus.running) {
val taskStatus = KubernetesBuildApi().getTask(taskId).data
taskStatus.let {
if (taskStatus!!.status == "failed") {
return DockerRunLogResponse(
status = DockerStatus.failure,
message = "get task status fail",
extraOptions = extraOptions
)
}
if (taskStatus.status != "succeeded") {
return DockerRunLogResponse(
status = DockerStatus.running,
message = "get task status...",
extraOptions = extraOptions
)
}
}
}
extraOptions["taskStatusFlag"] = DockerStatus.success

// get job status
val jobStatusFlag = param.extraOptions["jobStatusFlag"]
val jobName = param.extraOptions["bcsJobName"] ?: throw RuntimeException("bcsJobName is null")
var jobStatusResp: DispatchBuildStatusResp? = null
if (jobStatusFlag.isNullOrBlank() || jobStatusFlag == DockerStatus.running) {
jobStatusResp = KubernetesBuildApi().getJobStatus(jobName).data!!
val jobStatus = jobStatusResp.status
if ("failed" != jobStatus && "succeeded" != jobStatus && "running" != jobStatus) {
return DockerRunLogResponse(
status = DockerStatus.running,
message = "get job status...",
extraOptions = extraOptions
)
}
}
extraOptions["jobStatusFlag"] = DockerStatus.success

// actual get log logic
val startTimeStamp = extraOptions["startTimeStamp"]?.toInt() ?: (System.currentTimeMillis() / 1000).toInt()
val logs = mutableListOf<String>()

val logResult = KubernetesBuildApi().getJobLogs(jobName, startTimeStamp).data!!

if ((logResult.log != null && logResult.log.isNotEmpty()) || !logResult.errorMsg.isNullOrBlank()) {
extraOptions["startTimeStamp"] = (startTimeStamp + param.timeGap).toString()
logResult.log.let {
logs.addAll(logResult.log ?: emptyList())
}

logResult.errorMsg?.let {
logs.add(logResult.errorMsg)
}
}

if (jobStatusResp == null) {
jobStatusResp = KubernetesBuildApi().getJobStatus(jobName).data
}
val finalStatus = jobStatusResp

if (finalStatus!!.status in listOf("failed", "succeeded")) {
logger.info("final job status data: $jobStatusResp")
Thread.sleep(6000)
val finalLogs = KubernetesBuildApi().getJobLogs(jobName, startTimeStamp + 6).data!!
if (finalStatus.status == "failed") {
return DockerRunLogResponse(
log = logs.plus(finalLogs.errorMsg ?: ""),
status = DockerStatus.failure,
message = "docker run fail...",
extraOptions = extraOptions
)
}
return DockerRunLogResponse(
log = logs.plus(finalLogs?.log ?: emptyList()),
status = DockerStatus.success,
message = "docker run success...",
extraOptions = extraOptions
)
}

return DockerRunLogResponse(
log = logs,
status = DockerStatus.running,
message = "get log...",
extraOptions = extraOptions
)
}

private fun getJobRequest(param: DockerRunRequest): DispatchJobReq {
with(param) {
// get job param
val cmdTmp = mutableListOf<String>()
command.forEach {
cmdTmp.add(it.removePrefix("\"").removeSuffix("\"").removePrefix("\'")
.removeSuffix("\'"))
}
val cmd = if (cmdTmp.size == 1) {
Commandline.translateCommandline(cmdTmp.first()).toList()
} else {
cmdTmp
}
val jobParam = JobParam(
env = envMap,
command = cmd,
labels = labels,
ipEnabled = ipEnabled
)

if (jobParam.nfsVolume == null) {
val volumeServer = System.getenv(VOLUME_SERVER)
if (!volumeServer.isNullOrBlank()) {
jobParam.nfsVolume = listOf(
JobParam.NfsVolume(
System.getenv(VOLUME_SERVER),
System.getenv(VOLUME_PATH),
System.getenv(VOLUME_MOUNT_PATH)
)
)
}
}

// get docker image host & path
val imagePair = getImagePair(param.imageName)

// get user pass param
val registry = DockerRegistry(
host = imagePair.first,
username = param.dockerLoginUsername,
password = param.dockerLoginPassword
)

return DispatchJobReq(
alias = "job-${System.currentTimeMillis()}-${RandomStringUtils.randomAlphabetic(8).toLowerCase()}",
activeDeadlineSeconds = 86400,
image = imageName,
registry = registry,
params = jobParam,
podNameSelector = EnvUtils.getHostName()
)
}
}

private fun getImagePair(imageName: String): Pair<String, String> {
val targetImageRepo = imageName.split("/").first()
val targetImageName = imageName.removePrefix(targetImageRepo).removeSuffix("/")
return Pair(targetImageRepo, targetImageName)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* BK-CI 蓝鲸持续集成平台 is licensed under the MIT license.
*
* A copy of the MIT License is included in this file.
*
*
* Terms of the MIT License:
* ---------------------------------------------------
* 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.
*/

package com.tencent.bk.devops.plugin.pojo.kubernetes

data class DispatchBuildImageReq(
val jobName: String,
val imageName: List<String>,
val registry: List<Registry>,
val fromRegistry: List<Registry>,
val buildArgs: Map<String, Any>,
val workPath: String,
val dockerFile: String,
val podName: String
)

data class Registry(
val host: String,
val username: String,
val password: String
)
Loading

0 comments on commit 3353313

Please sign in to comment.