Skip to content

Commit

Permalink
feat: 构建并推送镜像支持插件接入 TencentBlueKing#5032
Browse files Browse the repository at this point in the history
Signed-off-by: sawyersong <sawyersong@tencent.com>
  • Loading branch information
sawyersong2 committed Sep 1, 2021
1 parent c1e0812 commit 4ad66a3
Show file tree
Hide file tree
Showing 8 changed files with 340 additions and 42 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
package com.tencent.devops.dockerhost

import com.github.dockerjava.api.DockerClient
import com.tencent.devops.common.api.util.script.ShellUtil
import com.tencent.devops.dockerhost.services.DockerHostImageScanService
import org.apache.commons.io.FileUtils
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component
import java.io.File
import java.util.concurrent.Executors

@Component
class SampleDockerHostImageScanService : DockerHostImageScanService {
Expand All @@ -19,29 +14,6 @@ class SampleDockerHostImageScanService : DockerHostImageScanService {
imageTagSet: MutableSet<String>,
dockerClient: DockerClient
) {
Executors.newFixedThreadPool(10).execute {
try {
imageTagSet.stream().forEach {
val imageAndTagArray = it.split(":")
val inputStream = dockerClient.saveImageCmd(imageAndTagArray[0])
.withTag(imageAndTagArray[1])
.exec()
val imageSavedPath = "/data/dockersavedimages/${imageAndTagArray[0]}.tar"
val targetSavedImagesFile = File(imageSavedPath)
FileUtils.copyInputStreamToFile(inputStream, targetSavedImagesFile)

val script = "dockerscan -t $imageSavedPath -p $pipelineId -u $it -i dev " +
"-T $projectId -b $buildId -n sawyersong"
val scanResult = ShellUtil.executeEnhance(script)
logger.info("[$buildId]|[$vmSeqId] scan docker $it result: $scanResult")
}
} catch (e: Exception) {
logger.error("[$buildId]|[$vmSeqId] scan docker error.", e)
}
}
}

companion object {
private val logger = LoggerFactory.getLogger(SampleDockerHostImageScanService::class.java)
// do something before push images
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,37 +28,92 @@
package com.tencent.devops.dockerhost.services

import com.github.dockerjava.api.DockerClient
import com.github.dockerjava.api.async.ResultCallback
import com.github.dockerjava.api.command.BuildImageResultCallback
import com.github.dockerjava.api.model.AuthConfig
import com.github.dockerjava.api.model.AuthConfigurations
import com.github.dockerjava.api.model.BuildResponseItem
import com.github.dockerjava.api.model.PushResponseItem
import com.github.dockerjava.core.DefaultDockerClientConfig
import com.github.dockerjava.core.DockerClientBuilder
import com.github.dockerjava.okhttp.OkDockerHttpClient
import com.github.dockerjava.transport.DockerHttpClient
import com.tencent.devops.dockerhost.config.DockerHostConfig
import com.tencent.devops.dockerhost.dispatch.DockerHostBuildResourceApi
import com.tencent.devops.dockerhost.pojo.DockerBuildParam
import org.apache.commons.lang3.StringUtils
import com.tencent.devops.dockerhost.services.image.ImageBuildHandler
import com.tencent.devops.dockerhost.services.image.ImageHandlerContext
import com.tencent.devops.dockerhost.services.image.ImagePushHandler
import com.tencent.devops.dockerhost.services.image.ImageScanHandler
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component
import java.io.File
import java.io.IOException
import java.nio.file.Paths

@Component
class DockerHostImageService(
private val dockerHostConfig: DockerHostConfig,
private val dockerHostBuildApi: DockerHostBuildResourceApi,
private val dockerHostImageScanService: DockerHostImageScanService
/* private val dockerHostBuildApi: DockerHostBuildResourceApi,
private val dockerHostImageScanService: DockerHostImageScanService,*/
private val imageBuildHandler: ImageBuildHandler,
private val imageScanHandler: ImageScanHandler,
private val imagePushHandler: ImagePushHandler
) {

companion object {
private val logger = LoggerFactory.getLogger(DockerHostImageService::class.java)
}

fun dockerBuildAndPushImage(
projectId: String,
pipelineId: String,
vmSeqId: String,
dockerBuildParam: DockerBuildParam,
buildId: String,
elementId: String?,
outer: Boolean
): Pair<Boolean, String?> {
lateinit var dockerClient: DockerClient
try {
val repoAddr = dockerBuildParam.repoAddr
val userName = dockerBuildParam.userName
val password = dockerBuildParam.password
val config = DefaultDockerClientConfig.createDefaultConfigBuilder()
.withDockerConfig(dockerHostConfig.dockerConfig)
.withApiVersion(dockerHostConfig.apiVersion)
.withRegistryUrl(repoAddr)
.withRegistryUsername(userName)
.withRegistryPassword(password)
.build()

val longHttpClient: DockerHttpClient = OkDockerHttpClient.Builder()
.dockerHost(config.dockerHost)
.sslConfig(config.sslConfig)
.connectTimeout(5000)
.readTimeout(300000)
.build()

dockerClient = DockerClientBuilder.getInstance(config).withDockerHttpClient(longHttpClient).build()

imageBuildHandler.setNextHandler(imageScanHandler.setNextHandler(imagePushHandler))
.handlerRequest(
ImageHandlerContext(
projectId = projectId,
pipelineId = pipelineId,
buildId = buildId,
vmSeqId = vmSeqId,
dockerBuildParam = dockerBuildParam,
dockerClient = dockerClient,
pipelineTaskId = elementId,
outer = outer
))

return Pair(true, null)
} catch (e: Exception) {
logger.error("Docker build and push failed, exception: ", e)
return Pair(false, e.message)
} finally {
try {
dockerClient.close()
} catch (e: IOException) {
logger.error("docker client close exception: ${e.message}")
}
}

}
/*
fun dockerBuildAndPushImage(
projectId: String,
pipelineId: String,
Expand Down Expand Up @@ -274,5 +329,5 @@ class DockerHostImageService(
}
super.onNext(item)
}
}
}*/
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.tencent.devops.dockerhost.services.image

abstract class Handler<T: HandlerContext> {
protected var nextHandler: Handler<T>? = null

fun setNextHandler(handler: Handler<T>): Handler<T> {
this.nextHandler = handler
return this
}
abstract fun handlerRequest(handlerContext: T)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.tencent.devops.dockerhost.services.image

open class HandlerContext(
open val projectId: String,
open val pipelineId: String,
open val buildId: String,
open val vmSeqId: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package com.tencent.devops.dockerhost.services.image

import com.github.dockerjava.api.command.BuildImageResultCallback
import com.github.dockerjava.api.model.AuthConfig
import com.github.dockerjava.api.model.AuthConfigurations
import com.github.dockerjava.api.model.BuildResponseItem
import com.tencent.devops.dockerhost.config.DockerHostConfig
import com.tencent.devops.dockerhost.dispatch.DockerHostBuildResourceApi
import org.apache.commons.lang3.StringUtils
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import java.io.File
import java.nio.file.Paths

@Service
class ImageBuildHandler(
private val dockerHostConfig: DockerHostConfig,
private val dockerHostBuildApi: DockerHostBuildResourceApi
) : Handler<ImageHandlerContext>() {
override fun handlerRequest(handlerContext: ImageHandlerContext) {
with(handlerContext) {
val authConfigurations = AuthConfigurations()
val ticket = dockerBuildParam.ticket
val args = dockerBuildParam.args
ticket.forEach {
val baseConfig = AuthConfig()
.withUsername(it.second)
.withPassword(it.third)
.withRegistryAddress(it.first)
authConfigurations.addConfig(baseConfig)
}

val workspace = getWorkspace(pipelineId, vmSeqId.toInt(), dockerBuildParam.poolNo ?: "0")
val buildDir = Paths.get(workspace + dockerBuildParam.buildDir).normalize().toString()
val dockerfilePath = Paths.get(workspace + dockerBuildParam.dockerFile).normalize().toString()
val baseDirectory = File(buildDir)
val dockerfile = File(dockerfilePath)

val imageNameTagSet = mutableSetOf<String>()
if (dockerBuildParam.imageTagList.isNotEmpty()) {
dockerBuildParam.imageTagList.forEach {
imageNameTagSet.add(getImageNameWithTag(
repoAddr = dockerBuildParam.repoAddr,
projectId = projectId,
imageName = dockerBuildParam.imageName,
imageTag = it,
outer = outer
))
}
} else {
imageNameTagSet.add(getImageNameWithTag(
repoAddr = dockerBuildParam.repoAddr,
projectId = projectId,
imageName = dockerBuildParam.imageName,
imageTag = dockerBuildParam.imageTag,
outer = outer
))
}

this.imageTagSet = imageNameTagSet

logger.info("[$buildId]|[$vmSeqId] Build docker image, workspace: $workspace, buildDir:$buildDir, dockerfile: $dockerfilePath")
logger.info("[$buildId]|[$vmSeqId] Build docker image, imageNameTag: $imageNameTagSet")
val step = dockerClient.buildImageCmd().withNoCache(true)
.withPull(true)
.withBuildAuthConfigs(authConfigurations)
.withBaseDirectory(baseDirectory)
.withDockerfile(dockerfile)
.withTags(imageNameTagSet)
args.map { it.trim().split("=") }.forEach {
step.withBuildArg(it.first(), it.last())
}
step.exec(MyBuildImageResultCallback(buildId, pipelineTaskId, dockerHostBuildApi))
.awaitImageId()

nextHandler?.handlerRequest(this)
}
}

private fun getWorkspace(pipelineId: String, vmSeqId: Int, poolNo: String): String {
return "${dockerHostConfig.hostPathWorkspace}/$pipelineId/${getTailPath(vmSeqId, poolNo.toInt())}/"
}

private fun getTailPath(vmSeqId: Int, poolNo: Int): String {
return if (poolNo > 1) {
"$vmSeqId" + "_$poolNo"
} else {
vmSeqId.toString()
}
}

private fun getImageNameWithTag(
repoAddr: String,
projectId: String,
imageName: String,
imageTag: String,
outer: Boolean = false
): String {
return if (outer) {
"$repoAddr/$imageName:$imageTag"
} else {
"$repoAddr/paas/$projectId/$imageName:$imageTag"
}
}

inner class MyBuildImageResultCallback internal constructor(
private val buildId: String,
private val elementId: String?,
private val dockerHostBuildApi: DockerHostBuildResourceApi
) : BuildImageResultCallback() {
override fun onNext(item: BuildResponseItem?) {
val text = item?.stream
if (null != text) {
dockerHostBuildApi.postLog(
buildId,
false,
StringUtils.removeEnd(text, "\n"),
elementId
)
}

super.onNext(item)
}
}

companion object {
private val logger = LoggerFactory.getLogger(ImageHandlerContext::class.java)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.tencent.devops.dockerhost.services.image

import com.github.dockerjava.api.DockerClient
import com.tencent.devops.dockerhost.pojo.DockerBuildParam

data class ImageHandlerContext(
val outer: Boolean, // 是否为外部请求创建镜像
val dockerClient: DockerClient,
val pipelineTaskId: String?,
val dockerBuildParam: DockerBuildParam,
var imageTagSet: MutableSet<String> = mutableSetOf(),
override val projectId: String,
override val pipelineId: String,
override val buildId: String,
override val vmSeqId: String
) : HandlerContext(
projectId = projectId,
pipelineId = pipelineId,
buildId = buildId,
vmSeqId = vmSeqId
)
Loading

0 comments on commit 4ad66a3

Please sign in to comment.