Skip to content

Commit

Permalink
feat:保存流水线时,校验是否有子流水线循环依赖 #10479
Browse files Browse the repository at this point in the history
  • Loading branch information
hejieehe committed Sep 5, 2024
1 parent 30d718a commit 9b7d3ca
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 231 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

package com.tencent.devops.process.pojo.pipeline

import com.tencent.devops.common.pipeline.pojo.element.Element
import com.tencent.devops.common.pipeline.pojo.element.EmptyElement
import io.swagger.v3.oas.annotations.media.Schema

@Schema(title = "子流水线依赖信息")
Expand All @@ -39,10 +41,8 @@ data class SubPipelineRef(
val projectId: String,
@get:Schema(title = "流水线项目渠道", required = true)
val channel: String,
@get:Schema(title = "插件ID", required = true)
val taskId: String,
@get:Schema(title = "插件名称", required = true)
val taskName: String,
@get:Schema(title = "插件", required = true)
val element: Element,
@get:Schema(title = "stage名称", required = true)
val stageName: String,
@get:Schema(title = "容器名称", required = true)
Expand All @@ -58,15 +58,18 @@ data class SubPipelineRef(
@get:Schema(title = "插件启用状态", required = true)
val elementEnable: Boolean = true,
@get:Schema(title = "是否为模板流水线", required = true)
val isTemplate: Boolean = false
val isTemplate: Boolean = false,
@get:Schema(title = "父流水线授权用户", required = true)
val oauthUser: String? = null,
@get:Schema(title = "上下文参数", required = false)
val contextMap: Map<String, String> = emptyMap()
) {
constructor(projectId: String, pipelineId: String, subPipelineId: String, subProjectId: String) : this(
pipelineId = pipelineId,
pipelineName = "",
projectId = projectId,
channel = "",
taskId = "",
taskName = "",
element = EmptyElement(),
stageName = "",
containerName = "",
subPipelineId = subPipelineId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,43 +46,42 @@ class SubPipelineRefDao {
}

with(TPipelineSubRef.T_PIPELINE_SUB_REF) {
dslContext.batch(
subPipelineRefList.map {
dslContext.insertInto(
this,
PROJECT_ID,
PIPELINE_ID,
PIPELINE_NAME,
CHANNEL,
STAGE_NAME,
CONTAINER_NAME,
TASK_ID,
TASK_NAME,
SUB_PROJECT_ID,
SUB_PIPELINE_ID,
SUB_PIPELINE_NAME
).values(
it.projectId,
it.pipelineId,
it.pipelineName,
it.channel,
it.stageName,
it.containerName,
it.taskId,
it.taskName,
it.subProjectId,
it.subPipelineId,
it.subPipelineName
).onDuplicateKeyUpdate()
.set(STAGE_NAME, it.stageName)
.set(CONTAINER_NAME, it.containerName)
.set(TASK_NAME, it.taskName)
.set(PIPELINE_NAME, it.pipelineName)
.set(SUB_PROJECT_ID, it.subProjectId)
.set(SUB_PIPELINE_ID, it.subPipelineId)
.set(SUB_PIPELINE_NAME, it.subPipelineName)
}
).execute()
subPipelineRefList.forEach {
dslContext.insertInto(
this,
PROJECT_ID,
PIPELINE_ID,
PIPELINE_NAME,
CHANNEL,
STAGE_NAME,
CONTAINER_NAME,
TASK_ID,
TASK_NAME,
SUB_PROJECT_ID,
SUB_PIPELINE_ID,
SUB_PIPELINE_NAME
).values(
it.projectId,
it.pipelineId,
it.pipelineName,
it.channel,
it.stageName,
it.containerName,
it.element.id,
it.element.name,
it.subProjectId,
it.subPipelineId,
it.subPipelineName
).onDuplicateKeyUpdate()
.set(STAGE_NAME, it.stageName)
.set(CONTAINER_NAME, it.containerName)
.set(TASK_NAME, it.element.name)
.set(PIPELINE_NAME, it.pipelineName)
.set(SUB_PROJECT_ID, it.subProjectId)
.set(SUB_PIPELINE_ID, it.subPipelineId)
.set(SUB_PIPELINE_NAME, it.subPipelineName)
.execute()
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@

package com.tencent.devops.process.service

import com.tencent.devops.common.auth.api.AuthPermission
import com.tencent.devops.common.pipeline.container.Container
import com.tencent.devops.common.pipeline.container.Stage
import com.tencent.devops.common.pipeline.enums.ChannelCode
import com.tencent.devops.common.pipeline.pojo.element.Element
import com.tencent.devops.common.pipeline.pojo.element.atom.BeforeDeleteParam
import com.tencent.devops.common.pipeline.pojo.element.atom.ElementCheckResult
import com.tencent.devops.process.engine.atom.plugin.IElementBizPluginService
import com.tencent.devops.process.pojo.pipeline.SubPipelineRef
import com.tencent.devops.process.service.pipeline.SubPipelineRefService
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
Expand All @@ -44,7 +45,8 @@ import org.springframework.stereotype.Service
*/
@Service
class SubPipelineElementBizPluginService @Autowired constructor(
private val subPipelineRepositoryService: SubPipelineRepositoryService
private val subPipelineRepositoryService: SubPipelineRepositoryService,
private val subPipelineRefService: SubPipelineRefService
) : IElementBizPluginService {

companion object {
Expand Down Expand Up @@ -89,21 +91,86 @@ class SubPipelineElementBizPluginService @Autowired constructor(
oauthUser: String?,
pipelineId: String
): ElementCheckResult {
logger.info(
"check the sub-pipeline permissions when deploying pipeline|projectId:$projectId|" +
"element:${element.id}|contextMap:$contextMap|appearedCnt:$appearedCnt|isTemplate:$isTemplate|" +
"oauthUser:$oauthUser|userId:$userId"
)
// 模板保存时不需要校验子流水线权限
if (isTemplate || projectId.isNullOrBlank()) return ElementCheckResult(true)
return subPipelineRepositoryService.checkElementPermission(
if (projectId.isNullOrBlank()) return ElementCheckResult(true)

val (subProjectId, subPipelineId, subPipelineName) = subPipelineRepositoryService.getSubPipelineInfo(
element = element,
projectId = projectId,
stageName = stage.name ?: "",
containerName = container.name,
contextMap = contextMap
) ?: return ElementCheckResult(true)
val subPipelineRef = SubPipelineRef(
projectId = projectId,
pipelineId = pipelineId,
pipelineName = "",
subProjectId = subProjectId,
subPipelineId = subPipelineId,
subPipelineName = subPipelineName,
element = element,
contextMap = contextMap,
permission = AuthPermission.EXECUTE,
userId = oauthUser ?: userId
) ?: ElementCheckResult(true)
containerName = container.name,
stageName = stage.name ?: "",
channel = ChannelCode.BS.name,
userId = userId,
elementEnable = enableElement(stage, container, element),
isTemplate = isTemplate,
oauthUser = oauthUser
)
logger.info("start check sub pipeline element|$subPipelineRef")
return subPipelineRef.check(
listOf(
this::checkPermission,
this::checkCircularDependency
)
)
}

fun SubPipelineRef.check(
list: List<(SubPipelineRef) -> ElementCheckResult>
): ElementCheckResult {
list.forEach {
val invoke = it.invoke(this)
if (!invoke.result) {
return invoke
}
}
return ElementCheckResult(true)
}

fun checkPermission(subPipelineRef: SubPipelineRef): ElementCheckResult {
with(subPipelineRef) {
logger.info(
"check the sub-pipeline permissions when deploying pipeline|projectId:$projectId|" +
"element:${element.id}|contextMap:$contextMap|isTemplate:$isTemplate|" +
"oauthUser:$oauthUser|userId:$userId"
)
// 模板保存时不需要校验子流水线权限
if (isTemplate) return ElementCheckResult(true)
return subPipelineRepositoryService.checkElementPermission(
projectId = projectId,
stageName = stageName,
containerName = containerName,
element = element,
contextMap = contextMap,
permission = com.tencent.devops.common.auth.api.AuthPermission.EXECUTE,
userId = oauthUser ?: userId
) ?: ElementCheckResult(true)
}
}

fun checkCircularDependency(subPipelineRef: SubPipelineRef): ElementCheckResult {
with(subPipelineRef) {
if (!elementEnable) return ElementCheckResult(true)
val startTime = System.currentTimeMillis()
val rootPipelineKey = "${projectId}_$pipelineId"
val checkResult = subPipelineRefService.checkCircularDependency(
subPipelineRef = this,
rootPipelineKey = rootPipelineKey,
existsPipeline = HashMap(mapOf(rootPipelineKey to this))
)
logger.info("finish check circular dependency|${System.currentTimeMillis() - startTime} ms")
return checkResult
}
}

private fun enableElement(stage: Stage, container: Container, element: Element) =
stage.stageEnabled() && container.containerEnabled() && element.elementEnabled()
}
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ class SubPipelineRepositoryService @Autowired constructor(
/**
* 获取最新版流水线编排
*/
private fun getModel(projectId: String, pipelineId: String): Model? {
fun getModel(projectId: String, pipelineId: String): Model? {
var model: Model? = null
val modelString = pipelineResDao.getLatestVersionModelString(dslContext, projectId, pipelineId)
if (modelString.isNullOrBlank()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,7 @@ class SubPipelineStartUpService @Autowired constructor(
private val buildParamCompatibilityTransformer: BuildParametersCompatibilityTransformer,
private val pipelinePermissionService: PipelinePermissionService,
private val pipelineUrlBean: PipelineUrlBean,
private val templateFacadeService: TemplateFacadeService
private val pipelineUrlBean: PipelineUrlBean,
private val templateFacadeService: TemplateFacadeService,
private val subPipelineRefService: SubPipelineRefService
) {

Expand Down
Loading

0 comments on commit 9b7d3ca

Please sign in to comment.